2014-01-29 52 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; 
     } 
    } 
} 
相關問題