Хобрук: Ваш путь к мастерству в программировании

Обертывание IHttpActionResult — универсальное решение

Я хотел бы обернуть IHttpActionResult, потому что мне нужны дополнительные данные, которые будут использоваться клиентским приложением.

Мой первый подход состоял в том, чтобы создать и вернуть простой DTO, обернув объект результата в случае успеха:

Ответ DTO:

 public class Response<T>
{
    public string ErrorMessage { get; set; }
    public bool Success { get; set; }
    public string CodeStatus { get; set; }
    public T Result { get; set; }

    public Response(bool isSuccess, [Optional] T result, [Optional] string codeStatus, [Optional] string errorMessage)
    {
        Success = isSuccess;
        Result = result;
        CodeStatus = codeStatus;
        ErrorMessage = errorMessage;
    }
}

Контроллер:

    public IHttpActionResult Get(int id)
    {
        return BadRequest(new Response<MyObjectClass>(false, null,"Invalid Id",400));
        ...
        return Ok(new Response<MyObjectClass>(true, result);

    }

Я нашел это очень неэффективным способом борьбы с обертыванием. Я не нахожу это очень элегантным способом. Я попытался найти какое-то общее решение и в итоге получил следующее:

Пример действия контроллера:

    public IHttpActionResult GetById(int id)
    {
        var result = _someService.Get(id);

        if (result == null)
            return NotFound().WithError("Invalid Id");

        return Ok().WithSuccess(result);
    }

Это по-прежнему возвращает Response DTO.

Я завернул IHttpActionResult для создания Response DTO:

public class HttpActionResult : IHttpActionResult
{
    private readonly string _errorMessage;
    private readonly IHttpActionResult _innerResult;
    private readonly object _result;
    private readonly bool _isSuccess;

    public HttpActionResult(IHttpActionResult inner, bool isSuccess, object result,string errorMessage)
    {
        _errorMessage = errorMessage;
        _innerResult = inner;
        _result = result;
        _isSuccess = isSuccess;            
    }


    public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        HttpResponseMessage response = await _innerResult.ExecuteAsync(cancellationToken);
        response.Content = new ObjectContent(typeof(Response), new Response(_isSuccess, _result, ((int)response.StatusCode).ToString(), _errorMessage), new JsonMediaTypeFormatter());
        return response;
    }
}

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

public static class IHttpActionResultExtensions
    {
        public static IHttpActionResult WithSuccess(this IHttpActionResult inner, object result = null, string message = null)
        {
            return new HttpActionResult(inner, true, result, message);
        }

        public static IHttpActionResult WithError(this IHttpActionResult inner,  string message = null)
        {
            return new HttpActionResult(inner, false,null, message);
        }
    }

Каковы альтернативы переносу http-сообщений в контроллер API? Какие слабые места вы видите в моем решении?


Ответы:


1

Кстати, я вижу некоторые слабые места в вашем подходе:

  1. WebAPI предназначен для создания веб-сервисов RESTful. Почему вы пытаетесь предоставить еще один уровень статуса и другие детали? HTTP достаточно богат, чтобы удовлетворить эти требования. Например, вы можете использовать стандартные коды состояния и субкод следующим образом: 500.1, 500.2.

  2. Успех или неудачу легче выразить с помощью кодов состояния HTTP. 2XX диапазон для успешных операций, а для неудачной можно использовать, например, 400 (Bad Request). 401 за несанкционированный доступ... 500 за сбой сервера...

  3. WebAPI уже предоставляет ModelState, чтобы позволить фреймворку построить объект ответа. Используйте его и постарайтесь не изобретать велосипед.

  4. Опять же, будьте проще. Сущность ответа помещается в тело ответа. Успех или неудача выражаются кодами состояния. Сведения о неверном запросе добавляются в словарь ModelState. Сообщение об ошибке должно быть установлено на ReasonPhrase ответа.

IHttpActionResult предназначены для преобразования результатов вашего домена в ответ HTTP. То есть вы на правильном пути, за исключением случаев, когда вы пытаетесь вернуть объект ответа как есть. Мой совет: вы должны использовать свой IHttpActionResult, чтобы настроить каждую деталь вашего собственного объекта ответа на стандартную семантику HTTP и уведомлять об ошибках, используя готовый подход ModelState, который хорошо работает.

20.12.2016
  • Ад.1-2. Создание нового кода состояния не входило в мои намерения. Я просто хотел включить это в тело ответа. Я видел, что это довольно распространенный шаблон, когда все это содержится в теле ответа, а фактический результат обертывается этой дополнительной информацией. 21.12.2016
  • Объявление. 3-4. Уточню ModelState, что я не учел в этом сценарии. Я использовал его только для проверки. 21.12.2016
  • @laszczm Не все так RESTful, как они рекламируют: D Около 3-4, проверка модели использует ModelState, но вы можете использовать ее не только для сообщения об ошибках. 21.12.2016

  • 2

    Избегайте IHttpActionResult и используйте HttpResponseException с Business Entity в качестве типа результата. Как и в вашем решении, вы не можете писать статически типизированные тестовые примеры.

    Например,

    protected void ThrowHttpError(HttpStatusCode statusCode, string message) 
    {
         throw new HttpResponseException(
             new HttpResponseMessage(statusCode) {
                ReasonPhrase = message,
                // HTTP 2.0 ignores ReasonPhrase
                // so we send ReasonPhrase again in the Content
                Content = new StringContent(message)
         });
    }
    
    
    // some generic option...
    protected void ThrowHttpError<T>(HttpStatusCode statusCode, T content) 
        where T:class
    {
         throw new HttpResponseException(
             new HttpResponseMessage(statusCode) {
                ReasonPhrase = "Error",
                Content = JsonConvert.Serialize(content)
         });
    }
    

    Ваши методы,

    public async Task<Product> Get(long id){
    
        var product = await context.Products
           .FirstOrDefaultAsync( x=> x.ProductID == id);
    
        if(product==null){
            ThrowHttpError(HttpStatusCode.NotFound, 
               $"Product not found for {id}");
        }
    
        if(product.RequiresValidation){
    
            // generic version....
    
            ThrowHttpError(HttpStatusCode.Conflict,
               new Product{
                    ProductID = product.ProductID,
                    ValidationRequestCode = product.ValidationRequestCode
            });
        }
    
        return product;
    }
    

    Кроме того, вы можете настроить метод ThrowHttpError в соответствии с вашими потребностями. Самое приятное то, что это все еще можно проверить.

    20.12.2016
    Новые материалы

    Управление DOM для чайников вроде меня
    Одной из первых вещей, которую мы рассмотрели, когда начали изучать Javascript во Flatiron, была модель DOM. Кто он? Чем он занимается? Он больше машина, чем человек? Ну да довольно много. ДОМ..

    Что такое структура данных?
    Структура данных хранит и извлекает данные. Все, что обеспечивает эти две функции, является структурой данных . Период. Вы можете пропустить оставшуюся часть статьи, если ответ..

    мои январские чтения по программированию
    Эрик Эллиот Программирование приложения JavaScript Эл Свейгарт «Автоматизируйте скучные вещи с помощью Python» Прогрессивное веб-приложение Google..

    Создание ассоциаций секвелизации с помощью инструмента командной строки Sequelize
    Sequelize - популярный, простой в использовании инструмент объектно-реляционного сопоставления (ORM) JavaScript, который работает с базами данных SQL. Довольно просто начать новый проект с..

    Искусственный интеллект в юридической отрасли - пример прогнозирования судебных решений с помощью глубокого обучения
    На протяжении всей истории люди полагались на суды, присяжных, королей и королев в отправлении правосудия. Сегодня способность судов обеспечивать справедливое и быстрое правосудие для своих..

    Введение в машинное обучение для обнаружения аномалий (часть 1)
    Тщательно созданный, тщательно спроектированный ресурс для специалистов по данным. Часть 1 Главы 03 из Руководства по машинному обучению для обнаружения аномалий Внимание! Прежде чем вы..

    Начало работы с Pulumi в Digital Ocean
    Цифровой океан (ДО) — отличная альтернатива многим другим поставщикам облачных услуг. DO предоставляет простой и понятный пользовательский интерфейс, упрощающий управление инфраструктурой и..