¿Por qué debería usar IHttpActionResult en lugar de HttpResponseMessage?

326

He estado desarrollando con WebApi y he pasado a WebApi2, donde Microsoft ha introducido una nueva IHttpActionResultinterfaz que parece recomendada para usarse antes de devolver a HttpResponseMessage. Estoy confundido sobre las ventajas de esta nueva interfaz. Parece que principalmente proporciona una manera LIGERAMENTE más fácil de crear un HttpResponseMessage.

Yo diría que esto es "abstracción por el bien de la abstracción". ¿Me estoy perdiendo de algo? ¿Cuáles son las ventajas del mundo real que obtengo al usar esta nueva interfaz además de guardar una línea de código?

Old way (WebApi):

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

Nueva forma (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();
    }
}
Jason Roell
fuente
Acabo de encontrar algo interesante. No estoy seguro de si alguien más puede verificar estos resultados. Pero el rendimiento mejoró mucho con algunas llamadas que hice: * Con un ejemplo usando HttpResponseMessageobtuve la respuesta en 9545 ms . * Utilizando el IHttpActionResult, obtuve la misma respuesta en 294 ms .
Chris Lesage
@chrislesage 9545 ms es casi 10 segundos. Incluso 294 ms es un poco lento. Si tiene algo que demore más de 100 ms, entonces hay algo más allí. Hay más en esa historia de lo que se conoce.
AaronLS

Respuestas:

305

Puede decidir no usarlo IHttpActionResultporque su código existente crea uno HttpResponseMessageque no se ajusta a una de las respuestas enlatadas. Sin embargo, puede adaptarse HttpResponseMessagea IHttpActionResultusar la respuesta enlatada de ResponseMessage. Me tomó un tiempo resolver esto, así que quería publicarlo mostrando que no necesariamente tienes que elegir uno u otro:

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

Tenga en cuenta que ResponseMessagees un método de la clase base ApiControllerque su controlador debe heredar.

AaronLS
fuente
1
Me tomó un tiempo descubrir que ResponseMessage ahora es ResponseMessageResult. Tal vez ha sido renombrado recientemente? PD: También ha perdido una nueva palabra clave en la respuesta y muchas gracias por su sugerencia :)
Ilya Chernomordik
10
@IlyaChernomordik ResponseMessagey ResponseMessageResultson dos cosas diferentes. ResponseMessage()es un método del ApiControllercual su controlador debería heredar y, por lo tanto, es solo una llamada al método. Entonces no newse necesita una palabra clave allí. Probablemente no estás heredando ApiControllero estás dentro de un método estático. ResponseMessageResultes el tipo de retorno de ResponseMessage().
AaronLS
3
@IlyaChernomordik Podría haber escrito response = base.ResponseMessage(responseMsg)para aclarar que es un método de la clase base ApiController
AaronLS
Gracias, tuve un servicio que no heredó de ApiController, por eso me tomó un tiempo encontrarlo.
Ilya Chernomordik
3
@pootzko Tienes razón, abordé el hecho de que el OP parecía implicar que creían que los dos enfoques eran mutuamente excluyentes al enmarcarlo como "antigua" frente a "nueva" cuando en realidad no lo son. Creo que la respuesta de Darrel hace un buen trabajo al explicar algunos de los beneficios de devolver IHttpActionResult, y mi respuesta demuestra cómo puede convertir el antiguo HttpResponseMessage en un IHttpActionResult para que pueda obtener lo mejor de ambos mundos.
AaronLS
84

Aún puedes usar HttpResponseMessage. Esa capacidad no desaparecerá. Sentí lo mismo que usted y discutí extensamente con el equipo que no había necesidad de una abstracción adicional. Hubo algunos argumentos para tratar de justificar su existencia, pero nada que me convenció de que valiera la pena.

Es decir, hasta que vi esta muestra de Brad Wilson . Si construye IHttpActionResultclases de una manera que se puede encadenar, obtendrá la capacidad de crear un canal de respuesta de "nivel de acción" para generar el HttpResponseMessage. Debajo de las cubiertas, así es como ActionFiltersse implementan, sin embargo, el orden de estos ActionFiltersno es obvio al leer el método de acción, que es una razón por la que no soy un fanático de los filtros de acción.

Sin embargo, al crear un IHttpActionResultque se puede encadenar explícitamente en su método de acción, puede componer todo tipo de comportamiento diferente para generar su respuesta.

Darrel Miller
fuente
62

Aquí hay varios beneficios de IHttpActionResultmás HttpResponseMessagemencionado en Microsoft ASP.Net de Documentación :

  • Simplifica la unidad de prueba de sus controladores.
  • Mueve la lógica común para crear respuestas HTTP en clases separadas.
  • Aclara la intención de la acción del controlador al ocultar los detalles de bajo nivel de la construcción de la respuesta.

Pero aquí hay algunas otras ventajas de usar que IHttpActionResultvale la pena mencionar:

  • Respeto del principio de responsabilidad única : hace que los métodos de acción tengan la responsabilidad de atender las solicitudes HTTP y no los involucre en la creación de los mensajes de respuesta HTTP.
  • Implementaciones útiles ya definidas en System.Web.Http.Results, a saber: Ok NotFound Exception Unauthorized BadRequest Conflict Redirect InvalidModelState( enlace a la lista completa )
  • Utiliza Async y Await por defecto.
  • Fácil de crear su propio ActionResult con solo implementar el ExecuteAsyncmétodo.
  • puede usar ResponseMessageResult ResponseMessage(HttpResponseMessage response)para convertir HttpResponseMessage a IHttpActionResult .
Behzad Bahmanyar
fuente
32
// this will return HttpResponseMessage as IHttpActionResult
return ResponseMessage(httpResponseMessage); 
Adam Tal
fuente
18

Esta es solo mi opinión personal y la gente del equipo de API web probablemente pueda expresarlo mejor, pero aquí está mi 2c.

En primer lugar, creo que no se trata de uno sobre otro. Puede usarlos según lo que desee hacer en su método de acción, pero para comprender el poder real de IHttpActionResult, es probable que necesite salir de esos métodos auxiliares convenientes ApiControllercomo Ok, por ejemplo ,NotFound , etc.

Básicamente, creo que una clase implementada IHttpActionResultcomo una fábrica deHttpResponseMessage . Con esa mentalidad, ahora se convierte en un objeto que necesita ser devuelto y en una fábrica que lo produce. En el sentido de programación general, puede crear el objeto usted mismo en ciertos casos y, en ciertos casos, necesita una fábrica para hacerlo. Igual que aquí.

Si desea devolver una respuesta que necesita ser construida a través de una lógica compleja, digamos muchos encabezados de respuesta, etc., puede abstraer toda esa lógica en una clase de resultado de acción implementada IHttpActionResulty usarla en múltiples métodos de acción para devolver la respuesta.

Otra ventaja de usar IHttpActionResultcomo tipo de retorno es que hace que el método de acción API web ASP.NET sea similar a MVC. Puede devolver cualquier resultado de acción sin quedar atrapado en los formateadores de medios.

Por supuesto, como señaló Darrel, puede encadenar los resultados de la acción y crear una potente microtubería similar a los propios manejadores de mensajes en la canalización API. Esto lo necesitará dependiendo de la complejidad de su método de acción.

Larga historia corta: no es IHttpActionResultversus HttpResponseMessage. Básicamente, es cómo quieres crear la respuesta. Hágalo usted mismo o a través de una fábrica.

Badri
fuente
El problema que tuve con la justificación de fábrica es que puedo crear fácilmente métodos estáticos como el ResponseFactory.CreateOkResponse()que devuelve HttpResponseMessage, y no tuve que lidiar con las cosas asíncronas al crear la respuesta. Un miembro del equipo mencionó el hecho de que el asíncrono puede ser útil si necesita hacer E / S para generar valores de encabezado. Sin embargo, no estoy seguro de con qué frecuencia sucede eso.
Darrel Miller
Creo que es lo mismo que decir por qué necesitamos un patrón de método de fábrica. ¿Por qué no usar métodos estáticos para crear objetos? Claro, es factible: toda creación de objetos no merece un patrón de fábrica adecuado. Pero, en mi humilde opinión, depende de cómo quieras modelar tus clases. ¿Desea tener lógica para crear diferentes tipos de respuestas, todas agrupadas en una clase en forma de múltiples métodos estáticos o tener diferentes clases basadas en una interfaz? De nuevo, no hay bueno o malo. Todo depende. De todos modos, mi punto es que no es uno sobre otro y personalmente me gusta más la cosa de fábrica :)
Badri
6

La API de Web básicamente de retorno 4 tipo de objeto: void, HttpResponseMessage, IHttpActionResult, y otros tipos fuertes. La primera versión de la API web regresa, HttpResponseMessageque es un mensaje de respuesta HTTP bastante sencillo.

El IHttpActionResultfue introducido por WebAPI 2, que es una especie de envoltura de HttpResponseMessage. Contiene el ExecuteAsync()método para crear un HttpResponseMessage. Simplifica las pruebas unitarias de su controlador.

Otro tipo de retorno son clases de tipo fuerte serializadas por la API web utilizando un formateador de medios en el cuerpo de la respuesta. El inconveniente es que no puede devolver directamente un código de error como un 404. Todo lo que puede hacer es arrojar un HttpResponseExceptionerror.

Peter.Wang
fuente
3

Prefiero implementar la función de interfaz TaskExecuteAsync para IHttpActionResult. Algo como:

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

, donde _request es HttpRequest y _respContent es la carga útil.

appletwo
fuente
2

Tenemos los siguientes beneficios de usar IHttpActionResultmás de HttpResponseMessage:

  1. Al usar el IHttpActionResultsolo nos estamos concentrando en los datos que se enviarán, no en el código de estado. Así que aquí el código será más limpio y muy fácil de mantener.
  2. Las pruebas unitarias del método del controlador implementado serán más fáciles.
  3. Usos asyncy awaitpor defecto.
Debendra Dash
fuente