在 ApiController 中添加自定义响应头

Posted

技术标签:

【中文标题】在 ApiController 中添加自定义响应头【英文标题】:Add a custom response header in ApiController 【发布时间】:2015-11-08 03:50:06 【问题描述】:

到目前为止,我有一个 GET 方法,如下所示:

protected override async Task<IHttpActionResult> GetAll(QueryData query)

     // ... Some operations

     //LINQ Expression based on the query parameters
     Expression<Func<Entity, bool>> queryExpression = BuildQueryExpression(query);

     //Begin to count all the entities in the repository
     Task<int> countingEntities = repo.CountAsync(queryExpression);

     //Reads an entity that will be the page start
     Entity start = await repo.ReadAsync(query.Start);

     //Reads all the entities starting from the start entity
     IEnumerable<Entity> found = await repo.BrowseAllAsync(start, queryExpression);

     //Truncates to page size
     found = found.Take(query.Size);

     //Number of entities returned in response
     int count = found.Count();

     //Number of total entities (without pagination)
     int total = await countingEntities;

     return Ok(new 
          Total = total,
          Count = count,
          Last = count > 0 ? GetEntityKey(found.Last()) : default(Key),
          Data = found.Select(e => IsResourceOwner(e) ? MapToOwnerDTO(e) : MapToDTO(e)).ToList()
     );

这就像一个魅力,它很好。但是,最近有人告诉我将响应元数据(即TotalCountLast 属性)作为响应自定义标头而不是响应正文发送。

我无法从 ApiController 访问 Response。我想到了一个过滤器或属性,但是如何获取元数据值?

我可以将所有这些信息保留在响应中,然后有一个过滤器在将响应发送到客户端之前反序列化响应,并使用标头创建一个新的,但这似乎既麻烦又糟糕。

有没有办法直接通过这个方法在ApiController 上添加自定义标头?

【问题讨论】:

应该像that一样简单 @Andrei 我没有HttpContext 属性,但我有ActionContext 属性。但是,该对象的Response 属性是null,我无法对其进行操作。 您需要使用 ActionContext.Request.CreateResponse() 来实际创建响应,然后将响应中的值设置为强类型对象而不是字符串 @entre 我希望 Web Api 序列化我的匿名对象(即使用 Web Api Ok&lt;T&gt;(T t) 方法。这还包括为我设置一些标题)。如果我创建一个响应,我必须序列化我的对象,并且我必须手动设置所有标题。 移动方法中的所有头部设置部分并在两个地方都使用该方法 【参考方案1】:

您可以在这样的方法中显式添加自定义标头:

[HttpGet]
[Route("home/students")]
public HttpResponseMessage GetStudents()

       // Get students from Database

       // Create the response
        var response = Request.CreateResponse(HttpStatusCode.OK, students);
    
        // Set headers for paging
        response.Headers.Add("X-Students-Total-Count", students.Count());
       
       return response;

欲了解更多信息,请阅读这篇文章:http://www.jerriepelser.com/blog/paging-in-aspnet-webapi-http-headers/

【讨论】:

我正在这样做,但标题被剥离 @weagle08 您的请求是否通过代理?如果是这样,您可以阅读以下内容:***.com/questions/20820572/… 为我工作,但我们的连接中不涉及代理【参考方案2】:

简单的解决方案就是这样写:

HttpContext.Current.Response.Headers.Add("MaxRecords", "1000");

【讨论】:

感谢 Darek,代码高亮。从现在开始,我会确保这样做:) HttpContext 不会出现在派生 ApiController 的控制器中。 如果我们谈论的是从system.web.http.apicontroller 派生的.net 框架控制器,这确实是正确的答案。我个人只熟悉核心语法,所以这对于这个遗留项目来说非常棒。欢呼 这对我有用。我的控制器继承自 ApiController 我遗留项目中的绝对救星。【参考方案3】:

或者,如果您需要对每个响应执行某项操作,则最好利用 DelegatingHandler。因为它将在请求/响应管道上工作,而不是在控制器/操作级别上工作。在我的情况下,我必须在每个响应中添加一些标题,所以我做了我所描述的。见下面的代码 sn-p

public class Interceptor : DelegatingHandler

    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    
        var response = await base.SendAsync(request, cancellationToken);
        response.Headers.Add("Access-Control-Allow-Origin", "*");
        response.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PATCH,DELETE,PUT,OPTIONS");
        response.Headers.Add("Access-Control-Allow-Headers", "Origin, Content-Type, X-Auth-Token, content-type");
        return response;
    


您需要在 WebApiConfig 中添加此处理程序

    public static class WebApiConfig
    
        public static void Register(HttpConfiguration config)
        
            config.MessageHandlers.Add(new Interceptor());
        
     

【讨论】:

【参考方案4】:

你需要的是:

public async Task<IHttpActionResult> Get() 
 
    var response = Request.CreateResponse();
    response.Headers.Add("Lorem", "ipsum");

    return base.ResponseMessage(response); 

我希望这能回答您的问题。

【讨论】:

【参考方案5】:

我已经输入了 cmets,这是我的完整答案。

您需要创建一个自定义过滤器并将其应用于您的控制器。

public class CustomHeaderFilter : ActionFilterAttribute

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    
       var count = actionExecutedContext.Request.Properties["Count"];
       actionExecutedContext.Response.Content.Headers.Add("totalHeader", count);
    

在你的控制器中

  public class AddressController : ApiController
        
            public async Task<Address> Get()
            
               Request.Properties["Count"] = "123";
            
    

【讨论】:

这很完美,但它是正确的方法吗?我的元数据应该是响应的属性,而不是请求的属性。我的意思是,它可以作为一种解决方案,但它在概念上是否正确? 这对我来说看起来像是双重工作。您可以添加标题directly @Nikola 但是你会丢失强类型的响应,OP 没有使用它,但仍然是这种方法的一个选项。我正在开发一个 web api 项目并且不使用强类型会导致问题 - 因为我们不能轻易地生成正确的招摇。如果可以,请避免返回无类型的响应 就我而言,我发现这是在标头中返回响应数据的最佳解决方案,但您必须小心操作过滤器获取数据的位置。我必须为您正在处理的请求获取数据,我到处寻找请求唯一的数据存储,我唯一能找到的是“context.Request.Properties”表,这很可能为什么@Yousuf 使用它。请记住,在处理操作时“context.Response”对象不存在,因此“context.Request”似乎是您可以存储此类数据的唯一地方。 关于强类型响应,不幸的是这是HTTP协议的本质,所有数据都是文本。您可以考虑一些 XML 或 JSON 格式,包括输入以验证数据传输。【参考方案6】:

您可以使用自定义 ActionFilter 来发送自定义标头并访问 HttpContext:

public class AddCustomHeaderFilter : ActionFilterAttribute

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    
       actionExecutedContext.Response.Content.Headers.Add("name", "value");
    

【讨论】:

但是我如何获得标题的“值”? 有趣的问题。看起来您可以在控制器中设置属性 Request.Properties["Count"] = "123" 并在过滤器中使用它。 在过滤器中,可以通过actionContext.Request.Properties["Count"]访问它

以上是关于在 ApiController 中添加自定义响应头的主要内容,如果未能解决你的问题,请参考以下文章

尽管端点在 apicontroller 内部的行为中使用了 cors 类,但 CORS 标头未添加到响应中

自定义响应头 Jersey/Java

ApiController 中长时间运行的任务(使用 WebAPI,自托管 OWIN)

ApiController实现自定义身份认证

NetCore3.1 自定义响应头

SpringCloudGateway 学习笔记 - 自定义过滤器 之 获取响应头