Типы возвращаемых действий контроллера в веб-API ASP.NET Core
Примечание.
Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 8 этой статьи.
Предупреждение
Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в статье о политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 8 этой статьи.
Внимание
Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
В текущем выпуске см . версию .NET 8 этой статьи.
Просмотреть или скачать образец кода (описание загрузки)
ASP.NET Core предоставляет следующие параметры для возвращаемых типов действий контроллера веб-API:
В этой статье объясняется, когда наиболее подходящим для использования каждого возвращаемого типа.
Определенный тип
Наиболее простое действие возвращает примитивный или сложный тип данных, например string
пользовательский объект. Рассмотрим следующее действие, которое возвращает коллекцию пользовательских объектов Product
:
[HttpGet]
public Task<List<Product>> Get() =>
_productContext.Products.OrderBy(p => p.Name).ToListAsync();
Без известных условий для защиты от, возвращая конкретный тип, может быть достаточно. Предыдущее действие не принимает параметры, поэтому проверка ограничений параметров не требуется.
Если возможны несколько типов возвращаемых данных, обычно можно смешать ActionResult возвращаемый тип с примитивным или сложным типом возвращаемого значения. Для удовлетворения этого типа действия необходимы либо IActionResult, либо ActionResult<T>. В этой статье приведено несколько примеров нескольких типов возвращаемых данных.
Возврат IEnumerable<T> или IAsyncEnumerable<T>
Ознакомьтесь с рекомендациями по возврату IEnumerable<T>
или IAsyncEnumerable<T>
производительности.
ASP.NET Core буферизирует результат действий, возвращаемых IEnumerable<T> перед записью в ответ. Рассмотрите возможность объявления возвращаемого типа подписи действия, чтобы IAsyncEnumerable<T> гарантировать асинхронную итерацию. В конечном счете режим итерации основан на возвращаемом базовом конкретном типе, а выбранный формататор влияет на обработку результата:
- При использовании
System.Text.Json
средства форматирования MVC использует поддержку,System.Text.Json
добавленную для потоковой передачи результата. - При использовании
Newtonsoft.Json
или сXML-based
форматировщиками результат буферичен.
Рассмотрим следующее действие, которое возвращает записи о продуктах со сниженной ценой как IEnumerable<Product>
:
[HttpGet("syncsale")]
public IEnumerable<Product> GetOnSaleProducts()
{
var products = _productContext.Products.OrderBy(p => p.Name).ToList();
foreach (var product in products)
{
if (product.IsOnSale)
{
yield return product;
}
}
}
Эквивалентом IAsyncEnumerable<Product>
для предшествующего действия является:
[HttpGet("asyncsale")]
public async IAsyncEnumerable<Product> GetOnSaleProductsAsync()
{
var products = _productContext.Products.OrderBy(p => p.Name).AsAsyncEnumerable();
await foreach (var product in products)
{
if (product.IsOnSale)
{
yield return product;
}
}
}
Тип IActionResult
Тип возвращаемого значения IActionResult можно использовать, если в действии допускаются несколько типов возвращаемого значения ActionResult
. Типы ActionResult
представляют различные коды состояния HTTP. Любой неабстрактный класс, унаследованный от квалификаторов ActionResult
в виде допустимого типа возвращаемого значения. Некоторые распространенные типы возвращаемых значений в этой категории: BadRequestResult (400), NotFoundResult (404) и OkObjectResult (200). Кроме того, удобные методы в классе ControllerBase можно использовать для получения типов ActionResult
из действия. Например, return BadRequest();
— это сокращенная форма return new BadRequestResult();
.
Так как в типе действия есть несколько типов возвращаемого значения и путей, необходимо использовать атрибут [ProducesResponseType]
. Этот атрибут создает описательные сведения об ответе для страниц справки по веб-API, создаваемых с помощью таких инструментов, как Swagger. [ProducesResponseType]
указывает известные типы и коды состояния HTTP, возвращаемые действием.
Синхронное действие
Рассмотрим следующее синхронное действие, в котором возможны два типа возвращаемых значений:
[HttpGet("{id}")]
[ProducesResponseType<Product>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetById_IActionResult(int id)
{
var product = _productContext.Products.Find(id);
return product == null ? NotFound() : Ok(product);
}
В предшествующем действии:
- возвращается код состояния 404, если продукт, представленный
id
, не существует в базовом хранилище данных; вызывается удобный метод NotFound в качестве сокращенияreturn new NotFoundResult();
. - Код состояния 200 возвращается с объектом
Product
, если продукт не существует. вызывается удобный метод Ok в качестве сокращенияreturn new OkObjectResult(product);
.
Асинхронное действие
Рассмотрим следующее асинхронное действие, в котором возможны два типа возвращаемых значений:
[HttpPost()]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CreateAsync_IActionResult(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return BadRequest();
}
_productContext.Products.Add(product);
await _productContext.SaveChangesAsync();
return CreatedAtAction(nameof(CreateAsync_IActionResult), new { id = product.Id }, product);
}
В предшествующем действии:
Код состояния 400 возвращается, если описание продукта содержит строку XYZ Widget. вызывается удобный метод BadRequest в качестве сокращения
return new BadRequestResult();
.Код состояния 201 генерируется удобным методом CreatedAtAction при создании продукта. Следующий код является альтернативой вызову
CreatedAtAction
:return new CreatedAtActionResult(nameof(CreateAsync), "Products", new { id = product.Id }, product);
В приведенном выше пути
Product
кода объект предоставляется в тексте ответа. Также предоставляется заголовок ответаLocation
с URL-адресом только что созданного продукта.
Например, следующая модель указывает на то, что запросы должны включать свойства Name
и Description
. Если Name
и Description
не были указаны в запросе, происходит сбой проверки модели.
public class Product
{
public int Id { get; set; }
[Required]
public string Name { get; set; } = string.Empty;
[Required]
public string Description { get; set; } = string.Empty;
public bool IsOnSale { get; set; }
}
[ApiController]
Если атрибут применен, ошибки проверки модели приводят к коду состояния 400. Дополнительные сведения см. в разделе Автоматические отклики HTTP 400.
ActionResult и IActionResult
В следующем разделе сравнивается ActionResult
с IActionResult
Тип ActionResult<T>
ASP.NET Core включает тип возвращаемого значения ActionResult<T> для действий контроллера веб-API. Он позволяет возвращать тип, производный от ActionResult определенного типа или возвращающий определенный тип. ActionResult<T>
имеет следующие преимущества по сравнению с типом IActionResult:
- Свойство
Type
атрибута[ProducesResponseType]
можно исключить. Например,[ProducesResponseType(200, Type = typeof(Product))]
упрощается до[ProducesResponseType(200)]
. Ожидаемый тип возвращаемого значения действия выводится из объектаT
ActionResult<T>
in. - Операторы неявного приведения поддерживают преобразование
T
иActionResult
вActionResult<T>
.T
преобразуется в ObjectResult, то естьreturn new ObjectResult(T);
упрощается доreturn T;
.
C# не поддерживает операторы неявных приведений в интерфейсах. Следовательно, для преобразования в конкретный тип необходимо использовать ActionResult<T>
. Например, использование IEnumerable
не работает в следующем примере:
[HttpGet]
public ActionResult<IEnumerable<Product>> Get() =>
_repository.GetProducts();
Один из способов исправить приведенный выше код — возвратить _repository.GetProducts().ToList();
.
Большинство действий имеют тип возвращаемого значения. При выполнении действия могут возникнуть непредвиденные условия, и в этом случае определенный тип не возвращается. Например, входной параметр действия может не пройти проверку модели. В этом случае обычно возвращается подходящий тип ActionResult
вместо конкретного типа.
Синхронное действие
Рассмотрим синхронное действие, в котором возможны два типа возвращаемых значений:
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetById_ActionResultOfT(int id)
{
var product = _productContext.Products.Find(id);
return product == null ? NotFound() : product;
}
В предшествующем действии:
- возвращается код состояния 404, если продукт не существует в базе данных;
- возвращается код состояния 200 с соответствующим объектом
Product
, если продукт существует.
Асинхронное действие
Рассмотрим асинхронное действие, в котором возможны два типа возвращаемых значений:
[HttpPost()]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<Product>> CreateAsync_ActionResultOfT(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return BadRequest();
}
_productContext.Products.Add(product);
await _productContext.SaveChangesAsync();
return CreatedAtAction(nameof(CreateAsync_ActionResultOfT), new { id = product.Id }, product);
}
В предшествующем действии:
- Код состояния 400 (BadRequest) возвращается средой выполнения ASP.NET Core, когда:
- Атрибут
[ApiController]
был применен, и проверка модели не пройдена. - Описание продукта содержит XYZ Widget.
- Атрибут
- Код состояния 201 генерируется методом CreatedAtAction при создании продукта. В этом пути к коду объект
Product
предоставляется в тексте ответа. Также предоставляется заголовок ответаLocation
с URL-адресом только что созданного продукта.
Тип HttpResults
Помимо встроенных типов результатов MVC (IActionResultи ActionResult<T>), ASP.NET Core включает типы HttpResults, которые можно использовать как в минимальных API, так и в веб-API.
Отличается от типов результатов, относящихся к MVC, :HttpResults
Реализация результатов, обрабатываемая вызовом IResult.ExecuteAsync.
Не использует настроенные форматировщики. Не используется настроенные средства форматирования:
- Некоторые функции, такие как
Content negotiation
недоступны. - Созданное решение
Content-Type
определяется реализациейHttpResults
.
- Некоторые функции, такие как
Это HttpResults
может быть полезно при совместном использовании кода между минимальными API и веб-API.
Тип IResult
Пространство Microsoft.AspNetCore.Http.HttpResults имен содержит классы, реализующие IResult интерфейс. Интерфейс IResult
определяет контракт, представляющий результат конечной точки HTTP. Статический класс Results используется для создания разных объектов IResult
, которые представляют разные типы ответов.
Встроенная таблица результатов показывает распространенные вспомогательные средства результатов.
Рассмотрим следующий код:
[HttpGet("{id}")]
[ProducesResponseType<Product>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IResult GetById(int id)
{
var product = _productContext.Products.Find(id);
return product == null ? Results.NotFound() : Results.Ok(product);
}
В предшествующем действии:
- возвращается код состояния 404, если продукт не существует в базе данных;
- Код состояния 200 возвращается с соответствующим
Product
объектом, когда продукт существует, сформированный results.Ok <T>().
Рассмотрим следующий код:
[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType<Product>(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IResult> CreateAsync(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return Results.BadRequest();
}
_productContext.Products.Add(product);
await _productContext.SaveChangesAsync();
var location = Url.Action(nameof(CreateAsync), new { id = product.Id }) ?? $"/{product.Id}";
return Results.Created(location, product);
}
В предшествующем действии:
- Код состояния 400 возвращается, когда:
- Атрибут
[ApiController]
был применен, и проверка модели не пройдена. - Описание продукта содержит XYZ Widget.
- Атрибут
- Код состояния 201 генерируется методом
Results.Create
при создании продукта. В этом пути к коду объектProduct
предоставляется в тексте ответа. Также предоставляется заголовок ответаLocation
с URL-адресом только что созданного продукта.
Результаты<TResult1, тип TResultN>
Статический класс TypedResults возвращает конкретную IResult
реализацию, которая позволяет использовать IResult
в качестве возвращаемого типа. Использование конкретной IResult
реализации обеспечивает следующее преимущество по сравнению с типом IResult:
[ProducesResponseType]
Все атрибуты можно исключить, так какHttpResult
реализация автоматически вносит свой вклад в метаданные конечной точки.
Если требуется несколько IResult
типов возврата, возврат Results<TResult1, TResultN>
предпочтителен по сравнению с возвратом IResult
. Results<TResult1, TResultN>
Возврат предпочтителен, так как универсальные типы объединения автоматически сохраняют метаданные конечной точки.
Results<TResult1, TResultN>
Типы объединения реализуют неявные операторы приведения, чтобы компилятор может автоматически преобразовать типы, указанные в универсальных аргументах, в экземпляр типа объединения. Это дает дополнительное преимущество при проверке времени компиляции, что обработчик маршрутов фактически возвращает только результаты, объявленные им. Попытка вернуть тип, который не объявлен как один из универсальных аргументов, приводит к Results<>
ошибке компиляции.
Рассмотрим следующий код:
[HttpGet("{id}")]
public Results<NotFound, Ok<Product>> GetById(int id)
{
var product = _productContext.Products.Find(id);
return product == null ? TypedResults.NotFound() : TypedResults.Ok(product);
}
В предшествующем действии:
- возвращается код состояния 404, если продукт не существует в базе данных;
- Код состояния 200 возвращается с соответствующим
Product
объектом при наличии продукта, созданного типом TypedResults.Ok<T>.
[HttpPost]
public async Task<Results<BadRequest, Created<Product>>> CreateAsync(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return TypedResults.BadRequest();
}
_productContext.Products.Add(product);
await _productContext.SaveChangesAsync();
var location = Url.Action(nameof(CreateAsync), new { id = product.Id }) ?? $"/{product.Id}";
return TypedResults.Created(location, product);
}
В предшествующем действии:
- Код состояния 400 возвращается, когда:
- Атрибут
[ApiController]
был применен, а проверка модели завершается ошибкой. - Описание продукта содержит XYZ Widget.
- Атрибут
- Код состояния 201 генерируется методом
TypedResults.Created
при создании продукта. В этом пути к коду объектProduct
предоставляется в тексте ответа. Также предоставляется заголовок ответаLocation
с URL-адресом только что созданного продукта.
Дополнительные ресурсы
Просмотреть или скачать образец кода (описание загрузки)
ASP.NET Core предоставляет следующие параметры для возвращаемых типов действий контроллера веб-API:
В этой статье объясняется, когда наиболее подходящим для использования каждого возвращаемого типа.
Определенный тип
Наиболее простое действие возвращает примитивный или сложный тип данных, например string
пользовательский объект. Рассмотрим следующее действие, которое возвращает коллекцию пользовательских объектов Product
:
[HttpGet]
public Task<List<Product>> Get() =>
_productContext.Products.OrderBy(p => p.Name).ToListAsync();
Без известных условий для защиты от, возвращая конкретный тип, может быть достаточно. Предыдущее действие не принимает параметры, поэтому проверка ограничений параметров не требуется.
Если возможны несколько типов возвращаемых данных, обычно можно смешать ActionResult возвращаемый тип с примитивным или сложным типом возвращаемого значения. Для удовлетворения этого типа действия необходимы либо IActionResult, либо ActionResult<T>. В этой статье приведено несколько примеров нескольких типов возвращаемых данных.
Возврат IEnumerable<T> или IAsyncEnumerable<T>
Ознакомьтесь с рекомендациями по возврату IEnumerable<T>
или IAsyncEnumerable<T>
производительности.
ASP.NET Core буферизирует результат действий, возвращаемых IEnumerable<T> перед записью в ответ. Рассмотрите возможность объявления возвращаемого типа подписи действия, чтобы IAsyncEnumerable<T> гарантировать асинхронную итерацию. В конечном счете режим итерации основан на возвращаемом базовом конкретном типе, а выбранный формататор влияет на обработку результата:
- При использовании
System.Text.Json
средства форматирования MVC использует поддержку,System.Text.Json
добавленную для потоковой передачи результата. - При использовании
Newtonsoft.Json
или сXML-based
форматировщиками результат буферичен.
Рассмотрим следующее действие, которое возвращает записи о продуктах со сниженной ценой как IEnumerable<Product>
:
[HttpGet("syncsale")]
public IEnumerable<Product> GetOnSaleProducts()
{
var products = _productContext.Products.OrderBy(p => p.Name).ToList();
foreach (var product in products)
{
if (product.IsOnSale)
{
yield return product;
}
}
}
Эквивалентом IAsyncEnumerable<Product>
для предшествующего действия является:
[HttpGet("asyncsale")]
public async IAsyncEnumerable<Product> GetOnSaleProductsAsync()
{
var products = _productContext.Products.OrderBy(p => p.Name).AsAsyncEnumerable();
await foreach (var product in products)
{
if (product.IsOnSale)
{
yield return product;
}
}
}
Тип IActionResult
Тип возвращаемого значения IActionResult можно использовать, если в действии допускаются несколько типов возвращаемого значения ActionResult
. Типы ActionResult
представляют различные коды состояния HTTP. Любой неабстрактный класс, унаследованный от квалификаторов ActionResult
в виде допустимого типа возвращаемого значения. Некоторые распространенные типы возвращаемых значений в этой категории: BadRequestResult (400), NotFoundResult (404) и OkObjectResult (200). Кроме того, удобные методы в классе ControllerBase можно использовать для получения типов ActionResult
из действия. Например, return BadRequest();
— это сокращенная форма return new BadRequestResult();
.
Так как в типе действия есть несколько типов возвращаемого значения и путей, необходимо использовать атрибут [ProducesResponseType]
. Этот атрибут создает описательные сведения об ответе для страниц справки по веб-API, создаваемых с помощью таких инструментов, как Swagger. [ProducesResponseType]
указывает известные типы и коды состояния HTTP, возвращаемые действием.
Синхронное действие
Рассмотрим следующее синхронное действие, в котором возможны два типа возвращаемых значений:
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Product))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetById_IActionResult(int id)
{
var product = _productContext.Products.Find(id);
return product == null ? NotFound() : Ok(product);
}
В предшествующем действии:
- возвращается код состояния 404, если продукт, представленный
id
, не существует в базовом хранилище данных; вызывается удобный метод NotFound в качестве сокращенияreturn new NotFoundResult();
. - Код состояния 200 возвращается с объектом
Product
, если продукт не существует. вызывается удобный метод Ok в качестве сокращенияreturn new OkObjectResult(product);
.
Асинхронное действие
Рассмотрим следующее асинхронное действие, в котором возможны два типа возвращаемых значений:
[HttpPost()]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CreateAsync_IActionResult(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return BadRequest();
}
_productContext.Products.Add(product);
await _productContext.SaveChangesAsync();
return CreatedAtAction(nameof(GetById_IActionResult), new { id = product.Id }, product);
}
В предшествующем действии:
Код состояния 400 возвращается, если описание продукта содержит строку XYZ Widget. вызывается удобный метод BadRequest в качестве сокращения
return new BadRequestResult();
.Код состояния 201 генерируется удобным методом CreatedAtAction при создании продукта. Следующий код является альтернативой вызову
CreatedAtAction
:return new CreatedAtActionResult(nameof(GetById), "Products", new { id = product.Id }, product);
В приведенном выше пути
Product
кода объект предоставляется в тексте ответа. Также предоставляется заголовок ответаLocation
с URL-адресом только что созданного продукта.
Например, следующая модель указывает на то, что запросы должны включать свойства Name
и Description
. Если Name
и Description
не были указаны в запросе, происходит сбой проверки модели.
public class Product
{
public int Id { get; set; }
[Required]
public string Name { get; set; } = string.Empty;
[Required]
public string Description { get; set; } = string.Empty;
public bool IsOnSale { get; set; }
}
[ApiController]
Если атрибут применен, ошибки проверки модели приводят к коду состояния 400. Дополнительные сведения см. в разделе Автоматические отклики HTTP 400.
ActionResult и IActionResult
В следующем разделе сравнивается ActionResult
с IActionResult
Тип ActionResult<T>
ASP.NET Core включает тип возвращаемого значения ActionResult<T> для действий контроллера веб-API. Он позволяет возвращать тип, производный от ActionResult определенного типа или возвращающий определенный тип. ActionResult<T>
имеет следующие преимущества по сравнению с типом IActionResult:
- Свойство
Type
атрибута[ProducesResponseType]
можно исключить. Например,[ProducesResponseType(200, Type = typeof(Product))]
упрощается до[ProducesResponseType(200)]
. Ожидаемый тип возвращаемого значения действия выводится из объектаT
ActionResult<T>
in. - Операторы неявного приведения поддерживают преобразование
T
иActionResult
вActionResult<T>
.T
преобразуется в ObjectResult, то естьreturn new ObjectResult(T);
упрощается доreturn T;
.
C# не поддерживает операторы неявных приведений в интерфейсах. Следовательно, для преобразования в конкретный тип необходимо использовать ActionResult<T>
. Например, использование IEnumerable
не работает в следующем примере:
[HttpGet]
public ActionResult<IEnumerable<Product>> Get() =>
_repository.GetProducts();
Один из способов исправить приведенный выше код — возвратить _repository.GetProducts().ToList();
.
Большинство действий имеют тип возвращаемого значения. При выполнении действия могут возникнуть непредвиденные условия, и в этом случае определенный тип не возвращается. Например, входной параметр действия может не пройти проверку модели. В этом случае обычно возвращается подходящий тип ActionResult
вместо конкретного типа.
Синхронное действие
Рассмотрим синхронное действие, в котором возможны два типа возвращаемых значений:
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetById_ActionResultOfT(int id)
{
var product = _productContext.Products.Find(id);
return product == null ? NotFound() : product;
}
В предшествующем действии:
- возвращается код состояния 404, если продукт не существует в базе данных;
- возвращается код состояния 200 с соответствующим объектом
Product
, если продукт существует.
Асинхронное действие
Рассмотрим асинхронное действие, в котором возможны два типа возвращаемых значений:
[HttpPost()]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<Product>> CreateAsync_ActionResultOfT(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return BadRequest();
}
_productContext.Products.Add(product);
await _productContext.SaveChangesAsync();
return CreatedAtAction(nameof(GetById_ActionResultOfT), new { id = product.Id }, product);
}
В предшествующем действии:
- Код состояния 400 (BadRequest) возвращается средой выполнения ASP.NET Core, когда:
- Атрибут
[ApiController]
был применен, и проверка модели не пройдена. - Описание продукта содержит XYZ Widget.
- Атрибут
- Код состояния 201 генерируется методом CreatedAtAction при создании продукта. В этом пути к коду объект
Product
предоставляется в тексте ответа. Также предоставляется заголовок ответаLocation
с URL-адресом только что созданного продукта.
Тип HttpResults
Помимо встроенных типов результатов MVC (IActionResultи ActionResult<T>), ASP.NET Core включает типы HttpResults, которые можно использовать как в минимальных API, так и в веб-API.
Отличается от типов результатов, относящихся к MVC, :HttpResults
Реализация результатов, обрабатываемая вызовом IResult.ExecuteAsync.
Не использует настроенные форматировщики. Не используется настроенные средства форматирования:
- Некоторые функции, такие как
Content negotiation
недоступны. - Созданное решение
Content-Type
определяется реализациейHttpResults
.
- Некоторые функции, такие как
Это HttpResults
может быть полезно при совместном использовании кода между минимальными API и веб-API.
Тип IResult
Пространство Microsoft.AspNetCore.Http.HttpResults имен содержит классы, реализующие IResult интерфейс. Интерфейс IResult
определяет контракт, представляющий результат конечной точки HTTP. Статический класс Results используется для создания разных объектов IResult
, которые представляют разные типы ответов.
Встроенная таблица результатов показывает распространенные вспомогательные средства результатов.
Рассмотрим следующий код:
[HttpGet("{id}")]
[ProducesResponseType(typeof(Product), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IResult GetById(int id)
{
var product = _productContext.Products.Find(id);
return product == null ? Results.NotFound() : Results.Ok(product);
}
В предшествующем действии:
- возвращается код состояния 404, если продукт не существует в базе данных;
- Код состояния 200 возвращается с соответствующим
Product
объектом, когда продукт существует, сформированный results.Ok <T>().
Рассмотрим следующий код:
[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(typeof(Product), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IResult> CreateAsync(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return Results.BadRequest();
}
_productContext.Products.Add(product);
await _productContext.SaveChangesAsync();
var location = Url.Action(nameof(GetById), new { id = product.Id }) ?? $"/{product.Id}";
return Results.Created(location, product);
}
В предшествующем действии:
- Код состояния 400 возвращается, когда:
- Атрибут
[ApiController]
был применен, и проверка модели не пройдена. - Описание продукта содержит XYZ Widget.
- Атрибут
- Код состояния 201 генерируется методом
Results.Create
при создании продукта. В этом пути к коду объектProduct
предоставляется в тексте ответа. Также предоставляется заголовок ответаLocation
с URL-адресом только что созданного продукта.
Результаты<TResult1, тип TResultN>
Статический класс TypedResults возвращает конкретную IResult
реализацию, которая позволяет использовать IResult
в качестве возвращаемого типа. Использование конкретной IResult
реализации обеспечивает следующее преимущество по сравнению с типом IResult:
[ProducesResponseType]
Все атрибуты можно исключить, так какHttpResult
реализация автоматически вносит свой вклад в метаданные конечной точки.
Если требуется несколько IResult
типов возврата, возврат Results<TResult1, TResultN>
предпочтителен по сравнению с возвратом IResult
. Results<TResult1, TResultN>
Возврат предпочтителен, так как универсальные типы объединения автоматически сохраняют метаданные конечной точки.
Results<TResult1, TResultN>
Типы объединения реализуют неявные операторы приведения, чтобы компилятор может автоматически преобразовать типы, указанные в универсальных аргументах, в экземпляр типа объединения. Это дает дополнительное преимущество при проверке времени компиляции, что обработчик маршрутов фактически возвращает только результаты, объявленные им. Попытка вернуть тип, который не объявлен как один из универсальных аргументов, приводит к Results<>
ошибке компиляции.
Рассмотрим следующий код:
[HttpGet("{id}")]
public Results<NotFound, Ok<Product>> GetById(int id)
{
var product = _productContext.Products.Find(id);
return product == null ? TypedResults.NotFound() : TypedResults.Ok(product);
}
В предшествующем действии:
- возвращается код состояния 404, если продукт не существует в базе данных;
- Код состояния 200 возвращается с соответствующим
Product
объектом при наличии продукта, созданного типом TypedResults.Ok<T>.
[HttpPost]
public async Task<Results<BadRequest, Created<Product>>> CreateAsync(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return TypedResults.BadRequest();
}
_productContext.Products.Add(product);
await _productContext.SaveChangesAsync();
var location = Url.Action(nameof(GetById), new { id = product.Id }) ?? $"/{product.Id}";
return TypedResults.Created(location, product);
}
В предшествующем действии:
- Код состояния 400 возвращается, когда:
- Атрибут
[ApiController]
был применен, а проверка модели завершается ошибкой. - Описание продукта содержит XYZ Widget.
- Атрибут
- Код состояния 201 генерируется методом
TypedResults.Create
при создании продукта. В этом пути к коду объектProduct
предоставляется в тексте ответа. Также предоставляется заголовок ответаLocation
с URL-адресом только что созданного продукта.
Дополнительные ресурсы
Просмотреть или скачать образец кода (описание загрузки)
ASP.NET Core предоставляет следующие параметры для типов возвращаемых значений действий контроллера веб-API:
В этом документе объясняется, когда лучше использовать каждый тип возвращаемого значения.
Определенный тип
Простейшее действие возвращает элементарный или сложный тип данных (например, string
или пользовательский тип объекта). Рассмотрим следующее действие, которое возвращает коллекцию пользовательских объектов Product
:
[HttpGet]
public List<Product> Get() =>
_repository.GetProducts();
Если не известны условия, которые необходимо соблюдать при выполнении действия, конкретного типа будет достаточно. Предыдущее действие не принимает параметры, поэтому проверка ограничений параметров не требуется.
Если возможны несколько типов возвращаемых данных, обычно можно смешать ActionResult возвращаемый тип с примитивным или сложным типом возвращаемого значения. Для удовлетворения этого типа действия необходимы либо IActionResult, либо ActionResult<T>. В этом документе представлено несколько примеров нескольких типов возвращаемых данных.
Возврат IEnumerable<T> или IAsyncEnumerable<T>
ASP.NET Core буферизирует результат действий, возвращаемых IEnumerable<T> перед записью в ответ. Рассмотрите возможность объявления возвращаемого типа подписи действия, чтобы IAsyncEnumerable<T> гарантировать асинхронную итерацию. То есть режим итерации зависит от возвращаемого базового конкретного типа. MVC автоматически буферизует все конкретные типы, которые реализуют IAsyncEnumerable<T>
.
Рассмотрим следующее действие, которое возвращает записи о продуктах со сниженной ценой как IEnumerable<Product>
:
[HttpGet("syncsale")]
public IEnumerable<Product> GetOnSaleProducts()
{
var products = _repository.GetProducts();
foreach (var product in products)
{
if (product.IsOnSale)
{
yield return product;
}
}
}
Эквивалентом IAsyncEnumerable<Product>
для предшествующего действия является:
[HttpGet("asyncsale")]
public async IAsyncEnumerable<Product> GetOnSaleProductsAsync()
{
var products = _repository.GetProductsAsync();
await foreach (var product in products)
{
if (product.IsOnSale)
{
yield return product;
}
}
}
Тип IActionResult
Тип возвращаемого значения IActionResult можно использовать, если в действии допускаются несколько типов возвращаемого значения ActionResult
. Типы ActionResult
представляют различные коды состояния HTTP. Любой неабстрактный класс, унаследованный от квалификаторов ActionResult
в виде допустимого типа возвращаемого значения. Некоторые распространенные типы возвращаемых значений в этой категории: BadRequestResult (400), NotFoundResult (404) и OkObjectResult (200). Кроме того, удобные методы в классе ControllerBase можно использовать для получения типов ActionResult
из действия. Например, return BadRequest();
— это сокращенная форма return new BadRequestResult();
.
Так как в типе действия есть несколько типов возвращаемого значения и путей, необходимо использовать атрибут [ProducesResponseType]
. Этот атрибут создает описательные сведения об ответе для страниц справки по веб-API, создаваемых с помощью таких инструментов, как Swagger. [ProducesResponseType]
указывает известные типы и коды состояния HTTP, возвращаемые действием.
Синхронное действие
Рассмотрим следующее синхронное действие, в котором возможны два типа возвращаемых значений:
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Product))]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetById(int id)
{
if (!_repository.TryGetProduct(id, out var product))
{
return NotFound();
}
return Ok(product);
}
В предшествующем действии:
- возвращается код состояния 404, если продукт, представленный
id
, не существует в базовом хранилище данных; вызывается удобный метод NotFound в качестве сокращенияreturn new NotFoundResult();
. - Код состояния 200 возвращается с объектом
Product
, если продукт не существует. вызывается удобный метод Ok в качестве сокращенияreturn new OkObjectResult(product);
.
Асинхронное действие
Рассмотрим следующее асинхронное действие, в котором возможны два типа возвращаемых значений:
[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CreateAsync(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return BadRequest();
}
await _repository.AddProductAsync(product);
return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}
В предшествующем действии:
- Код состояния 400 возвращается, если описание продукта содержит строку XYZ Widget. вызывается удобный метод BadRequest в качестве сокращения
return new BadRequestResult();
. - Код состояния 201 генерируется удобным методом CreatedAtAction при создании продукта. В качестве альтернативы вызову
CreatedAtAction
можно использоватьreturn new CreatedAtActionResult(nameof(GetById), "Products", new { id = product.Id }, product);
. В этом пути к коду объектProduct
предоставляется в тексте ответа. Также предоставляется заголовок ответаLocation
с URL-адресом только что созданного продукта.
Например, следующая модель указывает на то, что запросы должны включать свойства Name
и Description
. Если Name
и Description
не были указаны в запросе, происходит сбой проверки модели.
public class Product
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public bool IsOnSale { get; set; }
}
[ApiController]
Если атрибут применен, ошибки проверки модели приводят к коду состояния 400. Дополнительные сведения см. в разделе Автоматические отклики HTTP 400.
ActionResult и IActionResult
В следующем разделе сравнивается ActionResult
с IActionResult
Тип ActionResult<T>
ASP.NET Core включает тип возвращаемого значения ActionResult<T> для действий контроллера веб-API. Он позволяет возвращать тип, производный от ActionResult или определенный тип. ActionResult<T>
имеет следующие преимущества по сравнению с типом IActionResult:
- Свойство
Type
атрибута[ProducesResponseType]
можно исключить. Например,[ProducesResponseType(200, Type = typeof(Product))]
упрощается до[ProducesResponseType(200)]
. Ожидаемый тип возвращаемого значения действия вместо этого выводится изT
вActionResult<T>
. - Операторы неявного приведения поддерживают преобразование
T
иActionResult
вActionResult<T>
.T
преобразуется в ObjectResult, то естьreturn new ObjectResult(T);
упрощается доreturn T;
.
C# не поддерживает операторы неявных приведений в интерфейсах. Следовательно, для преобразования в конкретный тип необходимо использовать ActionResult<T>
. Например, использование IEnumerable
не работает в следующем примере:
[HttpGet]
public ActionResult<IEnumerable<Product>> Get() =>
_repository.GetProducts();
Один из способов исправить приведенный выше код — возвратить _repository.GetProducts().ToList();
.
Большинство действий имеют тип возвращаемого значения. При выполнении действия могут возникнуть непредвиденные условия, и в этом случае определенный тип не возвращается. Например, входной параметр действия может не пройти проверку модели. В этом случае обычно возвращается подходящий тип ActionResult
вместо конкретного типа.
Синхронное действие
Рассмотрим синхронное действие, в котором возможны два типа возвращаемых значений:
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetById(int id)
{
if (!_repository.TryGetProduct(id, out var product))
{
return NotFound();
}
return product;
}
В предшествующем действии:
- возвращается код состояния 404, если продукт не существует в базе данных;
- возвращается код состояния 200 с соответствующим объектом
Product
, если продукт существует.
Асинхронное действие
Рассмотрим асинхронное действие, в котором возможны два типа возвращаемых значений:
[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<Product>> CreateAsync(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return BadRequest();
}
await _repository.AddProductAsync(product);
return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}
В предшествующем действии:
- Код состояния 400 (BadRequest) возвращается средой выполнения ASP.NET Core, когда:
- Атрибут
[ApiController]
был применен, и проверка модели не пройдена. - Описание продукта содержит XYZ Widget.
- Атрибут
- Код состояния 201 генерируется методом CreatedAtAction при создании продукта. В этом пути к коду объект
Product
предоставляется в тексте ответа. Также предоставляется заголовок ответаLocation
с URL-адресом только что созданного продукта.
Дополнительные ресурсы
ASP.NET Core