Создание веб-API с помощью ASP.NET Core

ASP.NET Core поддерживает создание веб-API с помощью контроллеров или минимальных API. В веб-API контроллеры — это классы, производные от ControllerBase. Контроллеры активируются и удаляются на основе каждого запроса.

В этой статье показано, как использовать контроллеры для обработки веб-запросов API. Сведения о создании веб-API без контроллеров см. в руководстве по созданию минимального API с помощью ASP.NET Core.

Класс ControllerBase

Веб-API на основе контроллера состоит из одного или нескольких классов контроллера, которые являются производными от ControllerBase. Шаблон проекта веб-API предоставляет начальный контроллер:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Контроллеры веб-API обычно являются производными от ControllerBase, а не от Controller. Controller является производным от ControllerBase и добавляет поддержку для представлений, обеспечивая обработку веб-страниц, а не запросов веб-API. Если тот же контроллер должен поддерживать представления и веб-API, происходит наследование от Controller.

Класс ControllerBase предоставляет множество свойств и методов, которые удобны для обработки HTTP-запросов. Например, CreatedAtAction возвращает код состояния 201.

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

В следующей таблице приведены примеры методов в ControllerBase.

Способ Примечания.
BadRequest Возвращает код состояния 400.
NotFound Возвращает код состояния 404.
PhysicalFile Возвращает файл.
TryUpdateModelAsync Вызывает привязку модели.
TryValidateModel Вызывает проверку модели.

Список всех доступных методов и свойств см. здесь: ControllerBase.

Атрибуты

Пространство имен Microsoft.AspNetCore.Mvc предоставляет атрибуты, которые можно использовать для настройки поведения контроллеров и методов действия веб-API. В следующем примере атрибуты используются для указания поддерживаемой команды действия HTTP и всех известных кодов состояния HTTP, которые могут быть возвращены:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Вот еще примеры доступных атрибутов.

Атрибут Примечания.
[Route] Определяет шаблон URL-адреса для контроллера или действия.
[Bind] Задает префикс и свойства, которые добавляются для привязки модели.
[HttpGet] Определяет действие, которое поддерживает команду действия HTTP GET.
[Consumes] Указывает типы данных, которые принимает действие.
[Produces] Указывает типы данных, которые возвращает действие.

Список доступных атрибутов см. в пространстве имен Microsoft.AspNetCore.Mvc.

Атрибут ApiController

Атрибут [ApiController] можно применить к классу контроллера для включения следующих специализированных схем поведения API:

Атрибут в определенных контроллерах

Атрибут [ApiController] может применяться к определенным контроллерам, как показано в следующем примере из шаблона проекта:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Атрибут в нескольких контроллерах

Один из подходов к использованию атрибута на более чем одном контроллере заключается в создании пользовательского базового класса контроллера, аннотированного атрибутом [ApiController]. В следующем примере демонстрируется пользовательский базовый класс и производный от него контроллер:

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("[controller]")]
public class PetsController : MyControllerBase

Атрибут в сборке

Атрибут [ApiController] может быть применен к сборке. При применении атрибута [ApiController] к сборке атрибут [ApiController] применяется ко всем контроллерам в сборке. Его нельзя отменить для отдельных контроллеров. Примените атрибут уровня сборки к файлу Program.cs:

using Microsoft.AspNetCore.Mvc;
[assembly: ApiController]

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Обязательная маршрутизация атрибутов

Атрибут [ApiController] требует обязательной маршрутизации атрибутов. Например:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Действия недоступны через обычные маршруты, определяемые UseEndpoints, UseMvc или UseMvcWithDefaultRoute.

Автоматические отклики HTTP 400

Благодаря атрибуту [ApiController] ошибки проверки модели автоматически активируют отклик HTTP 400. В результате следующий код ненужен в методе действия:

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

Для выполнения предыдущей проверки ASP.NET Core MVC использует фильтр действий ModelStateInvalidFilter.

Отклик BadRequest по умолчанию

Для ответов HTTP 400 по умолчанию возвращается тип отклика ValidationProblemDetails. Следующий текст ответа является примером сериализованного типа:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

Тип ValidationProblemDetails:

  • предоставляет распознаваемый компьютером формат для указания ошибок в откликах веб-API;
  • соответствует спецификации RFC 7807.

Чтобы обеспечить согласованность автоматических и настраиваемых ответов, вызовите метод ValidationProblem, а не BadRequest. ValidationProblem возвращает объект ValidationProblemDetails, а также автоматический ответ.

Запись в журнал автоматических откликов HTTP 400

Чтобы регистрировать автоматические ответы 400, задайте свойство делегата InvalidModelStateResponseFactory для выполнения пользовательской обработки. По умолчанию InvalidModelStateResponseFactory использует ProblemDetailsFactory для создания экземпляра ValidationProblemDetails.

В следующем примере показано, как получить экземпляр ILogger<TCategoryName> для регистрации сведений об автоматическом ответе 400:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
      // To preserve the default behavior, capture the original delegate to call later.
        var builtInFactory = options.InvalidModelStateResponseFactory;

        options.InvalidModelStateResponseFactory = context =>
        {
            var logger = context.HttpContext.RequestServices
                                .GetRequiredService<ILogger<Program>>();

            // Perform logging here.
            // ...

            // Invoke the default behavior, which produces a ValidationProblemDetails
            // response.
            // To produce a custom response, return a different implementation of 
            // IActionResult instead.
            return builtInFactory(context);
        };
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Отключение автоматической активации отклика HTTP 400

Чтобы отключить автоматическую активацию отклика HTTP 400, задайте свойству SuppressModelStateInvalidFilter значение true. Добавьте выделенный ниже код:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Вывод параметров источника привязки

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

Атрибут Источник привязки
[FromBody] Текст запроса
[FromForm] Данные формы в тексте запроса
[FromHeader] Заголовок запроса
[FromQuery] Параметры строки запроса для запроса
[FromRoute] Данные маршрута из текущего запроса
[FromServices] Служба запросов, внедренная в качестве параметра действия
[AsParameters] Параметры методов

Предупреждение

Не используйте [FromRoute], если значения могут содержать %2f (то есть /). Для %2f не будет применяться отмена экранирования /. Используйте [FromQuery], если значение может содержать %2f.

Без атрибута [ApiController] или атрибутов источника привязки, таких как [FromQuery], среда выполнения ASP.NET Core попытается использовать связыватель модели для составного объекта. Связыватель модели для составного объекта извлекает данные из поставщиков значений в определенном порядке.

В следующем примере атрибут [FromQuery] указывает, что значение параметра discontinuedOnly задано в строке запроса URL-адреса для запроса:

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

Атрибут [ApiController] применяет правила зависимости к источникам данных по умолчанию для параметров действий. Эти правила избавляют от необходимости вручную определять источники привязки путем применения атрибутов к параметрам действий. Правила зависимости источника привязки работают следующим образом:

  • [FromServices] выводится для параметров сложного типа, зарегистрированных в контейнере DI.
  • [FromBody] выводится для параметров сложного типа, не зарегистрированных в контейнере DI. Исключением из правила зависимости [FromBody] является любой сложный встроенный тип со специальным значением, такой как IFormCollection и CancellationToken. Код определения источника привязки игнорирует эти особые типы.
  • [FromForm] выводится для параметров действия с типом IFormFile и IFormFileCollection. Он не выводится ни для каких простых или определяемых пользователем типов.
  • [FromRoute] выводится для любого имени параметра действия, соответствующего параметру в шаблоне маршрута. Если параметру действия соответствуют несколько маршрутов, любое значение маршрута рассматривается как [FromRoute].
  • [FromQuery] выводится для любых других параметров действия.

Заметки о выводе FromBody

[FromBody] не определен для простых типов, таких как string или int. Таким образом, атрибут [FromBody] должен использоваться для простых типов, когда требуются эти функции.

Если у действия более одного параметра для привязки из текста запроса, выдается исключение. Например, все следующие сигнатуры метода действия вызывают исключение:

  • [FromBody] выводится для обеих параметров, так как они являются сложными типами.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • Атрибут [FromBody], заданный одному параметру, выводится для другого, так как это сложный тип.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • Атрибут [FromBody] выводится для обоих параметров.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Заметки о выводе FromServices

Привязка параметров позволяет привязать параметры с помощью внедрения зависимостей при настройке типа в качестве службы. Это означает, что явно применять атрибут [FromServices] к параметру не нужно. В следующем коде оба действия возвращают время:

[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
    public ActionResult GetWithAttribute([FromServices] IDateTime dateTime) 
                                                        => Ok(dateTime.Now);

    [Route("noAttribute")]
    public ActionResult Get(IDateTime dateTime) => Ok(dateTime.Now);
}

В редких случаях автоматическое внедрение зависимостей может прервать работу приложения, если оно имеет во внедрении зависимостей тип, который также принимается в методах действий контроллеров API. Обычно тип во внедрении зависимостей и тип, используемый в качестве аргумента в действии контроллера API, не совпадают.

Чтобы отключить вывод [FromServices] для одного параметра действия, примените к параметру требуемый атрибут источника привязки. Например, примените атрибут [FromBody] к параметру действия, который должен быть привязан из текста запроса.

Чтобы отключить вывод [FromServices] глобально, задайте для DisableImplicitFromServicesParameters значение true.

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddSingleton<IDateTime, SystemDateTime>();

builder.Services.Configure<ApiBehaviorOptions>(options =>
{
    options.DisableImplicitFromServicesParameters = true;
});

var app = builder.Build();

app.MapControllers();

app.Run();

Типы проверяются при запуске приложения с помощью IServiceProviderIsService, чтобы определить, поступает ли аргумент в действии контроллера API из внедрения зависимостей или из других источников.

Механизм вывода источника привязки параметров действия контроллера API использует следующие правила:

  • Указанный ранее BindingInfo.BindingSource не перезаписывается.
  • Назначается параметр сложного типа, зарегистрированный в контейнере внедрения зависимостей: BindingSource.Services.
  • Назначается параметр сложного типа, не зарегистрированный в контейнере внедрения зависимостей: BindingSource.Body.
  • Параметру с именем, которое появляется как значение маршрута в любом шаблоне маршрута, присваивается BindingSource.Path.
  • В качестве остальных параметров используется BindingSource.Query.

Отключение правил зависимости

Чтобы отключить вывод источника привязки, задайте для SuppressInferBindingSourcesForParameters значение true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Вывод многокомпонентных запросов и запросов данных форм

Атрибут [ApiController] применяет правило вывода для параметров действия типа IFormFile и IFormFileCollection. Тип контента запроса multipart/form-data выводится для этих типов.

Чтобы отключить поведение по умолчанию, задайте свойству SuppressConsumesConstraintForFormFileParameters значение true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Сведения о проблемах для кодов состояния ошибки

MVC преобразовывает код ошибки (код состояния 400 или выше) в результат с ProblemDetails. Тип ProblemDetails основан на спецификации RFC 7807 и предоставляет считываемые компьютером сведения об ошибке в HTTP-ответе.

Рассмотрим следующий код в действии контроллера:

if (pet == null)
{
    return NotFound();
}

Метод NotFound создает код состояния HTTP 404 с текстом ProblemDetails. Например:

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Отключение ответа ProblemDetails

Отключить автоматическое создание ProblemDetails для кодов состояния ошибок можно, задав свойству SuppressMapClientErrors значение true. Добавьте следующий код:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Определение поддерживаемых типов содержимого запросов с помощью атрибута [Consumes]

По умолчанию действие поддерживает все доступные типы содержимого запросов. Например, если в приложении включена поддержка форматировщиков входных данных JSON и XML, действие будет поддерживать несколько типов содержимого, включая application/json и application/xml.

Атрибут [Consumes] позволяет выполнять действие для ограничения поддерживаемых типов содержимого запросов. Примените атрибут [Consumes] к действию или контроллеру, указав один или несколько типов содержимого:

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

В приведенном выше коде действие CreateProduct указывает тип содержимого application/xml. Запросы, направляемые в это действие, должны определять заголовок Content-Type application/xml. Запросы, не определяющие заголовок Content-Type application/xml, возвращают ответ 415 Unsupported Media Type (неподдерживаемый тип данных).

Атрибут [Consumes] также позволяет действию влиять на выбор с учетом типа содержимого входящего запроса, применяя ограничение типа. Рассмотрим следующий пример:

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

В приведенном выше коде для ConsumesController включена поддержка обработки запросов, отправляемых на URL-адрес https://localhost:5001/api/Consumes. Оба действия контроллера (PostJson и PostForm) обрабатывают запросы POST с одним и тем же URL-адресом. Если в атрибуте [Consumes] не применяется ограничение типа, возникает исключение неоднозначного соответствия.

Атрибут [Consumes] применяется к обоим действиям. Действие PostJson обрабатывает запросы, отправленные с заголовком Content-Type application/json. Действие PostForm обрабатывает запросы, отправленные с заголовком Content-Type application/x-www-form-urlencoded.

Дополнительные ресурсы

ASP.NET Core поддерживает создание веб-API с помощью контроллеров или минимальных API. В веб-API контроллеры — это классы, производные от ControllerBase. В этой статье показано, как использовать контроллеры для обработки веб-запросов API. Сведения о создании веб-API без контроллеров см. в руководстве по созданию минимального API с помощью ASP.NET Core.

Класс ControllerBase

Веб-API на основе контроллера состоит из одного или нескольких классов контроллера, которые являются производными от ControllerBase. Шаблон проекта веб-API предоставляет начальный контроллер:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Контроллеры веб-API обычно являются производными от ControllerBase, а не от Controller. Controller является производным от ControllerBase и добавляет поддержку для представлений, обеспечивая обработку веб-страниц, а не запросов веб-API. Если тот же контроллер должен поддерживать представления и веб-API, происходит наследование от Controller.

Класс ControllerBase предоставляет множество свойств и методов, которые удобны для обработки HTTP-запросов. Например, CreatedAtAction возвращает код состояния 201.

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

В следующей таблице приведены примеры методов в ControllerBase.

Способ Примечания.
BadRequest Возвращает код состояния 400.
NotFound Возвращает код состояния 404.
PhysicalFile Возвращает файл.
TryUpdateModelAsync Вызывает привязку модели.
TryValidateModel Вызывает проверку модели.

Список всех доступных методов и свойств см. здесь: ControllerBase.

Атрибуты

Пространство имен Microsoft.AspNetCore.Mvc предоставляет атрибуты, которые можно использовать для настройки поведения контроллеров и методов действия веб-API. В следующем примере атрибуты используются для указания поддерживаемой команды действия HTTP и всех известных кодов состояния HTTP, которые могут быть возвращены:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Вот еще примеры доступных атрибутов.

Атрибут Примечания.
[Route] Определяет шаблон URL-адреса для контроллера или действия.
[Bind] Задает префикс и свойства, которые добавляются для привязки модели.
[HttpGet] Определяет действие, которое поддерживает команду действия HTTP GET.
[Consumes] Указывает типы данных, которые принимает действие.
[Produces] Указывает типы данных, которые возвращает действие.

Список доступных атрибутов см. в пространстве имен Microsoft.AspNetCore.Mvc.

Атрибут ApiController

Атрибут [ApiController] можно применить к классу контроллера для включения следующих специализированных схем поведения API:

Атрибут в определенных контроллерах

Атрибут [ApiController] может применяться к определенным контроллерам, как показано в следующем примере из шаблона проекта:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Атрибут в нескольких контроллерах

Один из подходов к использованию атрибута на более чем одном контроллере заключается в создании пользовательского базового класса контроллера, аннотированного атрибутом [ApiController]. В следующем примере демонстрируется пользовательский базовый класс и производный от него контроллер:

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("[controller]")]
public class PetsController : MyControllerBase

Атрибут в сборке

Атрибут [ApiController] может быть применен к сборке. При применении атрибута [ApiController] к сборке атрибут [ApiController] применяется ко всем контроллерам в сборке. Его нельзя отменить для отдельных контроллеров. Примените атрибут уровня сборки к файлу Program.cs:

using Microsoft.AspNetCore.Mvc;
[assembly: ApiController]

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Обязательная маршрутизация атрибутов

Атрибут [ApiController] требует обязательной маршрутизации атрибутов. Например:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Действия недоступны через обычные маршруты, определяемые UseEndpoints, UseMvc или UseMvcWithDefaultRoute.

Автоматические отклики HTTP 400

Благодаря атрибуту [ApiController] ошибки проверки модели автоматически активируют отклик HTTP 400. В результате следующий код ненужен в методе действия:

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

Для выполнения предыдущей проверки ASP.NET Core MVC использует фильтр действий ModelStateInvalidFilter.

Отклик BadRequest по умолчанию

Следующий текст ответа является примером сериализованного типа:

{
  "": [
    "A non-empty request body is required."
  ]
}

Для ответов HTTP 400 по умолчанию возвращается тип отклика ValidationProblemDetails. Следующий текст ответа является примером сериализованного типа:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

Тип ValidationProblemDetails:

  • предоставляет распознаваемый компьютером формат для указания ошибок в откликах веб-API;
  • соответствует спецификации RFC 7807.

Чтобы обеспечить согласованность автоматических и настраиваемых ответов, вызовите метод ValidationProblem, а не BadRequest. ValidationProblem возвращает объект ValidationProblemDetails, а также автоматический ответ.

Запись в журнал автоматических откликов HTTP 400

Чтобы регистрировать автоматические ответы 400, задайте свойство делегата InvalidModelStateResponseFactory для выполнения пользовательской обработки. По умолчанию InvalidModelStateResponseFactory использует ProblemDetailsFactory для создания экземпляра ValidationProblemDetails.

В следующем примере показано, как получить экземпляр ILogger<TCategoryName> для регистрации сведений об автоматическом ответе 400:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
      // To preserve the default behavior, capture the original delegate to call later.
        var builtInFactory = options.InvalidModelStateResponseFactory;

        options.InvalidModelStateResponseFactory = context =>
        {
            var logger = context.HttpContext.RequestServices
                                .GetRequiredService<ILogger<Program>>();

            // Perform logging here.
            // ...

            // Invoke the default behavior, which produces a ValidationProblemDetails
            // response.
            // To produce a custom response, return a different implementation of 
            // IActionResult instead.
            return builtInFactory(context);
        };
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Отключение автоматической активации отклика HTTP 400

Чтобы отключить автоматическую активацию отклика HTTP 400, задайте свойству SuppressModelStateInvalidFilter значение true. Добавьте выделенный ниже код:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Вывод параметров источника привязки

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

Атрибут Источник привязки
[FromBody] Текст запроса
[FromForm] Данные формы в тексте запроса
[FromHeader] Заголовок запроса
[FromQuery] Параметры строки запроса для запроса
[FromRoute] Данные маршрута из текущего запроса
[FromServices] Служба запросов, внедренная в качестве параметра действия

Предупреждение

Не используйте [FromRoute], если значения могут содержать %2f (то есть /). Для %2f не будет применяться отмена экранирования /. Используйте [FromQuery], если значение может содержать %2f.

Без атрибута [ApiController] или атрибутов источника привязки, таких как [FromQuery], среда выполнения ASP.NET Core попытается использовать связыватель модели для составного объекта. Связыватель модели для составного объекта извлекает данные из поставщиков значений в определенном порядке.

В следующем примере атрибут [FromQuery] указывает, что значение параметра discontinuedOnly задано в строке запроса URL-адреса для запроса:

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

Атрибут [ApiController] применяет правила зависимости к источникам данных по умолчанию для параметров действий. Эти правила избавляют от необходимости вручную определять источники привязки путем применения атрибутов к параметрам действий. Правила зависимости источника привязки работают следующим образом:

  • [FromBody] выводится для параметров сложного типа, не зарегистрированных в контейнере DI. Исключением из правила зависимости [FromBody] является любой сложный встроенный тип со специальным значением, такой как IFormCollection и CancellationToken. Код определения источника привязки игнорирует эти особые типы.
  • [FromForm] выводится для параметров действия с типом IFormFile и IFormFileCollection. Он не выводится ни для каких простых или определяемых пользователем типов.
  • [FromRoute] выводится для любого имени параметра действия, соответствующего параметру в шаблоне маршрута. Если параметру действия соответствуют несколько маршрутов, любое значение маршрута рассматривается как [FromRoute].
  • [FromQuery] выводится для любых других параметров действия.

Заметки о выводе FromBody

[FromBody] не определен для простых типов, таких как string или int. Таким образом, атрибут [FromBody] должен использоваться для простых типов, когда требуются эти функции.

Если у действия более одного параметра для привязки из текста запроса, выдается исключение. Например, все следующие сигнатуры метода действия вызывают исключение:

  • [FromBody] выводится для обеих параметров, так как они являются сложными типами.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • Атрибут [FromBody], заданный одному параметру, выводится для другого, так как это сложный тип.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • Атрибут [FromBody] выводится для обоих параметров.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Отключение правил зависимости

Чтобы отключить вывод источника привязки, задайте для SuppressInferBindingSourcesForParameters значение true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Вывод многокомпонентных запросов и запросов данных форм

Атрибут [ApiController] применяет правило вывода для параметров действия типа IFormFile и IFormFileCollection. Тип контента запроса multipart/form-data выводится для этих типов.

Чтобы отключить поведение по умолчанию, задайте свойству SuppressConsumesConstraintForFormFileParameters значение true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Сведения о проблемах для кодов состояния ошибки

MVC преобразовывает код ошибки (код состояния 400 или выше) в результат с ProblemDetails. Тип ProblemDetails основан на спецификации RFC 7807 и предоставляет считываемые компьютером сведения об ошибке в HTTP-ответе.

Рассмотрим следующий код в действии контроллера:

if (pet == null)
{
    return NotFound();
}

Метод NotFound создает код состояния HTTP 404 с текстом ProblemDetails. Например:

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Отключение ответа ProblemDetails

Отключить автоматическое создание ProblemDetails для кодов состояния ошибок можно, задав свойству SuppressMapClientErrors значение true:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Определение поддерживаемых типов содержимого запросов с помощью атрибута [Consumes]

По умолчанию действие поддерживает все доступные типы содержимого запросов. Например, если в приложении включена поддержка форматировщиков входных данных JSON и XML, действие будет поддерживать несколько типов содержимого, включая application/json и application/xml.

Атрибут [Consumes] позволяет выполнять действие для ограничения поддерживаемых типов содержимого запросов. Примените атрибут [Consumes] к действию или контроллеру, указав один или несколько типов содержимого:

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

В приведенном выше коде действие CreateProduct указывает тип содержимого application/xml. Запросы, направляемые в это действие, должны определять заголовок Content-Type application/xml. Запросы, не определяющие заголовок Content-Type application/xml, возвращают ответ 415 Unsupported Media Type (неподдерживаемый тип данных).

Атрибут [Consumes] также позволяет действию влиять на выбор с учетом типа содержимого входящего запроса, применяя ограничение типа. Рассмотрим следующий пример:

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

В приведенном выше коде для ConsumesController включена поддержка обработки запросов, отправляемых на URL-адрес https://localhost:5001/api/Consumes. Оба действия контроллера (PostJson и PostForm) обрабатывают запросы POST с одним и тем же URL-адресом. Если в атрибуте [Consumes] не применяется ограничение типа, возникает исключение неоднозначного соответствия.

Атрибут [Consumes] применяется к обоим действиям. Действие PostJson обрабатывает запросы, отправленные с заголовком Content-Type application/json. Действие PostForm обрабатывает запросы, отправленные с заголовком Content-Type application/x-www-form-urlencoded.

Дополнительные ресурсы

ASP.NET Core поддерживает создание служб RESTful, также известных как веб-API, с помощью C#. Для обработки запросов веб-API использует контроллеры. В веб-API контроллеры — это классы, производные от ControllerBase. В этой статье показано, как использовать контроллеры для обработки веб-запросов API.

Просмотреть или скачать образец кода. (Инструкция по скачиванию.)

Класс ControllerBase

Веб-API состоит из одного или нескольких классов контроллера, которые являются производными от ControllerBase. Шаблон проекта веб-API предоставляет начальный контроллер:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Не создавайте контроллер веб-API путем наследования от класса Controller. Controller является производным от ControllerBase и добавляет поддержку для представлений, обеспечивая обработку веб-страниц, а не запросов веб-API. Существует одно исключение из этого правила: если вы планируете использовать один и тот же контроллер для представлений и веб-API, сделайте его производным от Controller.

Класс ControllerBase предоставляет множество свойств и методов, которые удобны для обработки HTTP-запросов. Например, ControllerBase.CreatedAtAction возвращает код состояния 201.

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Вот еще примеры методов, предоставляющих ControllerBase.

Способ Примечания.
BadRequest Возвращает код состояния 400.
NotFound Возвращает код состояния 404.
PhysicalFile Возвращает файл.
TryUpdateModelAsync Вызывает привязку модели.
TryValidateModel Вызывает проверку модели.

Список всех доступных методов и свойств см. здесь: ControllerBase.

Атрибуты

Пространство имен Microsoft.AspNetCore.Mvc предоставляет атрибуты, которые можно использовать для настройки поведения контроллеров и методов действия веб-API. В следующем примере атрибуты используются для указания поддерживаемой команды действия HTTP и всех известных кодов состояния HTTP, которые могут быть возвращены:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Вот еще примеры доступных атрибутов.

Атрибут Примечания.
[Route] Определяет шаблон URL-адреса для контроллера или действия.
[Bind] Задает префикс и свойства, которые добавляются для привязки модели.
[HttpGet] Определяет действие, которое поддерживает команду действия HTTP GET.
[Consumes] Указывает типы данных, которые принимает действие.
[Produces] Указывает типы данных, которые возвращает действие.

Список доступных атрибутов см. в пространстве имен Microsoft.AspNetCore.Mvc.

Атрибут ApiController

Атрибут [ApiController] можно применить к классу контроллера для включения следующих специализированных схем поведения API:

Атрибут в определенных контроллерах

Атрибут [ApiController] может применяться к определенным контроллерам, как показано в следующем примере из шаблона проекта:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Атрибут в нескольких контроллерах

Один из подходов к использованию атрибута на более чем одном контроллере заключается в создании пользовательского базового класса контроллера, аннотированного атрибутом [ApiController]. В следующем примере демонстрируется пользовательский базовый класс и производный от него контроллер:

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("[controller]")]
public class PetsController : MyControllerBase

Атрибут в сборке

Атрибут [ApiController] может быть применен к сборке. Аннотирование этим способом применяет поведение веб-API ко всем контроллерам в сборке. Его нельзя отменить для отдельных контроллеров. Примените атрибут уровня сборки к объявлению пространства имен, окружающему класс Startup:

[assembly: ApiController]
namespace WebApiSample
{
    public class Startup
    {
        ...
    }
}

Обязательная маршрутизация атрибутов

Атрибут [ApiController] требует обязательной маршрутизации атрибутов. Например:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase

Действия недоступны через обычные маршруты, определяемые UseEndpoints, UseMvc или UseMvcWithDefaultRoute в Startup.Configure.

Автоматические отклики HTTP 400

Благодаря атрибуту [ApiController] ошибки проверки модели автоматически активируют отклик HTTP 400. В результате следующий код ненужен в методе действия:

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

Для выполнения предыдущей проверки ASP.NET Core MVC использует фильтр действий ModelStateInvalidFilter.

Отклик BadRequest по умолчанию

Следующий текст запроса является примером сериализованного типа:

{
  "": [
    "A non-empty request body is required."
  ]
}

Для ответов HTTP 400 по умолчанию возвращается тип отклика ValidationProblemDetails. Следующий текст запроса является примером сериализованного типа:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

Тип ValidationProblemDetails:

  • предоставляет распознаваемый компьютером формат для указания ошибок в откликах веб-API;
  • соответствует спецификации RFC 7807.

Чтобы обеспечить согласованность автоматических и настраиваемых ответов, вызовите метод ValidationProblem, а не BadRequest. ValidationProblem возвращает объект ValidationProblemDetails, а также автоматический ответ.

Запись в журнал автоматических откликов HTTP 400

Чтобы регистрировать автоматические ответы 400, задайте свойство делегата InvalidModelStateResponseFactory для выполнения пользовательской обработки в Startup.ConfigureServices. По умолчанию InvalidModelStateResponseFactory использует ProblemDetailsFactory для создания экземпляра ValidationProblemDetails.

В следующем примере показано, как получить экземпляр ILogger<TCategoryName> для регистрации сведений об автоматическом ответе 400:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        // To preserve the default behavior, capture the original delegate to call later.
        var builtInFactory = options.InvalidModelStateResponseFactory;

        options.InvalidModelStateResponseFactory = context =>
        {
            var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<Startup>>();

            // Perform logging here.
            // ...

            // Invoke the default behavior, which produces a ValidationProblemDetails response.
            // To produce a custom response, return a different implementation of IActionResult instead.
            return builtInFactory(context);
        };
    });

Отключение автоматической активации отклика HTTP 400

Чтобы отключить автоматическую активацию отклика HTTP 400, задайте свойству SuppressModelStateInvalidFilter значение true. Добавьте выделенный ниже код в Startup.ConfigureServices:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Вывод параметров источника привязки

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

Атрибут Источник привязки
[FromBody] Текст запроса
[FromForm] Данные формы в тексте запроса
[FromHeader] Заголовок запроса
[FromQuery] Параметры строки запроса для запроса
[FromRoute] Данные маршрута из текущего запроса
[FromServices] Служба запросов, внедренная в качестве параметра действия

Предупреждение

Не используйте [FromRoute], если значения могут содержать %2f (то есть /). Для %2f не будет применяться отмена экранирования /. Используйте [FromQuery], если значение может содержать %2f.

Без атрибута [ApiController] или атрибутов источника привязки, таких как [FromQuery], среда выполнения ASP.NET Core попытается использовать связыватель модели для составного объекта. Связыватель модели для составного объекта извлекает данные из поставщиков значений в определенном порядке.

В следующем примере атрибут [FromQuery] указывает, что значение параметра discontinuedOnly задано в строке запроса URL-адреса для запроса:

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

Атрибут [ApiController] применяет правила зависимости к источникам данных по умолчанию для параметров действий. Эти правила избавляют от необходимости вручную определять источники привязки путем применения атрибутов к параметрам действий. Правила зависимости источника привязки работают следующим образом:

  • [FromBody] выводится для параметров сложного типа. Исключением из правила зависимости [FromBody] является любой сложный встроенный тип со специальным значением, такой как IFormCollection и CancellationToken. Код определения источника привязки игнорирует эти особые типы.
  • [FromForm] выводится для параметров действия с типом IFormFile и IFormFileCollection. Он не выводится ни для каких простых или определяемых пользователем типов.
  • [FromRoute] выводится для любого имени параметра действия, соответствующего параметру в шаблоне маршрута. Если параметру действия соответствуют несколько маршрутов, любое значение маршрута рассматривается как [FromRoute].
  • [FromQuery] выводится для любых других параметров действия.

Заметки о выводе FromBody

[FromBody] не определен для простых типов, таких как string или int. Таким образом, атрибут [FromBody] должен использоваться для простых типов, когда требуются эти функции.

Если у действия более одного параметра для привязки из текста запроса, выдается исключение. Например, все следующие сигнатуры метода действия вызывают исключение:

  • [FromBody] выводится для обеих параметров, так как они являются сложными типами.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • Атрибут [FromBody], заданный одному параметру, выводится для другого, так как это сложный тип.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • Атрибут [FromBody] выводится для обоих параметров.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Отключение правил зависимости

Чтобы отключить вывод источника привязки, задайте SuppressInferBindingSourcesForParameters значение true. Добавьте следующий код в Startup.ConfigureServices:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Вывод многокомпонентных запросов и запросов данных форм

Атрибут [ApiController] применяет правило вывода для параметров действия типа IFormFile и IFormFileCollection. Тип контента запроса multipart/form-data выводится для этих типов.

Чтобы отключить поведение по умолчанию, задайте свойству SuppressConsumesConstraintForFormFileParameters значение true в Startup.ConfigureServices:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Сведения о проблемах для кодов состояния ошибки

MVC преобразовывает код ошибки (код состояния 400 или выше) в результат с ProblemDetails. Тип ProblemDetails основан на спецификации RFC 7807 и предоставляет считываемые компьютером сведения об ошибке в HTTP-ответе.

Рассмотрим следующий код в действии контроллера:

if (pet == null)
{
    return NotFound();
}

Метод NotFound создает код состояния HTTP 404 с текстом ProblemDetails. Например:

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Отключение ответа ProblemDetails

Отключить автоматическое создание ProblemDetails для кодов состояния ошибок можно, задав свойству SuppressMapClientErrors значение true. Добавьте следующий код в Startup.ConfigureServices:

services.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
        options.DisableImplicitFromServicesParameters = true;
    });

Определение поддерживаемых типов содержимого запросов с помощью атрибута [Consumes]

По умолчанию действие поддерживает все доступные типы содержимого запросов. Например, если в приложении включена поддержка форматировщиков входных данных JSON и XML, действие будет поддерживать несколько типов содержимого, включая application/json и application/xml.

Атрибут [Consumes] позволяет выполнять действие для ограничения поддерживаемых типов содержимого запросов. Примените атрибут [Consumes] к действию или контроллеру, указав один или несколько типов содержимого:

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

В приведенном выше коде действие CreateProduct указывает тип содержимого application/xml. Запросы, направляемые в это действие, должны определять заголовок Content-Type application/xml. Запросы, не определяющие заголовок Content-Type application/xml, возвращают ответ 415 Unsupported Media Type (неподдерживаемый тип данных).

Атрибут [Consumes] также позволяет действию влиять на выбор с учетом типа содержимого входящего запроса, применяя ограничение типа. Рассмотрим следующий пример:

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

В приведенном выше коде для ConsumesController включена поддержка обработки запросов, отправляемых на URL-адрес https://localhost:5001/api/Consumes. Оба действия контроллера (PostJson и PostForm) обрабатывают запросы POST с одним и тем же URL-адресом. Если в атрибуте [Consumes] не применяется ограничение типа, возникает исключение неоднозначного соответствия.

Атрибут [Consumes] применяется к обоим действиям. Действие PostJson обрабатывает запросы, отправленные с заголовком Content-Type application/json. Действие PostForm обрабатывает запросы, отправленные с заголовком Content-Type application/x-www-form-urlencoded.

Дополнительные ресурсы

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase

Не создавайте контроллер веб-API путем наследования от класса Controller. Controller является производным от ControllerBase и добавляет поддержку для представлений, обеспечивая обработку веб-страниц, а не запросов веб-API. Существует одно исключение из этого правила: если вы планируете использовать один и тот же контроллер для представлений и веб-API, сделайте его производным от Controller. Класс ControllerBase предоставляет множество свойств и методов, которые удобны для обработки HTTP-запросов. Например, ControllerBase.CreatedAtAction возвращает код состояния 201.

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Вот еще примеры методов, предоставляющих ControllerBase:

Способ Примечания.
BadRequest Возвращает код состояния 400.
NotFound Возвращает код состояния 404.
PhysicalFile Возвращает файл.
TryUpdateModelAsync Вызывает привязку модели.
TryValidateModel Вызывает проверку модели.

Список всех доступных методов и свойств см. здесь: ControllerBase.

Атрибуты

Пространство имен Microsoft.AspNetCore.Mvc предоставляет атрибуты, которые можно использовать для настройки поведения контроллеров и методов действия веб-API. В следующем примере атрибуты используются для указания поддерживаемой команды действия HTTP и всех известных кодов состояния HTTP, которые могут быть возвращены:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Pet> Create(Pet pet)
{
    pet.Id = _petsInMemoryStore.Any() ? 
             _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
    _petsInMemoryStore.Add(pet);

    return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
}

Вот еще примеры доступных атрибутов:

Атрибут Примечания.
[Route] Определяет шаблон URL-адреса для контроллера или действия.
[Bind] Задает префикс и свойства, которые добавляются для привязки модели.
[HttpGet] Определяет действие, которое поддерживает команду действия HTTP GET.
[Consumes] Указывает типы данных, которые принимает действие.
[Produces] Указывает типы данных, которые возвращает действие.

Список доступных атрибутов см. в пространстве имен Microsoft.AspNetCore.Mvc.

Атрибут ApiController

Атрибут [ApiController] можно применить к классу контроллера для включения следующих специализированных схем поведения API:

Атрибут в определенных контроллерах

Атрибут [ApiController] может применяться к определенным контроллерам, как показано в следующем примере из шаблона проекта:

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase

Атрибут в нескольких контроллерах

Один из подходов к использованию атрибута на более чем одном контроллере заключается в создании пользовательского базового класса контроллера, аннотированного атрибутом [ApiController]. В следующем примере демонстрируется пользовательский базовый класс и производный от него контроллер:

[ApiController]
public class MyControllerBase : ControllerBase
{
}
[Produces(MediaTypeNames.Application.Json)]
[Route("api/[controller]")]
public class PetsController : MyControllerBase

Атрибут в сборке

Если задана версия совместимости 2.2 или последующая, атрибут [ApiController] можно применить к сборке. Аннотирование этим способом применяет поведение веб-API ко всем контроллерам в сборке. Его нельзя отменить для отдельных контроллеров. Примените атрибут уровня сборки к объявлению пространства имен, окружающему класс Startup:

[assembly: ApiController]
namespace WebApiSample
{
    public class Startup
    {
        ...
    }
}

Обязательная маршрутизация атрибутов

Атрибут [ApiController] требует обязательной маршрутизации атрибутов. Например:

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase

Действия недоступны через обычные маршруты, определяемые UseMvc или UseMvcWithDefaultRoute в Startup.Configure.

Автоматические отклики HTTP 400

Благодаря атрибуту [ApiController] ошибки проверки модели автоматически активируют отклик HTTP 400. В результате следующий код ненужен в методе действия:

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

Для выполнения предыдущей проверки ASP.NET Core MVC использует фильтр действий ModelStateInvalidFilter.

Отклик BadRequest по умолчанию

Если задана версия совместимости 2.1, для ответов HTTP 400 по умолчанию возвращается тип отклика SerializableError. Следующий текст запроса является примером сериализованного типа:

{
  "": [
    "A non-empty request body is required."
  ]
}

Если задана версия совместимости 2.2 или более поздние версии, для ответов HTTP 400 по умолчанию возвращается тип отклика ValidationProblemDetails. Следующий текст запроса является примером сериализованного типа:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|7fb5e16a-4c8f23bbfc974667.",
  "errors": {
    "": [
      "A non-empty request body is required."
    ]
  }
}

Тип ValidationProblemDetails:

  • предоставляет распознаваемый компьютером формат для указания ошибок в откликах веб-API;
  • соответствует спецификации RFC 7807.

Чтобы обеспечить согласованность автоматических и настраиваемых ответов, вызовите метод ValidationProblem, а не BadRequest. ValidationProblem возвращает объект ValidationProblemDetails, а также автоматический ответ.

Запись в журнал автоматических откликов HTTP 400

См. описание проблемы записи в журнал автоматических ответов HTTP 400 для ошибок проверки модели (aspnet/AspNetCore.Docs#12157).

Отключение автоматической активации отклика HTTP 400

Чтобы отключить автоматическую активацию отклика HTTP 400, задайте свойству SuppressModelStateInvalidFilter значение true. Добавьте выделенный ниже код в Startup.ConfigureServices:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

Вывод параметров источника привязки

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

Атрибут Источник привязки
[FromBody] Текст запроса
[FromForm] Данные формы в тексте запроса
[FromHeader] Заголовок запроса
[FromQuery] Параметры строки запроса для запроса
[FromRoute] Данные маршрута из текущего запроса
[FromServices] Служба запросов, внедренная в качестве параметра действия

Предупреждение

Не используйте [FromRoute], если значения могут содержать %2f (то есть /). Для %2f не будет применяться отмена экранирования /. Используйте [FromQuery], если значение может содержать %2f. Без атрибута [ApiController] или атрибутов источника привязки, таких как [FromQuery], среда выполнения ASP.NET Core попытается использовать связыватель модели для составного объекта. Связыватель модели для составного объекта извлекает данные из поставщиков значений в определенном порядке.

В следующем примере атрибут [FromQuery] указывает, что значение параметра discontinuedOnly задано в строке запроса URL-адреса для запроса:

[HttpGet]
public ActionResult<List<Product>> Get(
    [FromQuery] bool discontinuedOnly = false)
{
    List<Product> products = null;

    if (discontinuedOnly)
    {
        products = _productsInMemoryStore.Where(p => p.IsDiscontinued).ToList();
    }
    else
    {
        products = _productsInMemoryStore;
    }

    return products;
}

Атрибут [ApiController] применяет правила зависимости к источникам данных по умолчанию для параметров действий. Эти правила избавляют от необходимости вручную определять источники привязки путем применения атрибутов к параметрам действий. Правила зависимости источника привязки работают следующим образом:

  • [FromBody] выводится для параметров сложного типа. Исключением из правила зависимости [FromBody] является любой сложный встроенный тип со специальным значением, такой как IFormCollection и CancellationToken. Код определения источника привязки игнорирует эти особые типы.
  • [FromForm] выводится для параметров действия с типом IFormFile и IFormFileCollection. Он не выводится ни для каких простых или определяемых пользователем типов.
  • [FromRoute] выводится для любого имени параметра действия, соответствующего параметру в шаблоне маршрута. Если параметру действия соответствуют несколько маршрутов, любое значение маршрута рассматривается как [FromRoute].
  • [FromQuery] выводится для любых других параметров действия.

Заметки о выводе FromBody

[FromBody] не определен для простых типов, таких как string или int. Таким образом, атрибут [FromBody] должен использоваться для простых типов, когда требуются эти функции.

Если у действия более одного параметра для привязки из текста запроса, выдается исключение. Например, все следующие сигнатуры метода действия вызывают исключение:

  • [FromBody] выводится для обеих параметров, так как они являются сложными типами.

    [HttpPost]
    public IActionResult Action1(Product product, Order order)
    
  • Атрибут [FromBody], заданный одному параметру, выводится для другого, так как это сложный тип.

    [HttpPost]
    public IActionResult Action2(Product product, [FromBody] Order order)
    
  • Атрибут [FromBody] выводится для обоих параметров.

    [HttpPost]
    public IActionResult Action3([FromBody] Product product, [FromBody] Order order)
    

Примечание.

В ASP.NET Core 2.1 параметры типа коллекции, такие как списки и массивы, ошибочно выводятся как [FromQuery]. Для этих параметров следует использовать атрибут [FromBody], если они должны быть привязаны из текста запроса. Это поведение, при котором параметры типа коллекции выводятся для привязки из текста по умолчанию, исправлено в версии ASP.NET Core, начиная с 2.2.

Отключение правил зависимости

Чтобы отключить вывод источника привязки, задайте SuppressInferBindingSourcesForParameters значение true. Добавьте следующий код в Startup.ConfigureServices:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

Вывод многокомпонентных запросов и запросов данных форм

Атрибут [ApiController] применяет правило вывода для параметров действия типа IFormFile и IFormFileCollection. Тип контента запроса multipart/form-data выводится для этих типов. Чтобы отключить поведение по умолчанию, задайте свойству SuppressConsumesConstraintForFormFileParameters значение true в Startup.ConfigureServices:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

Сведения о проблемах для кодов состояния ошибки

Если задана версия совместимости, начиная с 2.2, MVC преобразовывает код ошибки (код состояния 400 и далее) в результат с ProblemDetails. Тип ProblemDetails основан на спецификации RFC 7807 и предоставляет считываемые компьютером сведения об ошибке в HTTP-ответе. Рассмотрим следующий код в действии контроллера:

if (pet == null)
{
    return NotFound();
}

Метод NotFound создает код состояния HTTP 404 с текстом ProblemDetails. Например:

{
  type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  title: "Not Found",
  status: 404,
  traceId: "0HLHLV31KRN83:00000001"
}

Отключение ответа ProblemDetails

Отключить автоматическое создание ProblemDetails для кодов состояния ошибок можно, задав свойству SuppressMapClientErrors значение true. Добавьте следующий код в Startup.ConfigureServices:

Определение поддерживаемых типов содержимого запросов с помощью атрибута [Consumes]

По умолчанию действие поддерживает все доступные типы содержимого запросов. Например, если в приложении включена поддержка форматировщиков входных данных JSON и XML, действие будет поддерживать несколько типов содержимого, включая application/json и application/xml.

Атрибут [Consumes] позволяет выполнять действие для ограничения поддерживаемых типов содержимого запросов. Примените атрибут [Consumes] к действию или контроллеру, указав один или несколько типов содержимого:

[HttpPost]
[Consumes("application/xml")]
public IActionResult CreateProduct(Product product)

В приведенном выше коде действие CreateProduct указывает тип содержимого application/xml. Запросы, направляемые в это действие, должны определять заголовок Content-Type application/xml. Запросы, не определяющие заголовок Content-Type application/xml, возвращают ответ 415 Unsupported Media Type (неподдерживаемый тип данных). Атрибут [Consumes] также позволяет действию влиять на выбор с учетом типа содержимого входящего запроса, применяя ограничение типа. Рассмотрим следующий пример:

[ApiController]
[Route("api/[controller]")]
public class ConsumesController : ControllerBase
{
    [HttpPost]
    [Consumes("application/json")]
    public IActionResult PostJson(IEnumerable<int> values) =>
        Ok(new { Consumes = "application/json", Values = values });

    [HttpPost]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult PostForm([FromForm] IEnumerable<int> values) =>
        Ok(new { Consumes = "application/x-www-form-urlencoded", Values = values });
}

В приведенном выше коде для ConsumesController включена поддержка обработки запросов, отправляемых на URL-адрес https://localhost:5001/api/Consumes. Оба действия контроллера (PostJson и PostForm) обрабатывают запросы POST с одним и тем же URL-адресом. Если в атрибуте [Consumes] не применяется ограничение типа, возникает исключение неоднозначного соответствия. Атрибут [Consumes] применяется к обоим действиям. Действие PostJson обрабатывает запросы, отправленные с заголовком Content-Type application/json. Действие PostForm обрабатывает запросы, отправленные с заголовком Content-Type application/x-www-form-urlencoded.

Дополнительные ресурсы