[英]How to set custom headers when using IHttpActionResult?
在ASP.NET Web API 2中, IHttpActionResult
在简化控制器代码IHttpActionResult
提供了很多价值,我不愿意停止使用它,但我遇到了问题。
我需要在传出响应上设置一个ETag,我找不到任何可以访问响应头的属性。 目前我正在使用ApiController
的Ok<T>(T content)
辅助方法,它返回一个OkNegotiatedContentResult<T>
对象。 这似乎没有暴露任何可以让我修改标题的东西。
我错过了什么,或者在使用股票IHttpActionResult
类型时是否真的没办法做到这一点? 我考虑过一个消息处理程序,但后来我必须弄清楚如何将ETag从动作中传出(对于不同的动作,ETag的生成方式不同,所以这不是为所有动作制作通用处理程序的问题)。
我想避免使用原始的HttpResponseMessage,但此刻看起来很难。
对于您的场景,您需要创建自定义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。 希望这可以帮助。
你可以找到OkNegotiatedContentResult<T>
和NegotiatedContentResult<T>
的源代码,你可以想象它实际上很简单。
您可以创建HttpResponseMessage
,根据需要添加标头,然后从中创建ResponseMessageResult
:
HttpResponseMessage response =new HttpResponseMessage(HttpStatusCode.OK);
response.Headers.Add("MyHeader", "MyHeaderValue");
return ResponseMessage(response);
这是我没有ActionFilterAttributes的简单实现,类似于AlexACD的响应。 我的解决方案使用ResponseMessageResult来实现IHttpActionResult接口。
HttpResponseMessage responseMessage = new HttpResponseMessage(HttpStatusCode.OK);
responseMessage.Headers.Add("Headername", "Value");
ResponseMessageResult response = new ResponseMessageResult(responseMessage);
return response;
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;
}
}
}
这可以通过ActionFilterAttribute来实现,它将在控制器函数之后但在它出去之前检查响应,然后你可以在控制器方法上设置属性来添加这些信息,这是我的实现如下:
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库代码中使用的解决方案,它可以轻松地支持设置任何标头 - 或ExecuteAsync
提供的HttpResponseMessage
上的任何其他属性 - 而不是绑定到任何特定的派生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"));
很老的问题。 可能其他答案在时间上是有意义的,但今天你可以简单地添加这一行而不改变或扩展IHttpActionResult
。 它在您的回复中完美地添加了标题。 确保格式化为RFC 1123标准,如下所示。 否则,虽然Last-Modified出现在标题中,但客户端无法使用HttpClient
读取它。
System.Web.HttpContext.Current.Response.Headers.
Add(Microsoft.Net.Http.Headers.HeaderNames.LastModified, DBdateModified.Value.ToString("r"));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.