2015-04-09 9 views
11

Zajmuję się dokumentacją WebAPI 2 i jestem poważnie rozczarowany sposobem, w jaki wyniki działania zostały zaprojektowane. Naprawdę mam nadzieję, że jest lepszy sposób.Typy zwrotu WebApi 2

Więc dokumentacja mówi, mogę powrócić nich:

**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). 

nie widzę czystą drogę powrotu tablicę elementów z niestandardowy kod stanu HTTP, niestandardowych nagłówków i samochodów do uzgodnienia zawartości chociaż.

Co chciałbym zobaczyć coś jak

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

    return result; 
} 

ten sposób mogę spojrzenie na metody i natychmiast zobaczyć co jest zwracane i modyfikować kod statusu oraz nagłówki.

Najbliższą rzeczą, którą znalazłem jest NegotiatedContentResult<T>, z dziwnym podpisem (dlaczego potrzebuje instancji kontrolera?), Ale nie ma możliwości ustawienia niestandardowych nagłówków?

Czy istnieje lepszy sposób?

Odpowiedz

9

nie wiem uważam, że projektanci web-api przeznaczonych dla metod kontrolerów mają problemy z nagłówkami. Wygląda na to, że wzorcem projektu jest użycie DelegatingHandler, ActionFilterAttribute i metody Overdatable ExecuteAsync ApiController do obsługi uwierzytelniania i formatowania odpowiedzi.

Więc może twoja logika do negocjacji treści wiadomości powinna być tam obsługiwana?

Jeśli jednak potrzebujesz kontrolować nagłówki za pomocą metody kontrolera, możesz zrobić małą konfigurację, aby działała. Aby to zrobić, dzięki czemu można stworzyć swój własny DelegationHandler że do przodu wybrany nagłówki z nagłówków „wewnętrzny” Odpowiedź:

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; 
    } 
} 

Następnie należy zarejestrować ten moduł obsługi w konfiguracji web-api, to zwykle w pliku GlobalConfig.cs .

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

Można także napisać własną niestandardową klasę do obiektu odpowiedzi jak ten:

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; 
    } 
} 

i używać go tak jak to w kontroler:

[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

To ApiQueryResult powinno być włączone w ramy, imo. – Evgeni

9

Poniższy kod powinien dać ci wszystko, co chcesz:

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

... czy naprawdę trzeba zwracać tablicę elementów ...

[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

Mam nadzieję, że istnieje lepszy sposób ... To jest brzydkie, dekorowanie metody typem zwrotu? Jak to jest naprawdę lepsze niż po prostu umieszczanie typu zwrotu w komentarzach? – Evgeni

+1

@Eugene: Dekoracja '[ResponseType]' jest opcjonalna, ale jeśli chcesz kontrolować odpowiedź, praca z 'IHttpActionResult' jest obecnie sposobem, aby to zrobić. – afrazier