Andrey on .NET | ASP.NET Core 2.1: Улучшения WebAPI

ASP.NET Core 2.1: Улучшения WebAPI

ASP.NET Core logoЧасть изменений, которые сделаны в ASP.NET Core 2.1, касаются возможностей создания WebAPI приложений. Они включается в себя специальные соглашения для контроллеров, улучшенную обработку ввода и ошибок, а так же JSON. Рассмотрим все это по подробнее.

Атрибут [ApiController]

ASP.NET Core 2.1 добавляет атрибут [ApiController], который упрощает разработку WebAPI контроллеров. Данный атрибут предназначен для класса контроллера и обеспечивает:

  • Создание ответа с HTTP кодом 400 при ошибке валидирования модели
  • Автоматическое добавление атрибутов для параметров действий контроллера:
    • [FromBody] для комплексных типов;
    • [FromRoute] где это возможно, исходя из настроек маршрутов;
    • [FromQuery] в остальных случаях.

Для корректной работы [ApiController] требуется задание маршрутов с использованием атрибутов.

ActionResult<T>

Новый тип ActionResult<T> позволяет вернуть как обобщенный ответ (например с кодом 404), так и значение заданного типа. Например:

[HttpGet("{id}")]
public ActionResult<User> Get(int id)
{
    if (!_repository.TryGetUser(id, out User user))
        return NotFound();

    return user;
}

В данном примере действие может вернуть экземпляр типа User или ответ 404, если данные не найдены.

Улучшенная обработка запроса

Еще одним нововведением в ASP.NET Core 2.1 стали улучшенные сообщения об ошибках обработки данных запроса. Например, если в запросе данные в формате JSON содержали значение неправильного типа, то раньше клиент получал в ответ следующее:

{
  "id": [
    "The input was not valid."
  ]
}

Теперь текст сообщения об ошибке стал более детальным. Для рассориваемого случая он будет выглядеть так:

{
  "id": [
    "Could not convert string to integer: abc. Path 'id', line 1, position 16."
  ]
}

Вот еще один пример ответа с пояснением ошибки:

{
  "": [
    "Unexpected end when reading JSON. Path '', line 1, position 1."
  ]
}

Валидация параметров запроса

Теперь можно добавлять атрибуты валидации непосредственно в сигнатуре Действия. Например:

public ActionResult Get(string testId, [Required]string name)

Поддержка Problem Details (RFC 7808)

В ASP.NET Core 2.1 появилась поддержка RFC 7808 – Problem Details for HTTP APIs – стандарта, который определяет формат возвращаемого сообщения об ошибках HTTP API. Для использования этой возможности необходимо добавить следующий код в метод ConfigureServices:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = context =>
    {
        var problemDetails = new ValidationProblemDetails(context.ModelState)
        {
            Instance = context.HttpContext.Request.Path,
            Status = StatusCodes.Status400BadRequest,
            Type = "https://asp.net/core",
            Detail = "Please refer to the errors property for additional details."
        };
        return new BadRequestObjectResult(problemDetails)
        {
            ContentTypes = { "application/problem+json", "application/problem+xml" }
        };
    };
});

Другой вариант – вернуть результат вызова ValidationProblem() из самого действия в контроллере.

В любом случае ответ примет следующий вид:

{
  "errors": {
    "Text": [
      "The Text field is required."
    ]
  },
  "type": "https://asp.net/core",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "detail": "Please refer to the errors property for additional details.",
  "instance": "/api/values"
}

Улучшения для JSON Patch

JSON Patch определяет структуру JSON документа для поддержки операции HTTP PATCH (частичной модификации данных). Его поддержка уже существует в ASP.NET Core. В версии 2.1 добавлена возможность использовать операцию test для проверки на наличие заданных значений перед выполнением обновления. Например:

[HttpPatch("{id}")]
public ActionResult Patch(int id, JsonPatchDocument<Value> patch)
{
    var value = new Value { ID = id, Text = "Do" };

    patch.ApplyTo(value, ModelState);

    if (!ModelState.IsValid)
        return BadRequest(ModelState);

    return value;
}

Следующий запрос успешно выполнит обновление данных.

[
  { "op": "test", "path": "/text", "value": "Do" },
  { "op": "add", "path": "/status/1", "value": "Done!" }
]

А обработка запроса вот с такими данными закончится ошибкой, т.к. значение свойства Text у переменной value не совпадает с заданным “Do not”:

[
  { "op": "test", "path": "/text", "value": "Do not" },
  { "op": "add", "path": "/status/1", "value": "Done!" }
]

Добавить комментарий