2014-01-29 54 views
34

在的ASP.NET Web API 2,IHttpActionResult提供了很多有价值的简化控制器代码,我不愿意停止使用它,但我已经打了一个问题。如何在使用IHttpActionResult时设置自定义标题?

我需要设置一个ETag上的输出响应,我找不到这使我获得了响应的头部任何财产。此刻,我使用ApiController中的Ok<T>(T content)帮助器方法,该方法返回OkNegotiatedContentResult<T>对象。这似乎没有任何暴露,让我修改标题。

我缺少的东西,或者是真的没有办法同时使用存量IHttpActionResult类型做到这一点?我考虑过一个消息处理程序,但是接下来我必须弄清楚如何将ETag传递出动作(对于不同的动作,ETag的生成方式不同),所以这不是为所有动作制作通用处理程序的问题。

我想避免使用原始HttpResponseMessage,但此刻是在寻找困难。

回答

29

对于您的情况,您需要创建一个自定义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>,正如你可以想像是简单的实际的源代码。

+0

我考虑去一个不同的路线,因为我不希望延长所有内置的响应。我目前的想法是有一个'HttpResponseHeaders'字典,我将它合并到消息处理程序中的IHttpActionResult中。这似乎工作,除了没有响应头的构造函数。 – ehdv

+0

您可以查看我对其他评论的回复。你的方法是不正确的。 –

+1

我用这个例子不断得到“406不可接受”,直到我用引号将值包裹起来:即用'string.Format(“\”{0} \“”,this.ETagValue)'替换'this.ETagValue'''。我试图添加内容类型参数(profile =“http:/ ...” - 必须引用href),我实际上偶然发现了这个解决方案。 – biscuit314

26

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

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

海报已明确要求使用IHttpActionResult的解决方案 –

+7

@ AdrianHope-Bailie这是一个X/Y问题,*任何人*试图做海报所要求的是做错了。这个解决方案是框架编写人员希望您为获得对响应进行细粒度控制所做的预期解决方案。 –

2

这可以用一个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 
     }; 
    } 
} 

}

2

这里是一个解决方案,我在我的常见的Web API 2库代码中使用,可以轻松支持设置任何头 - 或者在ExecuteAsync提供--without被捆绑到任何具体的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")); 
8

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

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

'ResponseMessage'返回'ResponseMessageResult' –

3
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; 
     } 
    } 
} 
相关问题