【譯】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。

額外資源


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM