Por que devo usar IHttpActionResult em vez de HttpResponseMessage?

326

Estou desenvolvendo com o WebApi e fui para o WebApi2, onde a Microsoft introduziu uma nova IHttpActionResultinterface que parece recomendada para ser usada no retorno de a HttpResponseMessage. Estou confuso sobre as vantagens desta nova interface. Parece fornecer apenas uma maneira ligeiramente mais fácil de criar um HttpResponseMessage.

Eu argumentaria que isso é "abstração em prol da abstração". Estou esquecendo de algo? Quais são as vantagens do mundo real que recebo ao usar essa nova interface, além de talvez salvar uma linha de código?

Maneira antiga (WebApi):

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

Nova maneira (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
fonte
Acabei de encontrar algo interessante. Não tenho certeza se esses resultados podem ser verificados por outra pessoa. Mas o desempenho melhorou muito com algumas chamadas que fiz: * Com um exemplo de uso HttpResponseMessage, recebi a resposta em 9545 ms . * Usando o IHttpActionResult, recebi a mesma resposta em 294 ms .
chris lesage
@chrislesage 9545 ms é de quase 10 segundos. Mesmo 294ms é meio lento. Se você tiver algo que leva mais de 100 ms, então algo mais está funcionando lá. Há mais nessa história do que o que se conhece.
AaronLS 15/10/19

Respostas:

305

Você pode optar por não usar, IHttpActionResultporque o código existente cria um HttpResponseMessageque não se encaixa em uma das respostas em lata. No entanto, você pode se adaptar HttpResponseMessageao IHttpActionResultuso da resposta enlatada de ResponseMessage. Demorei um pouco para descobrir isso, então eu queria publicá-lo mostrando que você não precisa necessariamente escolher um ou outro:

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

Observe que ResponseMessageé um método da classe base ApiControllerque seu controlador deve herdar.

AaronLS
fonte
1
Demorei um pouco para descobrir que o ResponseMessage agora é ResponseMessageResult. Talvez tenha sido renomeado recentemente? PS: Você também perdeu uma nova palavra-chave na resposta e agradece muito pela sugestão :)
Ilya Chernomordik
10
@IlyaChernomordik ResponseMessagee ResponseMessageResultsão duas coisas diferentes. ResponseMessage()é um método do ApiControllerqual seu controlador deve herdar e, portanto, é apenas uma chamada de método. Portanto, nenhuma newpalavra-chave é necessária lá. Você provavelmente não está herdando ApiControllerou está dentro de um método estático. ResponseMessageResulté o tipo de retorno de ResponseMessage().
AaronLS 06/02
3
@IlyaChernomordik eu poderia ter escrito response = base.ResponseMessage(responseMsg)para torná-lo mais claro que é um método do ApiController classe base
AaronLS
Obrigado, eu realmente tinha um serviço que não herdava do ApiController, por isso demorou um pouco para encontrar.
Ilya Chernomordik
3
@pootzko Você está certo, eu lidei com o fato de que o OP parecia sugerir que eles acreditavam que as duas abordagens eram mutuamente exclusivas ao enquadrá-lo como "maneira antiga" vs "maneira nova" quando na verdade não são. Acho que a resposta de Darrel explica bem alguns dos benefícios do retorno de IHttpActionResult, e minha resposta demonstra como você pode converter o antigo HttpResponseMessage em um IHttpActionResult para que você possa ter o melhor dos dois mundos.
AaronLS
84

Você ainda pode usar HttpResponseMessage. Essa capacidade não desaparece. Eu me senti da mesma maneira que você e argumentei extensivamente com a equipe que não havia necessidade de uma abstração adicional. Houve alguns argumentos para tentar justificar sua existência, mas nada que me convenceu de que valia a pena.

Ou seja, até eu ver essa amostra de Brad Wilson . Se você construir IHttpActionResultclasses de uma maneira que possa ser encadeada, você poderá criar um pipeline de resposta "no nível de ação" para gerar o HttpResponseMessage. Nos bastidores, é assim que ActionFilterssão implementadas, no entanto, a ordenação ActionFiltersdelas não é óbvia ao ler o método de ação, que é uma das razões pelas quais eu não sou fã de filtros de ação.

No entanto, ao criar um IHttpActionResultque possa ser explicitamente encadeado em seu método de ação, você poderá compor todos os tipos de comportamentos diferentes para gerar sua resposta.

Darrel Miller
fonte
62

Aqui estão vários benefícios mencionados IHttpActionResultacima HttpResponseMessagena documentação do Microsoft ASP.Net :

  • Simplifica o teste de unidade de seus controladores.
  • Move a lógica comum para criar respostas HTTP em classes separadas.
  • Torna mais clara a intenção da ação do controlador, ocultando os detalhes de baixo nível da construção da resposta.

Mas aqui estão algumas outras vantagens do uso que IHttpActionResultvale a pena mencionar:

  • Respeitando o princípio de responsabilidade única : faça com que os métodos de ação tenham a responsabilidade de atender às solicitações HTTP e não os envolva na criação das mensagens de resposta HTTP.
  • Implementações úteis já definidas no System.Web.Http.Results, a saber: Ok NotFound Exception Unauthorized BadRequest Conflict Redirect InvalidModelState( link para a lista completa )
  • Usa Assíncrono e Aguardar por padrão.
  • Fácil de criar o próprio ActionResult apenas implementando o ExecuteAsyncmétodo.
  • você pode usar ResponseMessageResult ResponseMessage(HttpResponseMessage response)para converter HttpResponseMessage em IHttpActionResult .
Behzad Bahmanyar
fonte
32
// this will return HttpResponseMessage as IHttpActionResult
return ResponseMessage(httpResponseMessage); 
Adam Tal
fonte
18

Esta é apenas a minha opinião pessoal e o pessoal da equipe de APIs da web provavelmente pode articular melhor, mas aqui está o meu 2c.

Antes de tudo, acho que não se trata de um sobre o outro. Você pode usá-los tanto dependendo do que você quer fazer em seu método de ação, mas a fim de compreender o verdadeiro poder IHttpActionResult, você provavelmente terá que passo fora desses métodos auxiliares convenientes de ApiControllertais como Ok, NotFound, etc.

Basicamente, acho que uma classe implementada IHttpActionResultcomo uma fábrica de HttpResponseMessage. Com essa mentalidade, agora se torna um objeto que precisa ser devolvido e uma fábrica que o produz. No sentido geral de programação, você pode criar o objeto em certos casos e, em certos casos, precisa de uma fábrica para fazer isso. O mesmo aqui.

Se você deseja retornar uma resposta que precisa ser construída por meio de uma lógica complexa, digamos muitos cabeçalhos de resposta, etc., você pode abstrair toda essa lógica em uma classe de resultado de ação implementada IHttpActionResulte usá-la em vários métodos de ação para retornar a resposta.

Outra vantagem de usar IHttpActionResultcomo tipo de retorno é que ele torna o método de ação da API da Web do ASP.NET semelhante ao MVC. Você pode retornar qualquer resultado de ação sem ser pego nos formatadores de mídia.

Obviamente, como observado por Darrel, você pode encadear os resultados das ações e criar um micro-pipeline poderoso semelhante aos próprios manipuladores de mensagens no pipeline da API. Isso será necessário dependendo da complexidade do seu método de ação.

Para encurtar a história - não é IHttpActionResultcontra HttpResponseMessage. Basicamente, é assim que você deseja criar a resposta. Faça você mesmo ou através de uma fábrica.

Badri
fonte
O problema que tive com a justificativa de fábrica é que posso criar facilmente métodos estáticos como ResponseFactory.CreateOkResponse()esse, retornando HttpResponseMessage, e não precisei lidar com o material assíncrono ao criar a resposta. Um membro da equipe mencionou o fato de que a assíncrona pode ser útil se você precisar fazer E / S para gerar valores de cabeçalho. Não sei quantas vezes isso acontece.
Darrel Miller
Eu acho que é o mesmo que dizer por que precisamos de um padrão de método de fábrica. Por que não usar métodos estáticos para criar objetos? Certamente, é possível - toda criação de objeto não merece um padrão de fábrica adequado. Mas então o IMHO depende de como você deseja modelar suas classes. Deseja ter lógica para criar diferentes tipos de respostas, todas agrupadas em uma classe na forma de vários métodos estáticos ou ter classes diferentes com base em uma interface. Novamente, não há bom ou ruim. Tudo depende. De qualquer forma, meu ponto é que não é um sobre o outro e eu, pessoalmente, como coisa fábrica melhor :)
Badri
6

A API Web retornar basicamente 4 tipo de objeto: void, HttpResponseMessage, IHttpActionResult, e outros tipos fortes. A primeira versão da API da Web retorna, o HttpResponseMessageque é uma mensagem de resposta HTTP bastante direta.

O IHttpActionResultfoi introduzido pelo WebAPI 2, que é uma espécie de quebra de HttpResponseMessage. Ele contém o ExecuteAsync()método para criar um HttpResponseMessage. Simplifica o teste de unidade do seu controlador.

Outro tipo de retorno são tipos de classes de tipo forte serializadas pela API da Web usando um formatador de mídia no corpo da resposta. A desvantagem foi que você não pode retornar diretamente um código de erro como o 404. Tudo o que você pode fazer é gerar um HttpResponseExceptionerro.

Peter.Wang
fonte
3

Prefiro implementar a função de interface 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);
    }

, em que _request é o HttpRequest e _respContent é a carga útil.

appletwo
fonte
2

Temos os seguintes benefícios do uso IHttpActionResultexcessivo HttpResponseMessage:

  1. Ao usar o IHttpActionResult, estamos nos concentrando apenas nos dados a serem enviados e não no código de status. Portanto, aqui o código será mais limpo e fácil de manter.
  2. O teste de unidade do método do controlador implementado será mais fácil.
  3. Usa asynce awaitpor padrão.
Debendra Dash
fonte