2015-04-09 7 views
11

Я смотрю документацию WebAPI 2, и я сильно разочарован тем, как результаты действий были заархивированы. Я действительно надеюсь, что есть лучший способ.Обратные типы WebApi 2

Так документация говорит, что я могу вернуть эти:

**void** Return empty 204 (No Content) 

**HttpResponseMessage** Convert directly to an HTTP response message. 

**IHttpActionResult** Call ExecuteAsync to create an HttpResponseMessage, then convert to an HTTP response message. 

**Other type** Write the serialized return value into the response body; return 200 (OK). 

Я не вижу чистый способ вернуть массив элементов с кодом состояния пользовательских HTTP, пользовательские заголовки и авто переговоры содержание, хотя.

То, что я хотел бы видеть что-то вроде

public HttpResult<Item> Post() 
{ 
    var item = new Item(); 
    var result = new HttpResult<Item>(item, HttpStatusCode.Created); 
    result.Headers.Add("header", "header value"); 

    return result; 
} 

Таким образом, я могу глянуть по методу и сразу увидеть, Что возвращается, и изменить код статуса и заголовки.

Ближайшая вещь, которую я нашел, это NegotiatedContentResult<T>, с странной подписью (зачем нужен экземпляр контроллера?), Но нет способа установить пользовательские заголовки?

Есть ли лучший способ?

ответ

9

Я не подумайте, что разработчики web-api предназначены для того, чтобы методы контроллера были запущены с заголовками. Образец дизайна, похоже, должен использовать DelegatingHandler, ActionFilterAttribute и переопределяемый метод ExecuteAsync ApiController для обработки аутентификации и отклика.

Так что, может быть, ваша логика для согласования содержимого сообщений должна быть обработана?

Однако, если вам определенно необходимо управлять заголовками внутри вашего контроллера, вы можете сделать небольшую настройку, чтобы заставить его работать. Для этого вы можете создать свой собственный DelegationHandler, который пересылает выбранные заголовки из «внутренней» заголовки ответа:

public class MessageHandlerBranding : DelegatingHandler { 
    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     var response = await base.SendAsync(request, cancellationToken); 
     //If we want to forward headers from inner content we can do this: 
     if (response.Content != null && response.Content.Headers.Any()) 
     { 
      foreach (var hdr in response.Content.Headers) 
      { 
       var keyUpr = hdr.Key.ToUpper(); //Response will not tolerate setting of some header values 
       if (keyUpr != "CONTENT-TYPE" && keyUpr != "CONTENT-LENGTH") 
       { 
        string val = hdr.Value.Any() ? hdr.Value.FirstOrDefault() : ""; 
        response.Headers.Add(hdr.Key, val);      
       } 
      } 
     } 
     //Add our branding header to each response 
     response.Headers.Add("X-Powered-By", "My product"); 
     return response; 
    } 
} 

Затем зарегистрировать этот обработчик в конфигурации веб-API, это, как правило, в файле GlobalConfig.cs ,

config.MessageHandlers.Add(new MessageHandlerBranding()); 

Вы также можете написать свой собственный класс для объекта ответа, как это:

public class ApiQueryResult<T> : IHttpActionResult where T : class 
{ 
    public ApiQueryResult(HttpRequestMessage request) 
    { 
     this.StatusCode = HttpStatusCode.OK; ; 
     this.HeadersToAdd = new List<MyStringPair>(); 
     this.Request = request; 
    } 

    public HttpStatusCode StatusCode { get; set; } 
    private List<MyStringPair> HeadersToAdd { get; set; } 
    public T Content { get; set; } 
    private HttpRequestMessage Request { get; set; } 

    public void AddHeaders(string headerKey, string headerValue) 
    { 
     this.HeadersToAdd.Add(new MyStringPair(headerKey, headerValue)); 
    } 

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) 
    { 
     var response = this.Request.CreateResponse<T>(this.StatusCode, this.Content); 
     foreach (var hdr in this.HeadersToAdd) 
     { 
      response.Content.Headers.Add(hdr.key, hdr.value); 
     } 
     return Task.FromResult(response); 
    } 


    private class MyStringPair 
    { 
     public MyStringPair(string key, string value) 
     { 
      this.key = key; 
      this.value = value; 
     } 
     public string key; 
     public string value; 
    } 
} 

И использовать его, как это в вашем контроллере:

[HttpGet] 
    public ApiQueryResult<CustomersView> CustomersViewsRow(int id) 
    { 
     var ret = new ApiQueryResult<CustomersView>(this.Request); 
     ret.Content = this.BLL.GetOneCustomer(id); 
     ret.AddHeaders("myCustomHkey","myCustomValue"); 
     return ret; 
    } 
+3

Что ApiQueryResult должен быть включен в структуру, imo. – Evgeni

9

Следующий код должен дать вам все, что вы хотите:

[ResponseType(typeof(Item))] 
public IHttpActionResult Post() 
{ 
    var item = new Item(); 
    HttpContext.Current.Response.AddHeader("Header-Name", "Header Value"); 
    return Content(HttpStatusCode.Created, item); 
} 

... если вам действительно нужно, чтобы вернуть массив элементов ...

[ResponseType(typeof(List<Item>))] 
public IHttpActionResult Post() 
{ 
    var items = new List<Item>(); 
    // Do something to fill items here... 
    HttpContext.Current.Response.AddHeader("Item-Count", items.Count.ToString()); 
    return Content(HttpStatusCode.Created, items); 
} 
+0

Я надеюсь, что есть лучший способ ... Это уродливое, украшающее метод с возвращаемым типом? Как это действительно лучше, чем просто вводить возвращаемый тип в комментариях? – Evgeni

+1

@Eugene: Декорация '[ResponseType]' не является обязательной, но если вы хотите контролировать ответ, в настоящее время это работает с 'IHttpActionResult'. – afrazier

Смежные вопросы