2022年8月16日:--使用 ASP.NET Core、最小 API 和 .NET 6 创建--使用最小 API、ASP.NET Core 和 .NET 6 生成 Web API(没看懂)

13 篇文章 0 订阅

了解如何使用最小 API 和 .NET 6 生成 Web API。

简介

生成 Web API 是一种常见任务。 希望能够提交一些数据,了解应用程序或服务如何使用它。 生成 API 的方式在不同的技术堆栈之间可能会有很大的差别。 了解在生成 API 的过程中,有许多需要处理的事项,例如数据存储、安全性、版本控制和文档。 处理所有这些事项可能需要进行复杂的操作。

什么是最小API?

生成 API 的过程可能很复杂,因为它需要支持多种功能,例如路由、在数据存储中进行读取和写入,以及身份验证。 为了节省时间,请从 .NET 框架着手,这些框架提供许多需要的功能。 但在启动并运行基本 API 之前,这些框架可能需要进行大量设置。 对于适用于 .NET 6 的最小 API,情况并非如此。 通过几行代码即可快速入门。

什么是最小API?

如果已开发 .NET Core Web API,则已使用了一种使用控制器的方法。 其思路是让控制器类方法(表示各种 HTTP 谓词)执行操作来完成特定任务。 例如,GetProducts() 将使用 GET 作为 HTTP 谓词来返回产品。

这个基于控制器的方法和最小 API 有何区别呢?

  • 无 Startup.cs:生成最小 API 时,不需要 Startup.cs 文件。 而习惯执行的所有任务都发生在 Program.cs 中。 习惯执行的任务包括设置路由和配置依赖项注入、安全和 CORS。

  • 顶级语句:由于最小 API 使用 .NET 6,因此可以使用顶级语句。

请注意 program 类、Main() 方法和 using 语句的使用。 使用顶级语句时,所有这些都将消失。

最小 API 使用此方法来减少需要键入的行数。 若要创建 API,只需使用以下几行代码:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();

不存在 using 语句、Main() 方法或类。 仅有四行代码。

路由看起来稍有不同:与基于控制器的 Web API 相比,路由看起来略有不同。 在 Web API 中,对于路由,编写如下所示的代码:

app.UseRouting();
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
    // add my own routes
});

对于最小 API,直接在 app 实例上添加路由:

app.MapGet("/todos", await (TodoDb db) => db.Todos.ToListAsync());
app.MapPost("/todos", await (Todo todo) => {});
app.MapPut("/todos", (Todo todo) => {});
app.MapDelete("/todos/{id}", (int id) => {}});

同样的功能仍然存在。 仍可以按照与过去几乎相同的方式来配置数据库、设置 CORS 和添加身份验证。

使用最小API来创建API

你需要做的第一件事就是安装 .NET 6。 安装它以后,就可以创建最小 API 项目了。 在命令行中运行以下代码来创建最小 API 项目:

dotnet new web -o PizzaStore -f net6.0

新创建的 PizzaStore 文件夹包含 API 项目。

 

 

检查文件

你获取的文件非常类似于你通过基于控制器的 API 获得的文件:

bin/
obj/
appsettings.Development.json
appsettings.json
PizzaStore.csproj
Program.cs

 

查看 PizzaStore.csproj 里的内容时,你会看到一个如下所示的条目:

<PropertyGroup>
  <TargetFramework>net6.0</TargetFramework>
  <Nullable>enable</Nullable>
</PropertyGroup>

此代码告知使用的是 .NET 6。

Program.cs 保存你的 API。 接下来查看此文件的内容。

 

了解代码

Program.cs 包含 API 代码。 让我们详细了解一个程序示例:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();

在前两行代码中,将创建一个生成器。 在 builder 中,将构造一个应用程序实例 app

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

此生成器具有 Services 属性。 通过使用 Services 属性,可以添加 CORS、实体框架或 Swagger 等功能。 下面是一个示例:

builder.Services.AddCors(options => {});

在 Services 属性中,告知 API 这是一项可用的功能。 与之相反,app 实例用于实际使用它。 因此,可以使用 app 实例来设置路由:

app.MapGet("/", () => "Hello World!");

还可以使用 app 实例来添加中间件。 下面是如何使用 CORS 等功能的示例:

app.UseCors("some unique string");

中间件通常是用于截获请求并执行检查(例如检查身份验证,或确保客户端获允根据 CORS 执行此操作)的代码。 在中间件执行完以后,将执行实际请求。在存储中读取或写入数据,然后将响应发送回调用客户端。

最后,app.Run() 启动 API,让其侦听来自客户端的请求。

若要运行代码,请使用 dotnet run 来启动你的项目,就像任何 .NET Core 项目一样。 默认情况下,这意味着在 http://localhost:{PORT} 上有一个运行的项目,其中 PORT 是介于 5000 和 5300 之间的值。

通过Swagger添加文档

文档是对于 API 所需要的内容。它是使文档与 API 在后者发生更改的情况下保持同步的关键。 好的方法是以标准化方式描述 API,确保它是自文档化的。 自文档化是指在代码发生更改时文档会随之更改。

Swagger 实现 Open API 规范。 此格式描述路由,以及它们接受和生成的数据。 Swagger UI 是工具的集合,可将 Open API 规范呈现为网站,并使可以通过网站与 API 交互。

若要在 API 中使用 Swagger 和 Swagger UI,需要执行以下两项操作:

安装包。 若要安装 Swagger,请指定安装名为 Swashbuckle 的包:

dotnet add package Swashbuckle.AspNetCore --version 6.1.4

配置它。 安装包后,通过代码对其进行配置。 添加几个不同的条目:

添加命名空间。 稍后调用 SwaggerDoc() 并提供 API 的标头信息时,需要此命名空间。

using Microsoft.OpenApi.Models;

添加 AddSwaggerGen()。 此方法在 API 上设置标头信息,例如它调用的内容和版本号。

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
  {
      c.SwaggerDoc("v1", new OpenApiInfo { Title = "Todo API", Description = "Keep track of your tasks", Version = "v1" });
  });

添加 UseSwagger() 和 UseSwaggerUI()。 这两个代码行会告知 API 项目使用 Swagger,并告知在何处查找规范文件 swagger.json。

app.UseSwagger();
app.UseSwaggerUI(c =>
  {
     c.SwaggerEndpoint("/swagger/v1/swagger.json", "Todo API V1");
  });

可以启动项目并转到 http://localhost:5000/swagger

创建最小API

本模块使用 .NET CLI(命令行接口)和 Visual Studio Code 进行本地开发。

此模块使用 .NET 6.0 SDK。 通过在首选终端中运行以下命令,确保你已安装 .NET 6.0:

dotnet --list-sdks

将显示类似于下面的输出:

3.1.100 [C:\program files\dotnet\sdk]
5.0.100 [C:\program files\dotnet\sdk]
6.0.100 [C:\program files\dotnet\sdk]

确保列出了以 6 开头的版本。

创建项目的基架

首先,需要搭建项目的基架。 

通过运行 dotnet new 创建 Web API:

dotnet new web -o PizzaStore -f net6.0

应该会看到 PizzaStore 目录。

通过调用 dotnet run 运行应用。 它生成应用,并将其托管在 5000 到 5300 之间的端口上。 HTTPS 为其选择了范围为 7000 到 7300 的端口。

如果要替代随机端口选择行为,可以设置要在 launchSettings.json 中使用的端口。

dotnet run

下面是输出在终端中的情况:

Building...
 info: Microsoft.Hosting.Lifetime[14]
       Now listening on: https://localhost:7200
 info: Microsoft.Hosting.Lifetime[14]
       Now listening on: http://localhost:5100
 info: Microsoft.Hosting.Lifetime[0]
       Application started. Press Ctrl+C to shut down.
 info: Microsoft.Hosting.Lifetime[0]
       Hosting environment: Development
 info: Microsoft.Hosting.Lifetime[0]
       Content root path: /<path>/PizzaStore

在浏览器中,转到指示的端口。 根据终端 http://localhost:{PORT},应会看到文本“Hello World!”

 

 

 

添加Swagger

使用 Swagger 来确保你有一个自文档化 API,其中的文档会随着代码的更改而更改。

安装 Swashbuckle 包:

dotnet add package Swashbuckle.AspNetCore --version 6.2.3

 打开 PizzaStore.csproj 验证安装。 你应该有一个如下所示的条目:

<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />

 

接下来,将项目配置为使用 Swagger。

打开 Program.cs,修改以下代码:

using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder(args);
    
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
     c.SwaggerDoc("v1", new OpenApiInfo { Title = "PizzaStore API", Description = "Making the Pizzas you love", Version = "v1" });
});
    
var app = builder.Build();
    
if (app.Environment.IsDevelopment())
{
     app.UseDeveloperExceptionPage();
}
    
app.UseSwagger();
app.UseSwaggerUI(c =>
{
   c.SwaggerEndpoint("/swagger/v1/swagger.json", "PizzaStore API V1");
});
    
app.MapGet("/", () => "Hello World!");
    
app.Run();

  

重新运行项目并转到应用的地址 http://localhost:{PORT}/swagger

 

了解如何添加路由和使用其他高级命令

现在,已详细了解了什么是最小 API 以及为何使用它。 但如何生成应用并处理诸如路由器参数、已发布的正文以及返回比文本字符串更高级的数据之类的事项呢?

若要支持所谓的 RESTful API,需要支持使用 HTTP 谓词并将这些谓词附加到不同的路由。 不同的谓词具有不同的含义。 请遵守 HTTP 谓词的含义。

HTTP 谓词说明
GET返回数据
POST发送创建资源的数据
PUT发送更新资源的数据
DELETE删除资源

最小API中的HTTP谓词

当客户端发起请求时,它会向服务器终结点发起请求。 假设向 GET http://localhost:3000/products 发出请求。 服务器验证请求,以查看使用了什么 HTTP 谓词。 服务器还需要知道请求的去向,这由“/products”指示。然后,服务器会尝试通过生成响应来解析请求。

最小 API 通过提供便捷方法来处理路由和 HTTP 谓词。 可在对 localhost:3000/products 的请求中通过 HTTP 谓词和路由(其中路由为 "/products")来映射请求。 下面是此类便利方法的示例:

app.MapGet("/products", () => data);

应按以下方式解读此代码:如果客户端对路由 "/products" 使用 GET HTTP 谓词,则使用 data 进行响应。

GET:提取资源

使用 GET 请求进行路由时,需了解两个主要情况:

仅路由:已经见过此路由。 例如:

app.MapGet("/products", () => data);

使用路由参数:路由参数用于查找特定资源。 如果 "/products" 表示所有产品的列表,则 "/products/1" 表示特定记录。 唯一标识符的值为“1”。若要处理此类请求,请使用通配符来匹配它。 在前面的示例中,我们使用“{ID}”来捕获“1”。 还可以将捕获的值映射到参数:

app.MapGet("/products/{id}", (int id) => data.SingleOrDefault(product => product.Id == id));

在此代码中,id 参数已捕获客户端发送的路由参数,即“/products/1”中的“1”,或“11”,在此请求中为“/products/1”。 然后,使用 id 来查找特定记录。

POST:创建资源

通常还需要创建资源。 若要进行创建,请使用 POST HTTP 谓词。 要使用的方法称为 MapPost()

app.MapPost("/products", (Product product) => /**/);

请注意如何将 product 发送到处理请求的 Lambda 中。 假设下面的 JSON 是客户端发送并已由框架序列化的正文。 假设客户端已发送以下 JSON 作为其正文:

{
  "Name" : "New product",
  "Description": "a description"
}

那么,此 JSON 就可以将这些字段映射到相同形状的对象实例。 下面是与所述的已发布正文匹配的 Product 类:

public record Product(int Id, string Name);

PUT:更新资源

谓词 PUT 是指更新资源。 因此,框架使用方法 MapPut()。 MapPut() 方法在语义上类似于 MapPost()。 其思路是,作为客户端,发送的已发布正文应带有包含更改的资源。 需要将这些更改应用于服务器的现有资源。 下面介绍如何使用 MapPut()

app.MapPut("/products", (Product product) => /* Update the data store using the `product` instance */);

DELETE:删除资源

若要支持 HTTP 谓词 DELETE,请使用 MapDelete()。 这里的思路是让客户端通过独一无二的标识符进行发送,该标识符有助于服务器识别要删除的记录。 下面是此方法的典型用法:

app.MapDelete("/products/{id}", (int id) => /* Remove the record whose unique identifier matches `id` */);

返回响应

默认情况下,当使用某些类型进行响应时,框架会认识到这些类型应序列化为 JSON。 下面是一些用例:

app.MapGet("/products", () => products);
app.MapGet("/products/{id}", (int id) => products.SingleOrDefault(product => product.Id == id));
app.MapGet("/product", () => new { id = 1 });

对于这些用例,会收到类似于以下示例的 JSON 响应:

[{
  "id": 1,
  "name": "a product"
}, {
  "id": 2,
  "name": "another product"
}]

[{
  "id": 1,
  "name": "a product"
}]

{
  "id": 1,
}

添加路由

添加数据

首先需要一些数据。 你将使用内存中存储来存储和管理数据。

创建文件 Db.cs 并在其中提供以下内容:

namespace PizzaStore.DB; 

 public record Pizza 
 {
   public int Id {get; set;} 
   public string ? Name { get; set; }
 }

 public class PizzaDB
 {
   private static List<Pizza> _pizzas = new List<Pizza>()
   {
     new Pizza{ Id=1, Name="Montemagno, Pizza shaped like a great mountain" },
     new Pizza{ Id=2, Name="The Galloway, Pizza shaped like a submarine, silent but deadly"},
     new Pizza{ Id=3, Name="The Noring, Pizza shaped like a Viking helmet, where's the mead"} 
   };

   public static List<Pizza> GetPizzas() 
   {
     return _pizzas;
   } 

   public static Pizza ? GetPizza(int id) 
   {
     return _pizzas.SingleOrDefault(pizza => pizza.Id == id);
   } 

   public static Pizza CreatePizza(Pizza pizza) 
   {
     _pizzas.Add(pizza);
     return pizza;
   }

   public static Pizza UpdatePizza(Pizza update) 
   {
     _pizzas = _pizzas.Select(pizza =>
     {
       if (pizza.Id == update.Id)
       {
         pizza.Name = update.Name;
       }
       return pizza;
     }).ToList();
     return update;
   }

   public static void RemovePizza(int id)
   {
     _pizzas = _pizzas.FindAll(pizza => pizza.Id == id).ToList();
   }
 }

有了数据存储后,接下来让 API 使用它。

将数据连续到路由

将内存中存储连接到 API:

  1. 添加命名空间。 这种添加很简单,只需添加适当的 using 语句即可。
  2. 设置路由。 请确保添加创建、读取、更新和删除所需的所有路由映射。
  3. 在路由中调用它。 最后,需要根据每个路由调用内存中存储,并传入请求中的任何参数或正文(如果适用)。

现在,在 API 中连接数据。

在 Program.cs 文件顶部添加以下代码行:

using PizzaStore.DB;

在 app.Run() 前面添加以下代码:

app.MapGet("/pizzas/{id}", (int id) => PizzaDB.GetPizza(id));
app.MapGet("/pizzas", () => PizzaDB.GetPizzas());
app.MapPost("/pizzas", (Pizza pizza) => PizzaDB.CreatePizza(pizza));
app.MapPut("/pizzas", (Pizza pizza) => PizzaDB.UpdatePizza(pizza));
app.MapDelete("/pizzas/{id}", (int id) => PizzaDB.RemovePizza(id));

使用 dotnet run 运行应用:

dotnet run

在浏览器中转到 http://localhost:{PORT}/swagger

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值