zoukankan      html  css  js  c++  java
  • 【译】ASP.NET Core Web API的返回类型

    原文链接:传送门

    ASP.NET Core为Web API控制器动作方法返回类型提供了如下几个选择:

    这篇文章解释了什么时候最适合使用各个类型。

    指定类型(Specific type)

    最简单的API会返回原生的或者复杂的数据类型(比如,string 或者自定义对象类型)。考虑如下的Action方法,其返回了一个自定义的Product对象的集合。

    [HttpGet]
    public List<Product> Get() =>
        _repository.GetProducts();

    在程序的执行过程中,如果没有可知的条件来破坏安全,便可以返回一个特定的类型。前面的Action方法没有接收任何参数,因此不需要任何参数约束验证。

    当有可能具有多个返回类型时,通常的做法是将一个ActionResult 返回类型与原生的或者复杂的返回类型混合起来。IActionResult 或者 ActionResult<T> 都可以搭配这种类型的Action。

    这篇文章也会提供多个返回类型的几个示例。

    返回IEnumerable<T> 或者 IAsyncEnumerable<T>

    在ASP.NET Core 2.2及之前的版本中,从一个Action中返回 IEnumerable<T> 会导致序列化器的同步集合迭代。其结果便会阻塞调用以及潜在的资源池匮乏。为了演示这个,假设我们将使用EF  Core来实现Web API的数据访问需求。如下的Action的返回类型在序列化时是同步枚举的。

    public IEnumerable<Product> GetOnSaleProducts() =>
        _context.Products.Where(p => p.IsOnSale);

    在ASP.NET Core 2.2及之前的版本中,为了避免同步枚举以及在数据库中的阻塞等待,我们可以调用ToListAsync。

    public async Task<IEnumerable<Product>> GetOnSaleProducts() =>
        await _context.Products.Where(p => p.IsOnSale).ToListAsync();

    在ASP.NET Core 3.0及后续的版本中,可直接从Action 中返回IAsyncEnumerable<T>。其将会:

    • 不会导致同步迭代
    • 变得和返回IEnumerable<T> 一样高效。

    ASP.NET Core 3.0及后续的版本在将如下结果提供给序列化器之前,会将其缓存起来:

    public IEnumerable<Product> GetOnSaleProducts() =>
        _context.Products.Where(p => p.IsOnSale);

    可以考虑将Action方法签名的返回类型声明为 IAsyncEnumerable<T> 来保证同步迭代。最终,其迭代模式会基于返回的底层具体类型。MVC会自动缓存任何实现了IAsyncEnumerable<T>的具体类型。

    考虑如下Action,其将sale-priced Product 记录作为一个IEnumerable<Product>来返回。

    [HttpGet("syncsale")]
    public IEnumerable<Product> GetOnSaleProducts()
    {
        var products = _repository.GetProducts();
    
        foreach (var product in products)
        {
            if (product.IsOnSale)
            {
                yield return product;
            }
        }
    }

    上述Action的 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;
            }
        }
    }

    在ASP.NET Core 3.0中,上述两个Action都是非阻塞的。

    IActionResult返回类型

    当在一个Action方法中有可能具有多个ActionResult返回类型时,我们便可以使用 IActionResult 返回类型。ActionResult 类型表示了各种各样的HTTP状态码。继承自ActionResult 的任何非抽象类型均描述了一个有效的返回类型。在这个类别中,一些通用的返回类型是: BadRequestResult (400),NotFoundResult (404)以及OkObjectResult (200)。同样的,我峨我们可以在Action中使用ControllerBase 类的一些便捷方法来返回ActionResult类型。举例来说,return BadRequest(),是 return new BadRequestResult()的一种简便写法。

    因为在这种类型的Action中会有多个返回类型,我们便可以使用 [ProducesResponseType] 属性。这个属性为由工具(比如Swagger)生成的Web API帮助文档页产生更具描述性的响应详细。[ProducesResponseType] 标识了由Action返回的确知的类型以及状态码。

    同步Action

    考虑如下同步Action,其有两种可能的返回类型。

    [HttpGet("{id}")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public IActionResult GetById(int id)
    {
        if (!_repository.TryGetProduct(id, out var product))
        {
            return NotFound();
        }
    
        return Ok(product);
    }

    在上述Action中:

    • 当由id表示的Product并不存在于底层的数据存储中的时候,其便会返回一个404状态码。调用NotFound  方法来作为return new NotFoundResult();.的便捷方式。
    • 当Product确实存在时,便会返回一个200状态码以及Product对象。我们调用Ok 方法来作为return new OkObjectResult(product);的一种便捷写法。

    异步Action

    考虑如下异步Action,其具有两个可能的返回类型。

    [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);
    }

    在上述Action中:

    • 当产品描述包含“XYZ Widget”时,便会返回一个400状态码。我们调用BadRequest  方法来作为 return new BadRequestResult();的一种便捷写法。
    • 当创建一个Product时,CreatedAtAction 方法便会产生一个201状态码。调用CreatedAtAction 的另一种替代方案是返回:return new CreatedAtActionResult(nameof(GetById), "Products", new { id = product.Id }, product);。在这个代码路径中,Product 对象提供在响应体中。而响应头Location则包含了新创建的Product对象的URL。

    举个例子,如下的Model表明请求必须包含Name和Description属性。在请求中如果不能提供Name或者Descrption属性将会导致模型验证失败。

    public class Product
    {
        public int Id { get; set; }
    
        [Required]
        public string Name { get; set; }
    
        [Required]
        public string Description { get; set; }
    }

    在ASP.NET Core 2.1及后续版本中,如果应用了[ApiController] 属性,模型验证错误会导致400错误状态码。更多信息,请查看 Automatic HTTP 400 responses

    ActionResult<T>类型

    ASP.NET Core 2.1 为Web API控制器Action方法引入了ActionResult<T> 返回类型。它使你能够返回一个继承自 ActionResult 的类型或者返回一个特定的类型。相比于IActionResult类型来说,ActionResult<T> 提供了如下优势:

    •  [ProducesResponseType] 属性的Type属性可以被排除。举个例子[ProducesResponseType(200, Type = typeof(Product))] 可以被简化为[ProducesResponseType(200)]。Action期望的类型将会从 ActionResult<T> 中的T中推断出来。
    • 隐式转化操作符(Implicit cast operators )支持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();。

    大部分Action都有特定的返回类型。在Action的执行中可能会发生非期望的情形,在这种情况下不会返回特定的类型。举个例子,一个Action的输入参数可能没有通过模型验证。在这种情况下,通常会返回一个合适的ActionResult类型而不是一个特定的类型。

    同步Action

    考虑一个同步的Action,其具有两种可能的返回类型。

    [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;
    }

    在上述Action中:

    • 当Product不存在于数据库中时,便返回一个404状态码。
    • 当Product确实存在时候便会随着相关的Product对象返回一个200状态码。在ASP.NET Core之前的版本中, return product; 将会是return Ok(product);。

    异步Action

    考虑一个异步的Action,其具有两种可能的返回类型。

    [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);
    }

    在上述Action中;

    • ASP.NET Core运行时会返回一个400状态码,当
      • 应用了[ApiController]特性并且模型验证失败。
      • Product描述信息包含 “XYZ Widget”。
    • 当创建一个product 时,CreatedAtAction 方法会生成一个201状态码。在这个代码路径中,Product 对象被提供在响应体中,而响应头 Location则包含了新创建的Product的URL。

    额外资源

  • 相关阅读:
    Maven 学习笔记——Maven和Eclipse(2)
    Maven 学习笔记——Maven环境配置(1)
    Selenium WebDriver VS Selenium RC
    ASP.NET_SessionId
    'NuGet.VisualStudio.Interop 报错
    HTTP Error 403.14 Forbidden
    关于Python字符编码encode和decode
    zabbix安装步骤
    centos7 上搭建私有云
    Python读写改Excel的方法
  • 原文地址:https://www.cnblogs.com/qianxingmu/p/13959017.html
Copyright © 2011-2022 走看看