如何使用IHttpActionResult设置自定义标题?

在ASP.NET Web API 2中, IHttpActionResult在简化控制器代码IHttpActionResult提供了很大的价值,我不愿意停止使用它,但是我遇到了一个问题。

我需要设置一个传出的响应ETag,我找不到任何属性,让我访问响应的标题。 目前我使用ApiControllerOk<T>(T content)辅助方法,该方法返回一个OkNegotiatedContentResult<T>对象。 这似乎没有任何暴露,这将让我修改标题。

我是否缺less某些东西,或者在使用库存IHttpActionResulttypes时真的没有办法做到这一点? 我考虑过一个消息处理程序,但是接下来我必须弄清楚如何将ETag传递出去(对于不同的动作,ETag的生成方式是不同的,所以不需要为所有的操作制作通用的处理程序)。

我想避免使用原始的HttpResponseMessage,但目前看起来很困难。

对于你的场景,你需要创build一个自定义的IHttpActionResult 。 以下是我从OkNegotiatedContentResult<T>派生的示例,它运行Content-Negotiation并设置Ok状态代码。

 public class CustomOkResult<T> : OkNegotiatedContentResult<T> { public CustomOkResult(T content, ApiController controller) : base(content, controller) { } public CustomOkResult(T content, IContentNegotiator contentNegotiator, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters) : base(content, contentNegotiator, request, formatters) { } public string ETagValue { get; set; } public override async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) { HttpResponseMessage response = await base.ExecuteAsync(cancellationToken); response.Headers.ETag = new EntityTagHeaderValue(this.ETagValue); return response; } } 

控制器

 public class ValuesController : ApiController { public IHttpActionResult Get() { return new CustomOkResult<string>(content: "Hello World!", controller: this) { ETagValue = "You ETag value" }; } } 

请注意,您也可以从NegotiatedContentResult<T>派生,在这种情况下,您需要自己提供StatusCode。 希望这可以帮助。

你可以findOkNegotiatedContentResult<T>NegotiatedContentResult<T>的源代码,你可以想象其实很简单。

您可以创build一个HttpResponseMessage ,根据需要添加标题,然后从中创buildResponseMessageResult

 HttpResponseMessage response =new HttpResponseMessage(HttpStatusCode.OK); response.Headers.Add("MyHeader", "MyHeaderValue"); return ResponseMessage(response); 

这里是我没有ActionFilterAttributes的简单实现,和AlexACD的响应类似。 我的解决scheme使用实现IHttpActionResult接口的ResponseMessageResult。

 HttpResponseMessage responseMessage = new HttpResponseMessage(HttpStatusCode.OK); responseMessage.Headers.Add("Headername", "Value"); ResponseMessageResult response = new ResponseMessageResult(responseMessage); return response; 

这可以通过一个ActionFilterAttribute来实现,它将检查控制器function之后但在它出去之前的响应,然后可以在控制器方法上设置属性来添加这些信息,下面是我的实现:

 public class EnableETag : ActionFilterAttribute { /// <summary> /// NOTE: a real production situation, especially when it involves a web garden /// or a web farm deployment, the tags must be retrieved from the database or some other place common to all servers. /// </summary> private static ConcurrentDictionary<string, EntityTagHeaderValue> etags = new ConcurrentDictionary<string, EntityTagHeaderValue>(); public override void OnActionExecuting(HttpActionContext context) { var request = context.Request; if (request.Method == HttpMethod.Get) { var key = GetKey(request); ICollection<EntityTagHeaderValue> etagsFromClient = request.Headers.IfNoneMatch; if (etagsFromClient.Count > 0) { EntityTagHeaderValue etag = null; if (etags.TryGetValue(key, out etag) && etagsFromClient.Any(t => t.Tag == etag.Tag)) { context.Response = new HttpResponseMessage(HttpStatusCode.NotModified); SetCacheControl(context.Response); } } } } public override void OnActionExecuted(HttpActionExecutedContext context) { var request = context.Request; var key = GetKey(request); EntityTagHeaderValue etag; if (!etags.TryGetValue(key, out etag) || request.Method == HttpMethod.Put || request.Method == HttpMethod.Post) { etag = new EntityTagHeaderValue("\"" + Guid.NewGuid().ToString() + "\""); etags.AddOrUpdate(key, etag, (k, val) => etag); } context.Response.Headers.ETag = etag; SetCacheControl(context.Response); } private string GetKey(HttpRequestMessage request) { return request.RequestUri.ToString(); } /// <summary> /// Defines the time period to hold item in cache (currently 10 seconds) /// </summary> /// <param name="response"></param> private void SetCacheControl(HttpResponseMessage response) { response.Headers.CacheControl = new CacheControlHeaderValue() { MaxAge = TimeSpan.FromSeconds(10), MustRevalidate = true, Private = true }; } } 

}

下面是我在通用的Web API 2库代码中使用的一个解决scheme,它可以轻松地支持设置任何头文件 – 或者ExecuteAsync提供的HttpResponseMessage上的任何其他属性 – 而ExecuteAsync绑定到任何特定派生的NegotiatedContentResult实现:

 public class FlexibleNegotiatedContentResult<T> : NegotiatedContentResult<T> { private readonly Action<HttpResponseMessage> _responseMessageDelegate; public FlexibleNegotiatedContentResult(HttpStatusCode statusCode, T content, IContentNegotiator contentNegotiator, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters) : base(statusCode, content, contentNegotiator, request, formatters) { } public FlexibleNegotiatedContentResult(HttpStatusCode statusCode, T content, ApiController controller, Action<HttpResponseMessage> responseMessageDelegate = null) : base(statusCode, content, controller) { _responseMessageDelegate = responseMessageDelegate; } public override async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) { HttpResponseMessage responseMessage = await base.ExecuteAsync(cancellationToken); if (_responseMessageDelegate != null) { _responseMessageDelegate(responseMessage); } return responseMessage; } } 

和一个示例用法:

 new FlexibleNegotiatedContentResult<string>(HttpStatusCode.Created, "Entity created!", controller, response => response.Headers.Location = new Uri("https://myapp.com/api/entity/1")); 
 public static class HttpExtentions { public static IHttpActionResult AddHeader(this IHttpActionResult action, string headerName, IEnumerable<string> headerValues) { return new HeaderActionResult(action, headerName, headerValues); } public static IHttpActionResult AddHeader(this IHttpActionResult action, string headerName, string header) { return AddHeader(action, headerName, new[] {header}); } private class HeaderActionResult : IHttpActionResult { private readonly IHttpActionResult action; private readonly Tuple<string, IEnumerable<string>> header; public HeaderActionResult(IHttpActionResult action, string headerName, IEnumerable<string> headerValues) { this.action = action; header = Tuple.Create(headerName, headerValues); } public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) { var response = await action.ExecuteAsync(cancellationToken); response.Headers.Add(header.Item1, header.Item2); return response; } } }