如何向ASP.NET Core Web API响应中添加自定义标头

128

我正在将我的API从Web API 2移植到ASP.NET Core Web API。 我过去可以通过以下方式添加自定义头部:

  HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
  response.Headers.Add("X-Total-Count", count.ToString());
  return ResponseMessage(response);

如何在ASP.NET Core Web API中添加自定义标头?


参考以下三种方法在ASP.NET Core中添加自定义标头:https://codepedia.info/add-custom-header-aspnet-core-response - Satinder singh
8个回答

161

在调用 return 前,您可以从传入的 Http Request 中劫持 HttpContext ,并向 Response 对象添加自定义标头。

如果您想让自定义标头保留并添加到多个控制器的所有 API 请求中,那么您应该考虑创建一个 Middleware 组件来为您完成此操作,然后将其添加到 Startup.cs 中的 Http 请求管道中。

public IActionResult SendResponse()
{
    Response.Headers.Add("X-Total-Count", "20");

    return Ok();
}    

1
为了在Fetch API中获得附加值,您需要在以下代码后编写: console.log(response.headers.get('X-Total-Count')); return response.text(); })``` - Naresh Bisht

51

这是一个简单的GET操作示例,它从某个列表中返回前X条记录,并在响应头X-Total-Count中返回计数:

using System;
using System.Linq;
using System.Net;
using Microsoft.AspNetCore.Mvc;

namespace WebApplication.Controllers
{
    [Route("api")]
    public class ValuesController : Controller
    {
        [HttpGet]
        [Route("values/{top}")]
        public IActionResult Get(int top)
        {
            // Generate dummy values
            var list = Enumerable.Range(0, DateTime.Now.Second)
                                 .Select(i => $"Value {i}")
                                 .ToList();
            list.Reverse();

            var result = new ObjectResult(list.Take(top))
            {
                StatusCode = (int)HttpStatusCode.OK
            };

            Response.Headers.Add("X-Total-Count", list.Count.ToString());

            return result;
        }
    }
}

该URL看起来像是 http://localhost:3377/api/values/5,结果(基于生成的19个虚拟记录)如下,因此X-Total-Count的值将为19:

["Value 18","Value 17","Value 16","Value 15","Value 14"]

3
感觉很不专业,因为我们基本上在两个地方设置了结果和属性。这可以通过自定义操作结果来封装。 当我输入时,我正准备创建一个ContentResultWithHeaders。但话虽如此,感觉需要付出很多努力。 - brumScouse
6
基类 Microsoft.AspNetCore.Mvc.Controller 已经包含属性 Response。因此,不需要调用 Request.HttpContext.Response.Headers,可以直接调用 Response.Headers - RAM

49

对于想要在所有请求中添加自定义标头的人来说,中间件是最好的方式。在startup.cs中进行一些更改,就像这样:

app.Use(async (context, next) =>
{
   context.Response.Headers.Add("X-Developed-By", "Your Name");
   await next.Invoke();
});

祝你好运。


5
在中间件中直接修改响应是不鼓励的,详见 这里。相反,您可以使用 context.Response.OnStarting 回调函数。 - Mojtaba
这在我的中间件中捕获异常时没有起作用。 DeveloperExceptionPageMiddleware 会删除我的头文件。下面 @Mojtaba 的解决方案有效。 - Bouke Versteegh
@BoukeVersteegh 这在你测试的特定情况下肯定行不通。问题不在解决方案。 - HO3EiN
@Mojtaba,直接在中间件中修改响应没有任何问题和不鼓励的地方。实际上,文档中提供了这样做的示例。但是在调用next之前应该完成修改。警告只是关于这一点:在调用next之后不应该修改响应。 - undefined
@Frédéric 你说得对。我已经调整了我的回答,以反映你的说明。 - undefined

22

自定义属性可以是一个不错的方式。

https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2

public class AddHeaderAttribute : ResultFilterAttribute
{
    private readonly string _name;
    private readonly string _value;

    public AddHeaderAttribute(string name, string value)
    {
        _name = name;
        _value = value;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_name, new string[] { _value });
        base.OnResultExecuting(context);
    }
}

然后在您的 API 方法上像这样使用它

[AddHeader("X-MyHeader", "123")]
如果您有一个常用的标题,您可以扩展这个类:
public class MySpecialHeaderAttribute : AddHeaderAttribute
{
    public MySpecialHeaderAttribute() : base("X-MyHeader", "true")
    {
    }
}

1
这是一个非常棒的方法!非常感谢。该属性也可以应用于控制器类。在我的情况下,我创建了一个从ControllerBase继承并根据需要附加属性的SafeControllerBase。现在我将所有控制器从SafeControllerBase继承,因此所需的响应包含在所有响应中。 - Matt

14
我同意 @Ho3Ein 的观点,如果你想要在所有请求中添加自定义头部,中间件是最好的方式。但是在中间件中直接修改 Response(在调用 next.Invoke() 之后)是不被鼓励的。根据 Microsoft 的文档,一旦响应已经开始,对 HttpResponse 的修改会抛出异常。例如,设置头部和状态码的修改都会抛出异常。
app.Use(async (context, next) =>
        {
            // Do work that doesn't write to the Response.
            await next.Invoke();
            // Do logging or other work that doesn't write to the Response.
        });

所以在中间件中添加自定义头的更好方法是使用 Response.OnStarting 回调,如下所示:
app.Use(async (context, next) =>
                      {

                          context.Response.OnStarting(() =>
                              {
                                  context.Response.Headers.Add("X-Developed-By", "Your Name");
                                  return Task.FromResult(0);
                              });

                          await next();
                      }
                      );

看起来不错,但是我该如何传递请求返回的记录数? - Tropin Alexey
这是对微软文档的错误解释。文档只是警告在执行继续next之后不要触摸响应。在此之前更改是可以的。 - undefined
@Frédéric,嗯,这里有两件事。首先,在这个问题的背景下,也就是添加自定义头部的情况下,文档明确给出了一个警告。其次,举个例子,在调用next.Invoke()之前,你想做什么样的改变呢? - undefined
警告只针对在调用next之后更改响应。在调用next之前,请添加您的标头。就像在这个其他的Microsoft文档中所示。 - undefined
@Frédéric 你说得对。我调整了我的回答。 - undefined

9

其他中间件可能会在您设置完标头后清除它们。为确保添加了您的标头,请在响应发送之前添加它们。

app.Use(async (context, next) => {
    context.Response.OnStarting(() => {
        context.Response.Headers.Add("X-Developed-By", "Your Name");
        return Task.FromResult(0);
    });

    await next();
});


在真正的中间件中,或许会这样:
public class AddHeadersMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        context.Response.OnStarting(() => {
            context.Response.Headers.Add("X-Developed-By", "Your Name");
            return Task.FromResult(0);
        });

        await next();
    }
}

2
所选答案没问题,但如果你想在头部添加类似AES编码的值,会出现错误:
标题中有无效的非ASCII或控制字符
一种通过的方法是将此值再次进行URL编码。操作步骤如下:
string urlEncodedValue = WebUtility.UrlEncode(value);

反之亦然,解码它:
string value = WebUtility.UrlDecode(urlEncodedValue);

1

如果你有一个ApiController而不是Controller,那么你可以这样做:

public class InfoController : ApiController
{
    // Without custom header
    public IHttpActionResult MyMethod(..)
    {
        var myObject= GetMyResult();
        return Ok(myObject);
    }

    // With custom header
    public IHttpActionResult MyMethod(..)
    {
        var myObject = GetMyResult();

        // inspired from https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/content-negotiation#how-content-negotiation-works
        var negotiator = Configuration.Services.GetContentNegotiator();
        var result = negotiator.Negotiate(typeof(TypeOfMyObject), Request, Configuration.Formatters);
        var msg = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new ObjectContent<TypeOfMyObject>(myObject, result.Formatter,result.MediaType.MediaType)
        };

        msg.Headers.Add("MyCustomHeader", "MyCustomHeaderValue");
        return ResponseMessage(msg);
    }
}

1
ApiController是Microsoft.AspNetCore.Mvc.WebApiCompatShim.dll的一部分,这是一个临时解决方案,旨在简化从asp.net 4.x迁移到asp.net core的过程。对于新代码,我不会使用ApiController。 - IulianT

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接