Dlaczego powinienem używać IHttpActionResult zamiast HttpResponseMessage?

326

Pracowałem z WebApi i przeniosłem się na WebApi2, gdzie Microsoft wprowadził nowy IHttpActionResultinterfejs, który wydaje się zalecany do użycia zamiast zwracania HttpResponseMessage. Jestem zdezorientowany zaletami tego nowego interfejsu. Wydaje się, że głównie zapewnia PO LEKKO łatwiejszy sposób utworzenia HttpResponseMessage.

Argumentowałbym, że jest to „abstrakcja ze względu na abstrakcję”. Czy coś brakuje? Jakie są rzeczywiste korzyści płynące z korzystania z tego nowego interfejsu oprócz oszczędzania linii kodu?

Stary sposób (WebApi):

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

Nowa droga (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
źródło
Właśnie znalazłem coś interesującego. Nie jestem pewien, czy te wyniki mogą być zweryfikowane przez kogoś innego. Ale wydajność uległa znacznej poprawie w przypadku niektórych połączeń: * Korzystając z przykładu HttpResponseMessage, otrzymałem odpowiedź w 9545 ms . * Używając IHttpActionResultotrzymałem tę samą odpowiedź w 294 ms .
Chris Lesage,
@chrislesage 9545 ms to prawie 10 sekund. Nawet 294 ms jest trochę powolne. Jeśli coś trwa dłużej niż 100 ms, wtedy działa tam coś innego. W tej historii jest coś więcej niż tylko poznanie.
AaronLS,

Odpowiedzi:

305

Możesz zdecydować, aby nie używać, IHttpActionResultponieważ istniejący kod buduje taki HttpResponseMessage, który nie pasuje do jednej z odpowiedzi w puszce. Możesz jednak dostosować się HttpResponseMessagedo IHttpActionResultkorzystania z konserwowanej odpowiedzi programu ResponseMessage. Zajęło mi to trochę czasu, aby to rozgryźć, więc chciałem to opublikować, pokazując, że niekoniecznie musisz wybierać jedno lub drugie:

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

Uwaga: ResponseMessagejest metodą klasy podstawowej ApiController, z której kontroler powinien dziedziczyć.

AaronLS
źródło
1
Zajęło mi trochę czasu, aby dowiedzieć się, że ResponseMessage jest teraz ResponseMessageResult. Być może ostatnio został przemianowany? PS W odpowiedzi przeoczyłeś także nowe słowo kluczowe i wielkie dzięki za sugestie :)
Ilya Chernomordik
10
@IlyaChernomordik ResponseMessagei ResponseMessageResultsą dwie różne rzeczy. ResponseMessage()jest metodą, z ApiControllerktórej kontroler powinien dziedziczyć, a zatem jest tylko wywołaniem metody. Dlatego nie newjest potrzebne żadne słowo kluczowe. Prawdopodobnie nie dziedziczysz ApiControllerlub jesteś w metodzie statycznej. ResponseMessageResultjest rodzajem zwrotu ResponseMessage().
AaronLS,
3
@IlyaChernomordik Mógłbym napisać, response = base.ResponseMessage(responseMsg)aby wyjaśnić, że jest to metoda klasy podstawowej ApiController
AaronLS
Dzięki, rzeczywiście miałem usługę, która nie odziedziczyła po ApiController, dlatego znalezienie jej zajęło trochę czasu.
Ilya Chernomordik
3
@pootzko Masz rację, zwróciłem uwagę na fakt, że OP wydawało się sugerować, że wierzyli, iż te dwa podejścia wzajemnie się wykluczają, określając je jako „stary sposób” a „nowy sposób”, podczas gdy w rzeczywistości tak nie jest. Myślę, że odpowiedź Darrela dobrze wyjaśnia wyjaśnienie niektórych zalet zwracania IHttpActionResult, a moja odpowiedź pokazuje, w jaki sposób jesteś w stanie przekonwertować stary HttpResponseMessage na IHttpActionResult, abyś miał to, co najlepsze z obu światów.
AaronLS
84

Nadal możesz używać HttpResponseMessage. Ta zdolność nie zniknie. Czułam to samo co ty i szeroko argumentowałam z zespołem, że nie ma potrzeby dodatkowej abstrakcji. Pojawiło się kilka argumentów, aby spróbować uzasadnić jego istnienie, ale nic nie przekonało mnie, że warto.

To znaczy, dopóki nie zobaczyłem tej próbki od Brada Wilsona . Jeśli konstruujesz IHttpActionResultklasy w sposób, który można połączyć w łańcuch, zyskujesz możliwość stworzenia potoku odpowiedzi „na poziomie akcji” do wygenerowania HttpResponseMessage. ActionFiltersJednak pod przykrywkami jest to realizowane, ale ich kolejność ActionFiltersnie jest oczywista podczas czytania metody akcji, co jest jednym z powodów, dla których nie jestem fanem filtrów akcji.

Jednak tworząc metodę, IHttpActionResultktóra może być wyraźnie powiązana w metodzie działania, możesz skomponować różne zachowania, aby wygenerować odpowiedź.

Darrel Miller
źródło
62

Oto kilka korzyści IHttpActionResultponad HttpResponseMessagewymienione w Microsoft ASP.NET Dokumentacja :

  • Upraszcza testowanie jednostek przez kontrolery.
  • Przenosi wspólną logikę tworzenia odpowiedzi HTTP do oddzielnych klas.
  • Wyjaśnia cel działania kontrolera, ukrywając niskiego poziomu szczegóły konstruowania odpowiedzi.

IHttpActionResultWarto jednak wspomnieć o kilku innych zaletach używania :

  • Poszanowanie zasady pojedynczej odpowiedzialności : powoduje, że metody działania są odpowiedzialne za obsługę żądań HTTP i nie angażują ich w tworzenie komunikatów odpowiedzi HTTP.
  • Przydatne implementacje zdefiniowane już w System.Web.Http.Results, a mianowicie: Ok NotFound Exception Unauthorized BadRequest Conflict Redirect InvalidModelState( link do pełnej listy )
  • Używa Async i OczekujeDomyślnie .
  • Łatwe tworzenie własnych ActionResult po prostu poprzez wdrożenieExecuteAsync metody.
  • możesz użyć ResponseMessageResult ResponseMessage(HttpResponseMessage response)do konwersji HttpResponseMessage na IHttpActionResult .
Behzad Bahmanyar
źródło
32
// this will return HttpResponseMessage as IHttpActionResult
return ResponseMessage(httpResponseMessage); 
Adam Tal
źródło
18

To tylko moja osobista opinia i ludzie z zespołu API mogą prawdopodobnie lepiej to wyrazić, ale oto mój 2c.

Przede wszystkim myślę, że nie jest to kwestia jednego nad drugim. Możesz użyć ich obu w zależności od tego, co chcesz zrobić w metodzie działania, ale aby zrozumieć prawdziwą moc IHttpActionResult, prawdopodobnie będziesz musiał wyjść poza te wygodne metody pomocnicze, ApiControllertakie jak Ok:NotFound itp

Zasadniczo myślę, że klasa wdrażająca IHttpActionResultjako fabrykaHttpResponseMessage . Po takim nastawieniu staje się teraz przedmiotem, który należy zwrócić, i fabryką, która go wytwarza. W ogólnym sensie programowania możesz w niektórych przypadkach sam stworzyć obiekt, aw niektórych przypadkach potrzebujesz fabryki, aby to zrobić. To samo tutaj.

Jeśli chcesz zwrócić odpowiedź, która musi zostać zbudowana za pomocą złożonej logiki, powiedzmy, wiele nagłówków odpowiedzi itp., Możesz wyodrębnić całą tę logikę do implementacji klasy wyników akcji IHttpActionResulti użyć jej w wielu metodach akcji, aby zwrócić odpowiedź.

Kolejną zaletą używania IHttpActionResultjako typu zwracanego jest to, że sprawia, że ​​metoda akcji ASP.NET Web API jest podobna do MVC. Możesz zwrócić dowolny wynik akcji, nie wpadając w formaty mediów.

Oczywiście, jak zauważył Darrel, możesz łączyć wyniki działań i tworzyć potężny mikroprocesor podobny do samych programów obsługi komunikatów w potoku API. Będzie to potrzebne w zależności od złożoności metody działania.

Krótko mówiąc - to nie jest IHttpActionResultkontra HttpResponseMessage. Zasadniczo tak właśnie chcesz utworzyć odpowiedź. Zrób to sam lub przez fabrykę.

Badri
źródło
Problem, który miałem z fabrycznym uzasadnieniem, polega na tym, że mogę łatwo tworzyć metody statyczne, takie jak ResponseFactory.CreateOkResponse()zwracana funkcja HttpResponseMessage, i nie musiałem zajmować się sprawami asynchronicznymi podczas tworzenia odpowiedzi. Jeden z członków zespołu wspomniał, że asynchronizacja może być przydatna, jeśli konieczne jest wykonanie operacji we / wy w celu wygenerowania wartości nagłówka. Nie jestem jednak pewien, jak często to się zdarza.
Darrel Miller
Myślę, że to tak samo, jak mówienie, dlaczego potrzebujemy wzorca metody fabrycznej. Dlaczego po prostu nie używać statycznych metod do tworzenia obiektów. Oczywiście, jest to wykonalne - każde stworzenie obiektu nie zasługuje na prawidłowy wzorzec fabryczny. Ale potem IMHO zależy od tego, jak chcesz modelować swoje zajęcia. Czy chcesz mieć logikę, aby tworzyć różne typy odpowiedzi wszystkie wciśnięte w klasę w postaci wielu metod statycznych lub mieć różne klasy oparte na interfejsie. Znów nie ma dobra ani zła. To wszystko zależy. W każdym razie mam na myśli to, że nie jest to jedna nad drugą i osobiście bardziej lubię fabrykę :)
Badri
6

Web API zasadzie powrotu 4 Typ obiektu: void, HttpResponseMessage, IHttpActionResultoraz inne silne typy. Zwraca pierwszą wersję interfejsu API sieci Web, HttpResponseMessagektóra jest dość prostym komunikatem odpowiedzi HTTP.

IHttpActionResultWprowadził WebAPI 2, który jest w rodzaju opasania HttpResponseMessage. Zawiera ExecuteAsync()metodę tworzenia HttpResponseMessage. Upraszcza testowanie jednostkowe kontrolera.

Innym typem zwracanym są rodzaj silnie typowanych klas serializowanych przez interfejs API sieci Web przy użyciu formatera multimediów w treści odpowiedzi. Wadą jest to, że nie można bezpośrednio zwrócić kodu błędu, takiego jak 404. Wszystko, co możesz zrobić, to zgłosić HttpResponseExceptionbłąd.

Peter.Wang
źródło
3

Wolę zaimplementować funkcję interfejsu TaskExecuteAsync dla IHttpActionResult. Coś jak:

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

, gdzie _request to HttpRequest, a _respContent to ładunek.

appletwo
źródło
2

Mamy następujące zalety korzystania z IHttpActionResultponad HttpResponseMessage:

  1. Korzystając z IHttpActionResult, koncentrujemy się tylko na przesyłanych danych, a nie na kodzie statusu. Więc tutaj kod będzie czystszy i bardzo łatwy w utrzymaniu.
  2. Testy jednostkowe zaimplementowanej metody sterownika będą łatwiejsze.
  3. Używa asynci awaitdomyślnie.
Debendra Dash
źródło