Perché dovrei usare IHttpActionResult invece di HttpResponseMessage?


326

Ho sviluppato con WebApi e sono passato a WebApi2, dove Microsoft ha introdotto una nuova IHttpActionResultinterfaccia che sembra raccomandata per essere utilizzata per la restituzione di a HttpResponseMessage. Sono confuso sui vantaggi di questa nuova interfaccia. Sembra principalmente fornire un modo LEGGERMENTE più semplice per creare un HttpResponseMessage.

Vorrei sostenere che si tratta di "astrazione per amore dell'astrazione". Mi sto perdendo qualcosa? Quali sono i vantaggi del mondo reale che ottengo dall'utilizzo di questa nuova interfaccia oltre a salvare una riga di codice?

Vecchio modo (WebApi):

public HttpResponseMessage Delete(int id)
{
    var status = _Repository.DeleteCustomer(id);
    if (status)
    {
        return new HttpResponseMessage(HttpStatusCode.OK);
    }
    else
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
}

New Way (WebApi2):

public IHttpActionResult Delete(int id)
{
    var status = _Repository.DeleteCustomer(id);
    if (status)
    {
        //return new HttpResponseMessage(HttpStatusCode.OK);
        return Ok();
    }
    else
    {
        //throw new HttpResponseException(HttpStatusCode.NotFound);
        return NotFound();
    }
}

Ho appena trovato qualcosa di interessante. Non sono sicuro che questi risultati possano essere verificati da qualcun altro. Ma le prestazioni sono migliorate molto con alcune chiamate che ho fatto: * Con un esempio usando HttpResponseMessageho ottenuto la risposta in 9545 ms . * Usando IHttpActionResultho ottenuto la stessa risposta in 294 ms .
chris lesage,

@chrislesage 9545 ms è quasi 10 secondi. Anche 294ms è un po 'lento. Se hai qualcosa che impiega più di 100 ms allora qualcos'altro è al lavoro lì. C'è di più in quella storia di quello che incontra l'ete.
AaronS

Risposte:


305

Potresti decidere di non utilizzarlo IHttpActionResultperché il codice esistente crea un valore HttpResponseMessageche non si adatta a una delle risposte predefinite. È tuttavia possibile adattare HttpResponseMessageal IHttpActionResultutilizzando la risposta predefinita di ResponseMessage. Mi ci è voluto un po 'per capirlo, quindi volevo pubblicarlo dimostrando che non devi necessariamente scegliere l'uno o l'altro:

public IHttpActionResult SomeAction()
{
   IHttpActionResult response;
   //we want a 303 with the ability to set location
   HttpResponseMessage responseMsg = new HttpResponseMessage(HttpStatusCode.RedirectMethod);
   responseMsg.Headers.Location = new Uri("http://customLocation.blah");
   response = ResponseMessage(responseMsg);
   return response;
}

Nota, ResponseMessageè un metodo della classe base da ApiControllercui il controller dovrebbe ereditare.


1
Mi ci è voluto un po 'per scoprire che ResponseMessage ora è ResponseMessageResult. Forse è stato rinominato di recente? PS Hai anche perso una nuova parola chiave nella risposta e grazie mille per il suggerimento :)
Ilya Chernomordik

10
@IlyaChernomordik ResponseMessagee ResponseMessageResultsono due cose diverse. ResponseMessage()è un metodo da ApiControllercui il controller dovrebbe ereditare e pertanto è solo una chiamata di metodo. Quindi non newè necessaria alcuna parola chiave lì. Probabilmente non erediti ApiControllero sei all'interno di un metodo statico. ResponseMessageResultè il tipo di ritorno di ResponseMessage().
AaronLS,

3
@IlyaChernomordik Avrei potuto scrivere response = base.ResponseMessage(responseMsg)per renderlo più chiaro che si tratta di un metodo della classe base ApiController
AaronLS

Grazie, avevo davvero un servizio che non ereditava da ApiController, ecco perché ci è voluto del tempo per trovarlo.
Ilya Chernomordik,

3
@pootzko Hai ragione, ho affrontato il fatto che l'OP sembrava implicare che credevano che i due approcci si escludessero a vicenda, inquadrandolo come "vecchio modo" contro "nuovo modo", quando in realtà non lo sono. Penso che la risposta di Darrel faccia un buon lavoro nel spiegare alcuni dei vantaggi della restituzione di IHttpActionResult e la mia risposta dimostra come si è in grado di convertire il vecchio HttpResponseMessage in IHttpActionResult in modo da poter avere il meglio dei due mondi.
AaronLS,

84

Puoi ancora usare HttpResponseMessage. Questa capacità non andrà via. Mi sono sentito allo stesso modo di te e ho discusso a fondo con il team che non c'era bisogno di un'ulteriore astrazione. Ci furono alcuni argomenti per cercare di giustificare la sua esistenza, ma nulla mi convinse che valesse la pena.

Cioè, fino a quando non ho visto questo campione da Brad Wilson . Se si costruiscono le IHttpActionResultclassi in un modo che può essere concatenato, si ottiene la possibilità di creare una pipeline di risposta "a livello di azione" per la generazione di HttpResponseMessage. Sotto le coperte, ecco come ActionFiltersvengono implementate, tuttavia, l'ordinamento di questi ActionFiltersnon è ovvio quando si legge il metodo di azione, che è uno dei motivi per cui non sono un fan dei filtri di azione.

Tuttavia, creando un elemento IHttpActionResultche può essere esplicitamente incatenato nel metodo di azione, è possibile comporre tutti i tipi di comportamento diverso per generare la risposta.


62

Ecco alcuni vantaggi di cui IHttpActionResultsopra HttpResponseMessagenella documentazione Microsoft ASP.Net :

  • Semplifica i test unitari dei controller.
  • Sposta la logica comune per la creazione di risposte HTTP in classi separate.
  • Rende più chiaro l'intento dell'azione del controller, nascondendo i dettagli di basso livello della costruzione della risposta.

Ma ecco alcuni altri vantaggi dell'utilizzo IHttpActionResultdegno di nota:

  • Rispetto del principio della singola responsabilità : far sì che i metodi di azione abbiano la responsabilità di servire le richieste HTTP e non le coinvolgono nella creazione dei messaggi di risposta HTTP.
  • Implementazioni utili già definite in System.Web.Http.Results ovvero: Ok NotFound Exception Unauthorized BadRequest Conflict Redirect InvalidModelState( collegamento all'elenco completo )
  • Utilizza Async e Await per impostazione predefinita.
  • Facile creare il proprio ActionResult semplicemente implementando il ExecuteAsyncmetodo.
  • è possibile utilizzare ResponseMessageResult ResponseMessage(HttpResponseMessage response)per convertire HttpResponseMessage in IHttpActionResult .

32
// this will return HttpResponseMessage as IHttpActionResult
return ResponseMessage(httpResponseMessage); 

18

Questa è solo la mia opinione personale e la gente del team API Web può probabilmente articolarla meglio, ma ecco il mio 2c.

Prima di tutto, penso che non si tratti di una questione piuttosto che dell'altra. È possibile utilizzarli entrambi, a seconda di cosa si vuole fare nel tuo metodo di azione, ma al fine di comprendere il vero potere di IHttpActionResult, si avrà probabilmente bisogno di uscire quei comodi metodi helper di ApiControllercome Ok, NotFound, etc.

Fondamentalmente, penso che una classe si stia implementando IHttpActionResultcome una fabbrica di HttpResponseMessage. Con questa mentalità, ora diventa un oggetto che deve essere restituito e una fabbrica che lo produce. In senso generale di programmazione, è possibile creare l'oggetto da soli in alcuni casi e in alcuni casi, è necessaria una fabbrica per farlo. Anch'io.

Se si desidera restituire una risposta che deve essere costruita attraverso una logica complessa, ad esempio molte intestazioni di risposta, ecc., È possibile astrarre tutta quella logica in una classe di risultati dell'azione che implementa IHttpActionResult e utilizzarla in più metodi di azione per restituire la risposta.

Un altro vantaggio dell'utilizzo IHttpActionResult come tipo restituito è che rende il metodo di azione dell'API Web ASP.NET simile a MVC. È possibile restituire qualsiasi risultato di azione senza essere scoperti nei formatter multimediali.

Naturalmente, come notato da Darrel, è possibile concatenare i risultati dell'azione e creare una potente micro-pipeline simile ai gestori dei messaggi stessi nella pipeline API. Ciò sarà necessario a seconda della complessità del metodo di azione.

Per farla breve: non è IHttpActionResultcontro HttpResponseMessage. Fondamentalmente, è come si desidera creare la risposta. Fallo da solo o attraverso una fabbrica.


Il problema che ho avuto con la justfication di fabbrica è che posso facilmente creare metodi statici come ResponseFactory.CreateOkResponse()quello che restituiscono HttpResponseMessage e non ho dovuto occuparmi delle cose asincrone durante la creazione della risposta. Un membro del team ha menzionato il fatto che l'asincrono può essere utile se è necessario eseguire I / O per generare valori di intestazione. Non sono sicuro di quanto spesso accada però.
Darrel Miller,

Penso che sia come dire perché abbiamo bisogno del modello di metodo di fabbrica. Perché non usare solo metodi statici per creare oggetti. Certo, è fattibile - ogni creazione di oggetti non merita un modello di fabbrica adeguato. Ma poi IMHO dipende da come vuoi modellare le tue classi. Vuoi avere la logica per creare diversi tipi di risposte tutte raggruppate in una classe sotto forma di più metodi statici o avere classi diverse basate su un'interfaccia. Ancora una volta, non c'è niente di buono o cattivo. Tutto dipende. Comunque, il mio punto è che non è uno sopra l'altro e personalmente mi piace di più la cosa in fabbrica :)
Badri,

6

L'API Web fondamentalmente ritorno 4 tipo di oggetto: void, HttpResponseMessage, IHttpActionResulte altri tipi forti. HttpResponseMessageViene restituita la prima versione dell'API Web che è un messaggio di risposta HTTP piuttosto semplice.

L' IHttpActionResultintroduzione è stata introdotta da WebAPI 2, che è una specie di avvolgente HttpResponseMessage. Contiene il ExecuteAsync()metodo per creare un HttpResponseMessage. Semplifica i test unitari del controller.

Altri tipi di restituzione sono tipi di classi con caratteri forti serializzati dall'API Web che utilizza un formattatore multimediale nel corpo della risposta. Lo svantaggio era che non puoi restituire direttamente un codice di errore come un 404. Tutto quello che puoi fare è lanciare un HttpResponseExceptionerrore.


3

Preferirei implementare la funzione di interfaccia TaskExecuteAsync per IHttpActionResult. Qualcosa di simile a:

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = _request.CreateResponse(HttpStatusCode.InternalServerError, _respContent);

        switch ((Int32)_respContent.Code)
        { 
            case 1:
            case 6:
            case 7:
                response = _request.CreateResponse(HttpStatusCode.InternalServerError, _respContent);
                break;
            case 2:
            case 3:
            case 4:
                response = _request.CreateResponse(HttpStatusCode.BadRequest, _respContent);
                break;
        } 

        return Task.FromResult(response);
    }

, dove _request è HttpRequest e _respContent è il payload.


2

Abbiamo i seguenti vantaggi dell'utilizzo di IHttpActionResultover HttpResponseMessage:

  1. Usando il IHttpActionResultci stiamo concentrando solo sui dati da inviare, non sul codice di stato. Quindi qui il codice sarà più pulito e molto facile da mantenere.
  2. Il test unitario del metodo del controller implementato sarà più semplice.
  3. Usi asynce awaitper impostazione predefinita.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.