在 .NET Core 中構建 REST API

技術譯民發表於2021-03-15

翻譯自 Camilo Reyes 2020年8月26日的文章 《Build a REST API in .NET Core》 [1]

REST API 可以使用簡單的動詞(如 POST、PUT、PATCH 等)將大型解決方案背後的複雜性隱藏起來。在本文中,Camilo Reyes 解釋瞭如何在 .NET Core 中建立 REST API。

擴充套件大型複雜解決方案的一種方法是將它們分解為 REST 微服務。微服務開啟了 API 背後的業務邏輯的可測試性和可重用性。因為 REST API 可以被多個客戶端重用,使得組織可以共享軟體模組。客戶端或許是移動端、網頁端,甚至單頁應用中的靜態資源端,它們可以呼叫任意多的 API。

在本文中,我將向您展示在 .NET Core 中構建 REST API 的全過程。我將用現實工作中的需求來解釋這個過程,比如版本控制、搜尋、日誌記錄等等。REST 通常與諸如 POSTPUTPATCH 的動詞一起使用,因此我打算將它們全部覆蓋。我希望您看到的是,使用現有工具交付價值的一個好的有效的方式。

入門介紹

本文假定您已掌握了 ASP.NET、C# 和 REST API,因此我不會涉及任何基礎知識。我建議在學習本文時使用最新的 .NET Core 版本[2]。如果您想從工作程式碼開始學習,可以從 GitHub 下載[3]示例程式碼。

你可以先新建一個資料夾,比如 BuildRestApiNetCore,然後在 shell 中開啟它:

dotnet new sln
dotnet new webapi --no-https
dotnet sln add .

該專案基於禁用了 HTTPS 的 Web API 模板,以簡化本地開發。雙擊解決方案檔案會在 Visual Studio 中開啟它(如果已安裝)。為了支援 .NET Core 3.1,請確保安裝了 2019 版的 IDE。

由於 API 在客戶端和資料庫之間建立了一層分離,因此準備資料是一個很好的開始。為了簡化資料訪問,Entity Framework 提供了一個記憶體中的替代方案,這樣我就可以只關注 API 本身。

通過 NuGet 獲取記憶體資料庫提供程式:

dotnet add package Microsoft.EntityFrameworkCore.InMemory

然後,建立以下資料模型。我將其放在 Models 資料夾中,以表示此名稱空間存放原始資料。若要使用資料標註,請在 using 語句中新增 System.ComponentModel.DataAnnotations

public class Product
{
    [Key]
    [Required]
    [Display(Name = "productNumber")]
    public string ProductNumber { get; set; }

    [Required]
    [Display(Name = "name")]
    public string Name { get; set; }

    [Required]
    [Range(10, 90)]
    [Display(Name = "price")]
    public double? Price { get; set; }

    [Required]
    [Display(Name = "department")]
    public string Department { get; set; }
}

在實際的解決方案中,這可能要根據團隊的需要將其放在單獨的專案中。請注意分配給該模型的屬性,例如 RequiredDisplayRange,這些是 ASP.NET 中的資料標註,用於在模型繫結時驗證 Product。因為我使用的是記憶體資料庫,所以 Entity Framework 需要一個唯一的 Key。這些屬性指定了驗證規則,例如:價格區間或者該屬性是否是必須的。

從業務的視角來看,這是一個包含產品編號、名稱和價格的電子商務站點。每個產品還指定了一個部門,以便按部門進行搜尋。

接下來,在 Models 名稱空間中設定 Entity Framework DbContext

public class ProductContext : DbContext
{
    public ProductContext(DbContextOptions<ProductContext> options) : base(options)
    {
    }

    public DbSet<Product> Products { get; set; }
}

該資料庫上下文被依賴注入到控制器中,用於查詢或更新資料。要在 ASP.NET Core 中啟用依賴注入,請開啟 Startup 類並將其新增到 ConfigureServices 中:

services.AddDbContext<ProductContext>(opt => opt.UseInMemoryDatabase("Products"));

這行程式碼完成了記憶體資料庫。請確保在兩個類的 using 語句中新增 Microsoft.EntityFrameworkCore。一個空白的後端是無趣的,因此我們來填充一些種子資料。

建立下面的擴充套件方法以幫助迭代生成種子資料,可以將它放在 Extensions 名稱空間或資料夾中:

public static class EnumerableExtensions
{
    public static IEnumerable<T> Times<T>(this int count, Func<int, T> func)
    {
        for (var i = 1; i <= count; i++) yield return func.Invoke(i);
    }
}

Models 名稱空間下新增一個靜態類以初始化種子資料:

public static class ProductSeed
{
    public static void InitData(ProductContext context)
    {
        var rnd = new Random();

        var adjectives = new[] { "Small", "Ergonomic", "Rustic", "Smart", "Sleek" };
        var materials = new[] { "Steel", "Wooden", "Concrete", "Plastic", "Granite", "Rubber" };
        var names = new[] { "Chair", "Car", "Computer", "Pants", "Shoes" };
        var departments = new[] { "Books", "Movies", "Music", "Games", "Electronics" };

        context.Products.AddRange(900.Times(x =>
        {
            var adjective = adjectives[rnd.Next(0, 5)];
            var material = materials[rnd.Next(0, 5)];
            var name = names[rnd.Next(0, 5)];
            var department = departments[rnd.Next(0, 5)];
            var productId = $"{x,-3:000}";

            return new Product
            {
                ProductNumber = $"{department.First()}{name.First()}{productId}",
                Name = $"{adjective} {material} {name}",
                Price = (double)rnd.Next(1000, 9000) / 100,
                Department = department
            };
        }));

        context.SaveChanges();
    }
}

這段程式碼迴圈遍歷一個 900 條資料的列表以生成大量的產品,這些產品的部門、價格和名稱都是隨機撿選的。每個產品都有一個“巧妙”的 key 作為主鍵,該主鍵由部門、名稱和產品 Id 組合而成。

有了這些種子資料,您就可以得到諸如在 Electronics 部門帶有標價的名為 “Smart Wooden Pants” 的產品了。

作為開始構建 Endpoints[4]的第一步,設定 API 版本是一個好主意。這使得客戶端應用可以隨時升級 API 功能,而無需緊密耦合。

API 版本控制來自一個 NuGet 包:

dotnet add package Microsoft.AspNetCore.Mvc.Versioning

回到 Startup 類,並將其新增到 ConfigureServices 中:

services.AddApiVersioning(opt => opt.ReportApiVersions = true);

我選擇在 API 響應中包含可用的版本號,以便客戶端知道何時有升級可用。我推薦使用 語義化的版本控制 [5]來傳達 API 中的重大更改。讓客戶端知道每次升級都修改了什麼,這樣有助於每個客戶端保持最新的功能。

REST API 中的搜尋 Endpoint

要構建一個 Endpoint,請在 Controllers 資料夾中轉到 ASP.NET 中的 Controller。

使用下面的程式碼建立一個 ProductsController,請確保在 using 語句中新增 Microsoft.AspNetCore.Mvc 名稱空間:

[ApiController]
[ApiVersion("1.0")]
[Route("v{version:apiVersion}/[controller]")]
[Produces("application/json")]
public class ProductsController : ControllerBase
{
    private readonly ProductContext _context;

    public ProductsController(ProductContext context)
    {
        _context = context;

        if (_context.Products.Any()) return;

        ProductSeed.InitData(context);
    }
}

請注意,當資料庫中沒有任何產品時,將執行 InitData 初始化種子資料。我設定了一個帶有版本控制的 Route,版本號通過 ApiVersion 設定。通過依賴注入將資料上下文 ProductContext 注入到建構函式中。在該 Controller 中,第一個 Endpoint 是返回一個產品列表的 GET

[HttpGet]
[Route("")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<IQueryable<Product>> GetProducts()
{
    var result = _context.Products as IQueryable<Product>;

    return Ok(result.OrderBy(p => p.ProductNumber));
}

請確保在 using 語句中新增 Microsoft.AspNetCore.Http,以設定響應型別中的狀態碼。

我選擇按照產品編號排序產品,以便更簡單地顯示結果。在生產系統中,可以檢查這種排序是否與聚集索引相匹配,以便減輕資料庫的執行壓力。經常檢查執行計劃和統計 IO,以確認有良好的效能。

此專案已經可以進行測試了!在命令列中執行以下命令:

dotnet watch run

使用 curl 測試該 Endpoint:

curl -i -X GET "http://localhost:5000/v1/products" -H "accept: application/json"

我在兩個獨立的控制檯視窗中執行上面這兩條命令。一個以監視模式執行專案,當我更改程式碼檔案時,會自動重新生成並重新整理;另一個是我保持 curl 結果的地方。您可以使用 Postman,但是伴隨 Windows 10 而來的 curl 也可以完成該工作。

結果如下:

curl results of GetProducts

該請求返回資料庫中的所有產品,但它不可擴充套件。隨著產品列表的增加,客戶端將受到未過濾資料的猛烈衝擊,從而給 SQL 和網路流量帶來更大的壓力。

更好的方法是在一個模型中引入 limitoffset 請求引數:

public class ProductRequest
{
    [FromQuery(Name = "limit")]
    public int Limit { get; set; } = 15;

    [FromQuery(Name = "offset")]
    public int Offset { get; set; }
}

將此請求引數關聯到 GetProducts Endpoint:

public ActionResult<IQueryable<Product>> GetProducts([FromQuery] ProductRequest request)
{
    var result = _context.Products as IQueryable<Product>;

    Response.Headers["x-total-count"] = result.Count().ToString();

    return Ok(result
        .OrderBy(p => p.ProductNumber)
        .Skip(request.Offset)
        .Take(request.Limit));
}

請注意我設定了一個值為 Count 的 HTTP header x-total-count,用於幫助想要分頁瀏覽整個結果集的客戶端。如果未指定請求引數,則該 API 預設返回前 15 條資料。

接下來,新增一個搜尋引數,按部門篩選產品:

public ActionResult<IQueryable<Product>> GetProducts([FromQuery] 
             string department, [FromQuery] ProductRequest request)
{
    // ...
    if (!string.IsNullOrEmpty(department))
    {
        result = result.Where(p => p.Department.StartsWith(department, 
                        StringComparison.InvariantCultureIgnoreCase));
    }
    // ..
}

可以通過修改 Query,讓搜尋進入條件塊內。請注意我用了 StartsWithInvariantCultureIgnoreCase 來簡化產品過濾,在實際的 SQL 中,可以使用 LIKE 運算子,還可以通過排序規則設定不區分大小寫。

要測試分頁和此新過濾器,請使用 curl 執行以下命令:

curl -i -X GET "http://localhost:5000/v1/products?offset=15&department=electronics" -H "accept: application/json"

檢查確定包含總數和受支援版本號的 HTTP 頭:

HTTP/1.1 200 OK
Date: Thu, 28 Jan 2021 11:19:09 GMT
Content-Type: application/json; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked
x-total-count: 155
api-supported-versions: 1.0

日誌記錄和 API 文件

當 API 成形後,如何向其他開發人員傳達 Endpoints 呢?對於團隊來說,在不破壞開放程式碼的情況下了解 API 公開的內容是有好處的。Swagger 是這裡的首選工具,它能通過反射,自動生成可用的文件。

如果我告訴您,Swagger 所需的一切都已經在此 API 中設定過了呢?來吧,再看一眼:

[Produces("application/json")]
[ProducesResponseType(StatusCodes.Status200OK)]
ActionResult<IQueryable<Product>> GetProducts([FromQuery] 
               string department, [FromQuery] ProductRequest request)

ASP.NET 屬性對於 Endpoints 的自文件化非常有用。Swagger 通過反射,從控制器方法中獲得返回型別,進而推斷響應該是什麼樣子,並獲得每個控制器方法的請求引數。因為它收集了工作程式碼中的所有內容,所以可以生成“活文件”,從而減少了故障的發生。

通過 NuGet 獲取缺少的依賴項:

dotnet add package Swashbuckle.AspNetCore

並在 ConfigureServices 中將其關聯進來:

services.AddSwaggerGen(c => c.SwaggerDoc("v1", new OpenApiInfo
{
    Title = "Products",
    Description = "The ultimate e-commerce store for all your needs",
    Version = "v1"
}));

然後,在 Configure 中啟用它:

app.UseSwagger();
app.UseSwaggerUI(opt => opt.SwaggerEndpoint("/swagger/v1/swagger.json", "Products v1"));

注意 OpenApiInfo 來自 Microsoft.OpenApi.Models 名稱空間。

此時,在瀏覽器中導航到 http://localhost:5000/swagger 就可以檢視 swagger 文件了。

頁面大概如下顯示:

swagger page

在 swagger 文件中,您可以輕鬆瀏覽 API 並通過這個工具向 API 發起請求,您所在組織的其他開發人員會因此受益而輕鬆愉快,他們甚至可能會請您喝杯咖啡。

展開 GET /Products 檢視從控制器方法中提取的 C# 資料型別:

swagger page

下一站是日誌記錄。我將使用 NLog 在後端儲存日誌,使得 API 能夠儲存日誌以供進一步分析。在實際環境中,日誌對於故障排除非常有用;另外,它們還可以幫助收集遙測資料,以幫助瞭解 API 在未知狀態下的使用情況。

要設定日誌記錄器,需要完成做以下操作:

  • 一個 NuGet 包
  • 一個 nlog.config 設定檔案
  • 修改 Program
  • 微調 appsettings.json

安裝 NuGet 包:

dotnet add package NLog.Web.AspNetCore

設定的 nlog.config 檔案可以如下:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      throwExceptions="false"
      throwConfigExceptions="false"
      autoReload="true"
      internalLogLevel="Warn"
      internalLogFile=
           "C:\temp\BuildRestApiNetCore\RestApi-internal-nlog.txt">
 
  <extensions>
    <add assembly="NLog.Web.AspNetCore"/>
  </extensions>
 
  <targets async="true">
    <target xsi:type="File"
            name="ownFile-web"
            fileName=
              "C:\temp\BuildRestApiNetCore\RestApi-${shortdate}.log">
 
      <layout xsi:type="JsonLayout">
        <attribute name="Timestamp" layout="${longdate}" />
        <attribute name="Level" layout="${uppercase:${level}}" />
        <attribute name="Logger" layout="${logger}" />
        <attribute name="Action" layout="${aspnet-mvc-action}" />
        <attribute name="Message" layout="${message}" />
        <attribute 
           name="Exception" layout="${exception:format=tostring}" />
      </layout>
    </target>
  </targets>
 
  <rules>
    <logger name="Microsoft.*" maxlevel="Info" final="true" /> 
                
    <logger name="*" minlevel="Info" writeTo="ownFile-web" />
  </rules>
</nlog>

請注意 Layout,因為它設定了日誌檔案的型別,這裡將其設定為 JsonLayout。當在不同的分析工具中使用日誌檔案時,JSON 格式具有最大的靈活性。為了讓冗餘降到最小,記錄器規則不記錄來自 Microsoft.* 的錯誤。另外,因為將 throwExceptions 設定為了 false,API 中未處理的異常會被記錄,但不會被重新丟擲。這裡的用法可能是多變的,但通常最好是在 logger 中處理所有未處理的異常。

Program 類中,啟用 NLog,記得新增 using NLog.Web

Host.CreateDefaultBuilder(args)
  .ConfigureWebHostDefaults(webBuilder =>
  {
    webBuilder.UseStartup<Startup>();
  })
  .UseNLog();

最後,在 appsettings.json 中進行以下微調來配置日誌記錄:

"Logging": {
  "LogLevel": {
    "Default": "Information",
    "Microsoft": "None",
    "Microsoft.AspNetCore": "Error",
    "Microsoft.Hosting.Lifetime": "Information"
  }
}

這裡的基本思想是減少與此 API 無關的日誌條目的數量。您可以隨意調整這些設定,以便恰當地記錄 API 所需要的日誌內容。

是時候言歸正傳了,在 Controller 類中,新增 using Microsoft.Extensions.Logging 並注入一個普通的舊式 ASP.NET logger:

private readonly ILogger<ProductsController> _logger;
 
public ProductsController(ProductContext context, 
            ILogger<ProductsController> logger)
{
  _logger = logger;
  // ...
}

假設,現在您的團隊決定要抓取客戶端請求獲取 100 條或更多條記錄的頻率相關的遙測資料。

將下面的程式碼放入 GetProducts 中:

if (request.Limit >= 100)
  _logger.LogInformation("Requesting more than 100 products.");

請確保有一個已存在的臨時資料夾來核查日誌,例如:C:\temp\BuildRestApiNetCore\

一條日誌記錄看起來可能是這樣的:

{
  "Timestamp": "2020-07-12 10:30:30.8960",
  "Level": "INFO",
  "Logger": "BuildRestApiNetCore.Controllers.ProductsController",
  "Action": "GetProducts",
  "Message": "Requesting more than 100 products."
}

帶動詞的 REST Endpoints

深吸一口氣,然後暢快地撥出。該 API 差不多可以投入生產環境了,而且只用了很少的程式碼。現在,我將快速轉向 POSTPUTPATCHDELETE 等 REST 特性的介紹。

POST Endpoint 接收帶有新產品的 body,並將其新增到列表當中。此方法是非冪等的,因為它在呼叫時會建立新的資源。

將下面的程式碼放入 ProductsController 中:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Product> PostProduct([FromBody] Product product)
{
  try
  {
    _context.Products.Add(product);
    _context.SaveChanges();
 
    return new CreatedResult($"/products/{product.ProductNumber.ToLower()}", product);
  }
  catch (Exception e)
  {
    _logger.LogWarning(e, "Unable to POST product.");
 
    return ValidationProblem(e.Message);
  }
}

ASP.NET 通過 ValidationProblem 自動處理異常。該驗證將返回一條符合 RFC 7807 規範[6]的響應,並帶有一條訊息。在實際的系統中,我建議確保不要暴露任何關於 API 的內部資訊。將異常資訊放在此處有助於客戶端對程式碼進行故障排除,但安全性也很重要。我在這裡選擇包含錯誤資訊主要是為了演示目的。此處還會將異常記錄為警告,以避免記錄大量的錯誤。當異常太多時,監控工具可能會呼叫值班人員。最佳實踐是僅在可能需要人工干預的災難性故障期間記錄錯誤。

使用 swagger 工具,curl 命令為:

curl -i -X POST http://localhost:5000/v1/products
  -H "accept: application/json"
  -H "Content-Type: application/json"
  -d "{\"productNumber\":\"string\",\"name\":\"string\",\"price\":10,\"department\":\"string\"}"

當請求有問題時,API 會如下響應:

{
  "errors": {},
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title":"One or more validation errors occurred.",
  "status": 400,
  "detail": "An item with the same key has already been added. Key: string",
  "traceId":"|c445a403-43564e0626f9af50."
}

400 (Bad Request) 響應表示請求中的使用者錯誤。因為無法信任使用者傳送有效資料,所以 API 會記錄一個警告。

請注意,如果成功,POST 將返回帶有 Location 的 201:

HTTP/1.1 201 Created
Date: Mon, 13 Jul 2020 22:52:46 GMT
Content-Type: application/json; charset=utf-8
Server: Kestrel
Content-Length: 76
Location: /products/bc916
api-supported-versions: 1.0

這將引導客戶端轉向新資源。此處,轉向 GET Endpoint 是個好主意:

[HttpGet]
[Route("{productNumber}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetProductByProductNumber([FromRoute] 
            string productNumber)
{
  var productDb = _context.Products
    .FirstOrDefault(p => p.ProductNumber.Equals(productNumber, 
              StringComparison.InvariantCultureIgnoreCase));
 
  if (productDb == null) return NotFound();
 
  return Ok(productDb);
}

404 響應表示該資源在 API 中尚不存在,但可能會在將來的某個時候變得可用。

PUT 是類似的:

[HttpPut]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Product> PutProduct([FromBody] Product product)
{
  try
  {
    var productDb = _context.Products
      .FirstOrDefault(p => p.ProductNumber.Equals(product.ProductNumber, 
           StringComparison.InvariantCultureIgnoreCase));
 
    if (productDb == null) return NotFound();
 
    productDb.Name = product.Name;
    productDb.Price = product.Price;
    productDb.Department = product.Department;
    _context.SaveChanges();
 
    return Ok(product);
  }
  catch (Exception e)
  {
    _logger.LogWarning(e, "Unable to PUT product.");
 
    return ValidationProblem(e.Message);
  }
}

在 REST 設計中,PUT 允許對整個資源進行更新。它是冪等的,因為多次相同的請求不會改變資源的數量。

就像 GET 404 響應一樣,表示該資源不可用於更新,但這可能在稍後發生改變。另外,ASP.NET 提供現成的模型繫結驗證。接下來,嘗試一下使用錯誤的資料更新現有資源。

下面的 JSON 是您可能看到的 Bad Request 的響應:

{
  "errors": {
    "Price": ["The price field is required."]
  },
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "|c445a409-43564e0626f9af50."
}

PATCH 是所有動詞中最複雜的,因為它通過 JSON Patch 文件[7]僅更新資源的一部分。

慶幸的是 .NET Core 提供了一個 NuGet 包:

dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson

然後,在 ConfigureServices 中啟用它:

services.AddControllers().AddNewtonsoftJson();

下面是 PATCH Endpoint,記得新增 using Microsoft.AspNetCore.JsonPatch

[HttpPatch]
[Route("{productNumber}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<Product> PatchProduct([FromRoute] 
      string productNumber, [FromBody] JsonPatchDocument<Product> patch)
{
  try
  {
    var productDb = _context.Products
      .FirstOrDefault(p => p.ProductNumber.Equals(productNumber, 
           StringComparison.InvariantCultureIgnoreCase));
 
    if (productDb == null) return NotFound();
 
    patch.ApplyTo(productDb, ModelState);
 
    if (!ModelState.IsValid || !TryValidateModel(productDb)) 
             return ValidationProblem(ModelState);
 
    _context.SaveChanges();
 
    return Ok(productDb);
  }
  catch (Exception e)
  {
    _logger.LogWarning(e, "Unable to PATCH product.");
 
    return ValidationProblem(e.Message);
  }
}

我希望您看到一種含有不同狀態碼響應型別的模式開始浮現。200 OK 表示成功,400 Bad Request 表示使用者錯誤。當 patch 被應用後,將會在 ModelState 中追加所有的驗證錯誤。仔細看一下進行模型繫結的 JsonPatchDocument 和 應用更改的 ApplyTo。這就是將 JSON Patch 文件應用到資料庫中現有產品的方式。像所有其它 Endpoints 一樣,異常會被記錄幷包含在響應中。與其它動詞一樣,404 (Not Found) 響應表示相同的情形。響應狀態碼的一致性有助於客戶端處理所有可能的場景。

一個 JSON patch 請求的 body 大概如下所示:

[{
  "op": "replace",
  "path": "price",
  "value": 13.67
}]

模型繫結驗證規則依然適用於 patch 操作,以保持資料的完整性。請注意,patch 操作被包裝在一個陣列中,因此它支援任意的操作列表。

下圖是 curl 中請求 PATCH 的結果:

PATCH in curl

最後一站,DELETE 方法:

[HttpDelete]
[Route("{productNumber}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> DeleteProduct([FromRoute] 
        string productNumber)
{
  var productDb = _context.Products
    .FirstOrDefault(p => p.ProductNumber.Equals(productNumber, 
           StringComparison.InvariantCultureIgnoreCase));
 
  if (productDb == null) return NotFound();
 
  _context.Products.Remove(productDb);
  _context.SaveChanges();
 
  return NoContent();
}

它的狀態碼響應是 No Content

HTTP/1.1 204 No Content
Date: Tue, 14 Jul 2020 22:59:20 GMT
Server: Kestrel
api-supported-versions: 1.0

此狀態向客戶端發出訊號,表示資源不再可用,因為響應主體為空。如果需要非同步後臺程式來清理資料,則響應也可以為 204 (Accepted)。在實際系統中,有時最好使用軟刪除,以允許在稽核期間進行回滾。刪除資料時,請確保遵守 GPDR 或適用於該資料的任一策略。

總結

.NET Core 在您的工具袋中新增許多有用的特性,從而讓使用 REST API 變得更加輕鬆,將諸如文件、驗證、日誌記錄和 PATCH 請求等複雜的用例變得更易於實現。


作者 : Camilo Reyes
譯者 : 技術譯民
出品 : 技術譯站
連結 : 英文原文


  1. https://www.red-gate.com/simple-talk/dotnet/c-programming/build-a-rest-api-in-net-core/ Build a REST API in .NET Core ↩︎

  2. https://dotnet.microsoft.com/download/dotnet-core Download .NET Core ↩︎

  3. https://github.com/beautifulcoder/BuildRestApiNetCore 示例程式碼 ↩︎

  4. https://rapidapi.com/blog/api-glossary/endpoint/ API Endpoints ↩︎

  5. https://semver.org/ Semantic Versioning ↩︎

  6. https://tools.ietf.org/html/rfc7807 RFC 7807 規範 ↩︎

  7. https://tools.ietf.org/html/rfc6902 JSON Patch 文件 ↩︎

相關文章