Почему я должен использовать IHttpActionResult вместо HttpResponseMessage?

326

Я работал с WebApi и перешел к WebApi2, где Microsoft представила новый IHttpActionResultинтерфейс, который, кажется, рекомендуется использовать вместо возврата a HttpResponseMessage. Я запутался в преимуществах этого нового интерфейса. Похоже, что в основном это просто НЕМНОГО более простой способ создания HttpResponseMessage.

Я бы сделал аргумент, что это «абстракция ради абстракции». Я что-то упускаю? Какие преимущества в реальном мире я получаю от использования этого нового интерфейса, кроме, возможно, сохранения строки кода?

Старый способ (WebApi):

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

Новый путь (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();
    }
}
Джейсон Роелл
источник
Я просто нашел что-то интересное. Я не уверен, что эти результаты могут быть проверены кем-то другим. Но при некоторых вызовах, которые я сделал, производительность значительно улучшилась: * Используя пример, HttpResponseMessageя получил ответ в 9545 мс . * Используя IHttpActionResultI, я получил тот же ответ в 294 мс .
Крис Лесаж
@ chrislesage 9545 мс - это почти 10 секунд. Даже 294 мс - это довольно медленно. Если у вас есть что-то, что занимает более 100 мс, то там работает что-то еще. В этой истории есть нечто большее, чем кажется.
AaronLS

Ответы:

305

Возможно, вы решите не использовать, IHttpActionResultпотому что ваш существующий код создает такой HttpResponseMessage, который не соответствует ни одному из стандартных ответов. Однако вы можете адаптироваться HttpResponseMessageк IHttpActionResultиспользованию постоянного ответа ResponseMessage. Мне потребовалось некоторое время, чтобы понять это, поэтому я хотел опубликовать это, показывая, что вам не обязательно выбирать один или другой:

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

Обратите внимание, ResponseMessageэто метод базового класса ApiController, от которого ваш контроллер должен наследовать.

AaronLS
источник
1
Мне потребовалось некоторое время, чтобы узнать, что ResponseMessage теперь ResponseMessageResult. Возможно, он был недавно переименован? PS Вы также пропустили новое ключевое слово в ответе, и большое спасибо за предложение :)
Илья Черномордик
10
@IlyaChernomordik ResponseMessageи ResponseMessageResultэто две разные вещи. ResponseMessage()это метод, от ApiControllerкоторого ваш контроллер должен наследовать, и, следовательно, это просто вызов метода. Так что здесь не newнужно ключевое слово. Вы, вероятно, не наследуете ApiControllerили находитесь внутри статического метода. ResponseMessageResultтип возвращаемого значения ResponseMessage().
AaronLS
3
@IlyaChernomordik Я мог бы написать, response = base.ResponseMessage(responseMsg)чтобы прояснить, что это метод базового класса ApiController
AaronLS
Спасибо, у меня действительно был сервис, который не наследовал от ApiController, поэтому на его поиск потребовалось некоторое время.
Илья Черномордик
3
@pootzko Вы правы, я обратил внимание на тот факт, что ФП, по-видимому, подразумевает, что они полагают, что оба подхода являются взаимоисключающими, представляя его как «старый способ» против «нового способа», хотя на самом деле это не так. Я думаю, что ответ Даррела хорошо объясняет некоторые преимущества возврата IHttpActionResult, и мой ответ демонстрирует, как вы можете преобразовать старое HttpResponseMessage в IHttpActionResult, чтобы у вас было лучшее из обоих миров.
AaronLS
84

Вы все еще можете использовать HttpResponseMessage. Эта возможность не исчезнет. Я чувствовал то же самое, что и вы, и много спорил с командой, что нет необходимости в дополнительной абстракции. Было несколько аргументов, чтобы попытаться оправдать его существование, но ничто не убедило меня в том, что оно того стоит.

То есть, пока я не увидел этот образец от Брэда Уилсона . Если вы создаете IHttpActionResultклассы так, чтобы их можно было объединить в цепочку, вы получаете возможность создать конвейер ответов «на уровне действия» для генерации HttpResponseMessage. Под покровами, это то, как ActionFiltersэто реализовано, однако порядок их ActionFiltersне очевиден при чтении метода действия, и это одна из причин, по которой я не фанат фильтров действий.

Однако, создавая, IHttpActionResultкоторый может быть явно связан в вашем методе действия, вы можете составить все виды различного поведения для генерации вашего ответа.

Даррел Миллер
источник
62

Вот несколько преимуществ IHttpActionResultнад HttpResponseMessageупомянутым в Microsoft ASP.Net документации :

  • Упрощает модульное тестирование ваших контроллеров.
  • Перемещает общую логику для создания HTTP-ответов в отдельные классы.
  • Делает намерение действия контроллера более понятным, скрывая низкоуровневые детали построения ответа.

Но вот некоторые другие преимущества использования, о которых IHttpActionResultстоит упомянуть:

  • Соблюдение принципа единой ответственности : заставить методы действий отвечать за обслуживание HTTP-запросов и не привлекать их к созданию ответных HTTP-сообщений.
  • Полезные реализации, уже определенные в System.Web.Http.Results, а именно: Ok NotFound Exception Unauthorized BadRequest Conflict Redirect InvalidModelState( ссылка на полный список )
  • Использует Async и Await по умолчанию.
  • Легко создать собственный ActionResult, просто внедрив ExecuteAsyncметод.
  • Вы можете использовать ResponseMessageResult ResponseMessage(HttpResponseMessage response)для преобразования HttpResponseMessage в IHttpActionResult .
Бехзад Бахманьяр
источник
32
// this will return HttpResponseMessage as IHttpActionResult
return ResponseMessage(httpResponseMessage); 
Адам Тал
источник
18

Это только мое личное мнение, и ребята из команды веб-API, вероятно, могут сформулировать это лучше, но вот мой 2c.

Прежде всего, я думаю, что это не вопрос одного над другим. Вы можете использовать их оба в зависимости от того, что вы хотите сделать в вашем методе действия, но чтобы понять реальную силу IHttpActionResult, вам, вероятно, нужно будет выйти за пределы таких удобных вспомогательных методов ApiController, как Ok, NotFoundи т. Д.

В основном, я думаю, что класс реализуется IHttpActionResultкак фабрика HttpResponseMessage. С таким настроем он теперь становится объектом, который необходимо вернуть, и фабрикой, которая его производит. В общем смысле программирования, вы можете создать объект самостоятельно в определенных случаях, а в некоторых случаях вам нужна фабрика для этого. Тоже самое.

Если вы хотите вернуть ответ, который должен быть построен с помощью сложной логики, скажем, большого количества заголовков ответа и т. Д., Вы можете абстрагировать всю эту логику в реализующий класс действия IHttpActionResultи использовать его в нескольких методах действия для возврата ответа.

Еще одним преимуществом использования в IHttpActionResultкачестве возвращаемого типа является то, что он делает метод действия ASP.NET Web API похожим на MVC. Вы можете вернуть любой результат действия, не попадая в средства форматирования медиа.

Конечно, как отметил Даррел, вы можете объединить результаты действий и создать мощный микропроцесс, подобный самим обработчикам сообщений в конвейере API. Это вам понадобится в зависимости от сложности вашего метода действий.

Короче говоря - это не IHttpActionResultпротив HttpResponseMessage. По сути, это то, как вы хотите создать ответ. Сделай сам или через фабрику.

Бадри
источник
Проблема, с которой я столкнулся при фабричной установке, заключается в том, что я могу легко создавать статические методы, такие как ResponseFactory.CreateOkResponse()этот, возвращающие HttpResponseMessage, и мне не приходилось иметь дело с асинхронными вещами при создании ответа. Один из членов команды упомянул тот факт, что асинхронность может быть полезна, если вам нужно выполнить ввод-вывод для генерации значений заголовка. Не уверен, как часто это происходит, хотя.
Даррел Миллер
Я думаю, это то же самое, что сказать, зачем нам нужен шаблон фабричного метода. Почему бы просто не использовать статические методы для создания объектов. Несомненно, это выполнимо - каждое создание объекта не заслуживает надлежащего фабричного образца. Но тогда ИМХО это зависит от того, как вы хотите смоделировать свои занятия. Хотите ли вы иметь логику для создания разных типов ответов, втиснутых в класс в виде нескольких статических методов, или иметь разные классы на основе интерфейса. Опять же, нет хорошего или плохого. Все это зависит. В любом случае, я хочу сказать, что это не одно из другого, и мне лично больше нравится заводская вещь :)
Badri
6

Web API в основном возвращает 4 типа объекта: void, HttpResponseMessage, IHttpActionResult, и другие сильные тип. Первая версия Web API возвращает HttpResponseMessageдовольно простое ответное сообщение HTTP.

Он IHttpActionResultбыл представлен WebAPI 2, который является своего рода оберткой HttpResponseMessage. Он содержит ExecuteAsync()метод для создания HttpResponseMessage. Это упрощает модульное тестирование вашего контроллера.

Другой тип возвращаемых данных - это классы со строгой типизацией, сериализуемые Web API с использованием средства форматирования мультимедиа в теле ответа. Недостатком было то, что вы не можете напрямую вернуть код ошибки, такой как 404. Все, что вы можете сделать, это выдать HttpResponseExceptionошибку.

Peter.Wang
источник
3

Я бы предпочел реализовать интерфейсную функцию TaskExecuteAsync для IHttpActionResult. Что-то вроде:

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

где _request - HttpRequest, а _respContent - полезная нагрузка.

appletwo
источник
2

У нас есть следующие преимущества использования IHttpActionResultболее HttpResponseMessage:

  1. При использовании IHttpActionResultмы концентрируемся только на данных для отправки, а не на коде состояния. Так что здесь код будет чище и очень прост в обслуживании.
  2. Модульное тестирование реализованного метода контроллера будет проще.
  3. Использует asyncи awaitпо умолчанию.
Дебендра Дэш
источник