ASP.NET Core Web API 中控制器操作的返回类型
ASP.NET Core Web API 中控制器操作的返回类型
ASP.NET Core为 Web 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 类型
当操作中可能有多个 ActionResult
返回类型时,适合使用 IActionResult 返回类型。 ActionResult
类型表示多种 HTTP 状态代码。 派生自 ActionResult
的任何非抽象类都限定为有效的返回类型。 此类别中的某些常见返回类型为 BadRequestResult (400)、NotFoundResult (404) 和 OkObjectResult (200)。 或者,可以使用 ControllerBase 类中的便利方法从操作返回 ActionResult
类型。 例如,return BadRequest();
是 return new BadRequestResult();
的简写形式。
由于此操作类型中有多个返回类型和路径,因此必须自由使用 [ProducesResponseType] 特性。 此特性可针对 Swagger 等工具生成的 Web API 帮助页生成更多描述性响应详细信息。 [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); }
在上述操作中:
- 当
id
代表的产品不在基础数据存储中时,则返回 404 状态代码。 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); }
在上述操作中:
-
当产品说明包含“XYZ 小组件”时,返回 400 状态代码。 BadRequest 便利方法作为
return new BadRequestResult();
的简写调用。 -
在创建产品后,CreatedAtAction 便利方法生成 201 状态代码。 以下代码是调用
CreatedAtAction
的替代方法:
return new CreatedAtActionResult(nameof(GetById), "Products", new { id = product.Id }, product);
-
在前面的代码路径中,
Product
对象在响应正文中提供。 提供了包含新建产品 URL 的Location
响应标头。
例如,以下模型指明请求必须包含 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包括 Web API 控制器操作的 ActionResult<T> 返回类型。 它允许返回派生自 ActionResult 或返回 特定类型的类型。 ActionResult<T>
与 IActionResult 类型不同的优势如下:
- 可排除 [ProducesResponseType] 特性的
Type
属性。 例如,[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_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); }
在上述操作中:
- 在以下情况下,ASP.NET Core 运行时返回 400 状态代码 (BadRequest):
- 已应用 [ApiController] 属性,且模型验证失败。
- 产品说明包含“XYZ 小组件”。
- 在创建产品后,CreatedAtAction 方法生成 201 状态代码。 在此代码路径中,将在响应正文中提供
Product
对象。 提供了包含新建产品 URL 的Location
响应标头。
参考:https://learn.microsoft.com/zh-cn/aspnet/core/web-api/action-return-types?view=aspnetcore-7.0