使用 ASP.NET Core 建立 Web API

作者:Scott AddieTom Dykstra

ASP.NET Core 支援使用 C# 建立 RESTful 服務,也稱為 Web API。 若要處理要求,Web API 會使用控制器。 Web API 中的「控制器」都衍生自類別 ControllerBase。 本文說明如何使用控制器來處理 web API 要求。

查看或下載範例程式碼。 (如何下載)。

ControllerBase 類別

Web API 是由一或多個衍生自的控制器類別所組成 ControllerBase 。 Web API 專案範本提供入門控制器:

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

請不要從 Controller 類別衍生以建立 Web API 控制器。 Controller 衍生自 ControllerBase 並會新增檢視支援,以供處理網頁,而不是 Web API 要求。 這項規則有一個例外狀況:如果您打算針對 views 和 web 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 命名空間提供的屬性,可用來設定 Web 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 特有的行為:

錯誤狀態碼功能的問題詳細資料 需要2.2 或更新 版本的相容性版本。 其他功能需要2.1 或更新版本的相容性版本。

這些功能都需要相容性版本 2.1 或更新版本。

特定控制器上的屬性

[ApiController] 屬性可以套用至特定的控制器,如專案範本中的下列範例所示:

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

多個控制器上的屬性

在多個控制站上使用同一屬性的方法之一,就是建立以 [ApiController] 屬性標註的自訂基底控制器類別。 下列範例顯示自訂基類,以及衍生自該類別的控制器:

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

元件上的屬性

如果相容性版本設定為 2.2 或更新版本,[ApiController] 屬性就可以套用至組件。 以這種方式標註會將 Web API 行為套用至組件中的所有控制器。 沒有任何方法可以退出個別控制器。 將元件層級屬性套用至類別周圍的命名空間宣告 Startup

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

屬性路由需求

[ApiController] 屬性會讓屬性路由需求。 例如:

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

無法透過 UseEndpointsUseMvcUseMvcWithDefaultRoute 在中定義的傳統路由來存取動作 Startup.Configure

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

無法透過 UseMvc 中所定義的慣例路由Startup.Configure 中的 UseMvcWithDefaultRoute 來存取動作。

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類型:

  • 提供機器可讀取的格式來指定 web API 回應中的錯誤。
  • 符合 RFC 7807 規格

若要讓自動和自訂回應保持一致,請呼叫 ValidationProblem 方法,而不是 BadRequestValidationProblem 傳回 ValidationProblemDetails 物件以及自動回應。

記錄自動 400 回應

請參閱 如何記錄模型驗證錯誤的自動400回應 (dotnet/AspNetCore.Docs # 12157)

停用自動400回應

若要停用自動 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";
    });
services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[404].Link =
            "https://httpstatuses.com/404";
    });
services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

繫結來源參數推斷

繫結來源屬性可定義動作參數值的所在位置。 系統提供下列繫結來源屬性:

屬性 繫結來源
[FromBody] 要求本文
[FromForm] 要求本文中的表單資料
[FromHeader] 要求標頭
[FromQuery] 要求查詢字串參數
[FromRoute] 來自目前要求的路由資料
[FromServices] 作為動作參數插入的要求服務

警告

當值可能包含 %2f (也就是 /) 時,請勿使用 [FromRoute]%2f 不會是未逸出的 /。 如果值可能包含 %2f,請使用 [FromQuery]

若沒有 [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]。 如果是任何具有像是 IFormCollectionCancellationToken 等特殊意義的複雜內建類型,則為 [FromBody] 推斷規則的例外。 繫結來源推斷程式碼會忽略這些特殊的類型。
  • [FromForm] 針對類型 IFormFileIFormFileCollection 的動作參數推斷的。 而不會依據任何簡單或使用者定義的類型進行推斷。
  • 系統會依據符合路由範本參數的任何動作參數名稱推斷 [FromRoute]。 如果有多個路由符合動作參數,則會將任何路由值視為 [FromRoute]
  • 系統會依據任何其他動作參數推斷 [FromQuery]

FromBody 推斷備註

並不會為像是 stringint 等簡單型別,推斷 [FromBody]。 因此,當需要使用該功能時,應為簡單型別使用 [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.AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[StatusCodes.Status404NotFound].Link =
            "https://httpstatuses.com/404";
    });
services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[404].Link =
            "https://httpstatuses.com/404";
    });
services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

多部分/表單資料要求推斷

[ApiController]當動作參數以屬性標注時,屬性會套用推斷規則 [FromForm]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";
    });
services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[404].Link =
            "https://httpstatuses.com/404";
    });
services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressConsumesConstraintForFormFileParameters = true;
    options.SuppressInferBindingSourcesForParameters = true;
    options.SuppressModelStateInvalidFilter = true;
});

錯誤狀態碼的問題詳細資料

當相容性版本為 2.2 或更新版本時,MVC 會將錯誤結果 (具有狀態碼 400 以上的結果) 為具有 ProblemDetails 的結果。 以 RFC 7807 規格為基礎的 ProblemDetails 類型,在 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當屬性設定為時,會停用自動建立 for 錯誤狀態碼 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";
    });
services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .ConfigureApiBehaviorOptions(options =>
    {
        options.SuppressConsumesConstraintForFormFileParameters = true;
        options.SuppressInferBindingSourcesForParameters = true;
        options.SuppressModelStateInvalidFilter = true;
        options.SuppressMapClientErrors = true;
        options.ClientErrorMapping[404].Link =
            "https://httpstatuses.com/404";
    });

使用 [使用] 屬性定義支援的要求內容類型

依預設,動作支援所有可用的要求內容類型。 例如,如果將應用程式設定為支援 JSON 和 XML 輸入格式器,則動作支援多種內容類型,包括 application/jsonapplication/xml

[取用]屬性允許動作限制支援的要求內容類型。 將 [Consumes] 屬性套用至動作或控制器,並指定一或多個內容類型:

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

在上述程式碼中, CreateProduct 動作會指定內容類型 application/xml 。 路由至此動作的要求必須指定的 Content-Type 標頭 application/xml 。 未指定 Content-Type 標頭的要求會 application/xml415 不支援的媒體類型 回應中指定結果。

[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 處理具有相同 URL 的 POST 要求。 如果沒有套用 [Consumes] 類型條件約束的屬性,則會擲回不明確的 match 例外狀況。

[Consumes]屬性會套用至這兩個動作。 PostJson動作會處理與 Content-Type 標頭一起傳送的要求 application/jsonPostForm動作會處理與 Content-Type 標頭一起傳送的要求 application/x-www-form-urlencoded

其他資源