用于在 .NET 中构建 Web API 的 FastEndpoints 入门
使用 ASP.NET Core 构建 Web API 可能涉及大量样板代码,尤其是在处理控制器、路由和模型绑定时。
FastEndpoints 是一个轻量级库,可简化此过程,允许您使用最少的代码和出色的性能定义终端节点。
在这篇博文中,我们将探讨如何开始使用 FastEndpoints。
我将向您展示如何创建 API 端点、处理请求、响应和添加验证。
什么是 FastEndpoints?
FastEndpoints 是一个适用于 .NET 的开源库,它通过消除对控制器和路由属性的需求来简化 Web API 的创建。
它基于 ASP.NET Core Minimal API 构建,利用所有性能优势,同时提供更直接的编程模型。
在 Minimal API 中,您需要定义自己希望如何构建终端节点,如何将它们分组或不分组到单个文件中。
在 FastEndpoints 中,您可以在单独的类中定义每个终端节点,从而生成一个 Responsible 且可维护的终端节点。
对我来说,这个概念非常适合 Vertical Slice Architecture。
FastEndpoints 遵循 REPR 设计模式 (Request-Endpoint-Response),并为 Web API 开发提供以下优势:
- 简单性:通过允许您将端点定义为单个类来降低复杂性
- 性能:针对速度进行了优化,提供更好的吞吐量和更低的延迟
- 可维护性:更简洁的代码结构使维护和扩展应用程序变得更加容易
- 快速开发:更快地设置和开始构建 API,提高生产力
FastEndpoints 入门
要开始使用 FastEndpoints,您需要创建一个 WebApi 项目并添加以下 Nuget 包:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>dotnet add package FastEndpoints
</code></span></span>
以下是使用 FastEndpoints 创建 API 终端节点的方法:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">record</span> <span style="color:var(--syntax-name-color)">RegisterUserRequest</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">string</span> <span style="color:var(--syntax-text-color)">Email</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-declaration-color)">string</span> <span style="color:var(--syntax-text-color)">Password</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-declaration-color)">string</span> <span style="color:var(--syntax-text-color)">Name</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">record</span> <span style="color:var(--syntax-name-color)">RegisterUserResponse</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">Guid</span> <span style="color:var(--syntax-text-color)">Id</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-declaration-color)">string</span> <span style="color:var(--syntax-text-color)">Email</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-declaration-color)">string</span> <span style="color:var(--syntax-text-color)">Name</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">class</span> <span style="color:var(--syntax-name-color)">CreateUserEndpoint</span> <span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">Endpoint</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">RegisterUserRequest</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">RegisterUserResponse</span><span style="color:var(--syntax-text-color)">></span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">void</span> <span style="color:var(--syntax-name-color)">Configure</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">Post</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"/users/register"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-name-color)">AllowAnonymous</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">HandleAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">RegisterUserRequest</span> <span style="color:var(--syntax-text-color)">request</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">CancellationToken</span> <span style="color:var(--syntax-text-color)">token</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">SendAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">RegisterUserResponse</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">Guid</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">NewGuid</span><span style="color:var(--syntax-text-color)">(),</span> <span style="color:var(--syntax-string-color)">"email"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-string-color)">"name"</span><span style="color:var(--syntax-text-color)">));</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
您需要定义请求、响应模型和继承自 base 的类。
在方法中,您可以指定:Endpoint<TRequest, TResponse>Configure
- HTTP 方法类型
- 终端节点 URL
- 额外属性:如 authentication、authorization、allow anonymous、versioning、rate limiting 等。
FastEndpoints 中的终端节点类型
FastEndpoints 提供 4 种终端节点基本类型,您可以从中继承:
- Endpoint - 如果只有一个请求 DTO,请使用此类型。但是,您可以将任何对象发送到客户端,这些对象可以使用此泛型重载序列化为响应。
- Endpoint - 如果您同时具有请求和响应 DTO,请使用此类型。这种泛型重载的好处是,在执行集成测试和验证时,您可以获得对 DTO 属性的强类型访问。
- EndpointWithoutRequest - 如果没有请求或响应 DTO,请使用此类型。您也可以在此处发送任何可序列化对象作为响应。
- EndpointWithoutRequest - 如果没有请求 DTO 但有响应 DTO,请使用此类型。
如果需要,还可以使用 EmptyRequest 和 EmptyResponse 定义端点:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">class</span> <span style="color:var(--syntax-name-color)">Endpoint</span> <span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">Endpoint</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">EmptyRequest</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-text-color)">EmptyResponse</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
在 FastEndpoints 中发送响应
FastEndpoints 提供了多种发送响应的方法,让我们来了解一下。
- 直接分配基类的属性,例如:
Response
Endpoint
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">class</span> <span style="color:var(--syntax-name-color)">CreateUserEndpoint</span> <span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">Endpoint</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">RegisterUserRequest</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">RegisterUserResponse</span><span style="color:var(--syntax-text-color)">></span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">void</span> <span style="color:var(--syntax-name-color)">Configure</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">Post</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"/users/register"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-name-color)">AllowAnonymous</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">HandleAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">RegisterUserRequest</span> <span style="color:var(--syntax-text-color)">request</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">CancellationToken</span> <span style="color:var(--syntax-text-color)">token</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">Response</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">RegisterUserResponse</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">Guid</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">NewGuid</span><span style="color:var(--syntax-text-color)">(),</span> <span style="color:var(--syntax-string-color)">"email"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-string-color)">"name"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">CompletedTask</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
- 直接返回类型:
Response
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">class</span> <span style="color:var(--syntax-name-color)">CreateUserEndpoint</span> <span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">Endpoint</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">RegisterUserRequest</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">RegisterUserResponse</span><span style="color:var(--syntax-text-color)">></span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">void</span> <span style="color:var(--syntax-name-color)">Configure</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">Post</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"/users/register"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-name-color)">AllowAnonymous</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">HandleAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">RegisterUserRequest</span> <span style="color:var(--syntax-text-color)">request</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">CancellationToken</span> <span style="color:var(--syntax-text-color)">token</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">SendAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">RegisterUserResponse</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">Guid</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">NewGuid</span><span style="color:var(--syntax-text-color)">(),</span> <span style="color:var(--syntax-string-color)">"email"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-string-color)">"name"</span><span style="color:var(--syntax-text-color)">));</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
在这里,您需要将响应模型直接传递给 base 方法。SendAsync
- 在方法中使用 TypedResults:
HandleAsync
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">class</span> <span style="color:var(--syntax-name-color)">CreateUserEndpoint</span> <span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">Endpoint</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">RegisterUserRequest</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">RegisterUserResponse</span><span style="color:var(--syntax-text-color)">></span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">void</span> <span style="color:var(--syntax-name-color)">Configure</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">Post</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"/users/register"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-name-color)">AllowAnonymous</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">HandleAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">RegisterUserRequest</span> <span style="color:var(--syntax-text-color)">request</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">CancellationToken</span> <span style="color:var(--syntax-text-color)">token</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(...)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">SendResultAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">TypedResults</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">BadRequest</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Email already exists"</span><span style="color:var(--syntax-text-color)">));</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">response</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">RegisterUserResponse</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">Guid</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">NewGuid</span><span style="color:var(--syntax-text-color)">(),</span> <span style="color:var(--syntax-string-color)">"email"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-string-color)">"name"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">SendResultAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">TypedResults</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Ok</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">response</span><span style="color:var(--syntax-text-color)">));</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
这里你需要将相应的响应模型传递给 base 方法。TypedResultsSendResultAsync
- 在方法中使用 TypedResults 作为 Union-Type:
ExecuteAsync
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">class</span> <span style="color:var(--syntax-name-color)">CreateUserEndpoint</span>
<span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">Endpoint</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">RegisterUserRequest</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">Results</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Ok</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">RegisterUserResponse</span><span style="color:var(--syntax-text-color)">>,</span> <span style="color:var(--syntax-text-color)">BadRequest</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">>>></span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">void</span> <span style="color:var(--syntax-name-color)">Configure</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">Post</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"/users/register"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-name-color)">AllowAnonymous</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Results</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Ok</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">RegisterUserResponse</span><span style="color:var(--syntax-text-color)">>,</span> <span style="color:var(--syntax-text-color)">BadRequest</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">>>></span> <span style="color:var(--syntax-name-color)">ExecuteAsync</span><span style="color:var(--syntax-text-color)">(</span>
<span style="color:var(--syntax-text-color)">RegisterUserRequest</span> <span style="color:var(--syntax-text-color)">request</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">CancellationToken</span> <span style="color:var(--syntax-text-color)">token</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(...)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-text-color)">TypedResults</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">BadRequest</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Email already exists"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">response</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">RegisterUserResponse</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">Guid</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">NewGuid</span><span style="color:var(--syntax-text-color)">(),</span> <span style="color:var(--syntax-string-color)">"email"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-string-color)">"name"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-text-color)">TypedResults</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Ok</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">response</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
在这种情况下,您需要使用 method 而不是 .
您需要指定将返回的所有方法。
如果尝试返回错误的类型 - 将引发编译错误。ExecuteAsyncHandleAsyncTypedResults
在实际应用程序中使用 FastEndpoints
今天,我将向您展示如何将 FastEndpoints 用于负责为已订购产品创建和更新货件的运输应用程序。
此应用程序有 3 个 Web API 端点:
- 创建货件
- 更新货件状态
- 按编号获取货件
让我们探索一下 POST “Create Shipment” 端点实现:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">sealed</span> <span style="color:var(--syntax-declaration-color)">record</span> <span style="color:var(--syntax-name-color)">CreateShipmentRequest</span><span style="color:var(--syntax-text-color)">(</span>
<span style="color:var(--syntax-declaration-color)">string</span> <span style="color:var(--syntax-text-color)">OrderId</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">Address</span> <span style="color:var(--syntax-text-color)">Address</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-declaration-color)">string</span> <span style="color:var(--syntax-text-color)">Carrier</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-declaration-color)">string</span> <span style="color:var(--syntax-text-color)">ReceiverEmail</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">List</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">ShipmentItem</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-text-color)">Items</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">class</span> <span style="color:var(--syntax-name-color)">CreateShipmentEndpoint</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">IShipmentRepository</span> <span style="color:var(--syntax-text-color)">repository</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">ILogger</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">CreateShipmentEndpoint</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-text-color)">logger</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">Endpoint</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">CreateShipmentRequest</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">Results</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Ok</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">ShipmentResponse</span><span style="color:var(--syntax-text-color)">>,</span> <span style="color:var(--syntax-text-color)">Conflict</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">>>></span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">void</span> <span style="color:var(--syntax-name-color)">Configure</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">Post</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"/api/shipments"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-name-color)">AllowAnonymous</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Results</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Ok</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">ShipmentResponse</span><span style="color:var(--syntax-text-color)">>,</span> <span style="color:var(--syntax-text-color)">Conflict</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">>>></span> <span style="color:var(--syntax-name-color)">ExecuteAsync</span><span style="color:var(--syntax-text-color)">(</span>
<span style="color:var(--syntax-text-color)">CreateShipmentRequest</span> <span style="color:var(--syntax-text-color)">request</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">CancellationToken</span> <span style="color:var(--syntax-text-color)">cancellationToken</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">shipmentAlreadyExists</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">repository</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">ExistsAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">request</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">OrderId</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">cancellationToken</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">shipmentAlreadyExists</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">logger</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">LogInformation</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Shipment for order '{OrderId}' is already created"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">request</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">OrderId</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-text-color)">TypedResults</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Conflict</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">$"Shipment for order '</span><span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-text-color)">request</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">OrderId</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-string-color)">' is already created"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">shipmentNumber</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">Faker</span><span style="color:var(--syntax-text-color)">().</span><span style="color:var(--syntax-text-color)">Commerce</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Ean8</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">shipment</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">request</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">MapToShipment</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">shipmentNumber</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">repository</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">AddAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">shipment</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">cancellationToken</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">logger</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">LogInformation</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Created shipment: {@Shipment}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">shipment</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">response</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">shipment</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">MapToResponse</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-text-color)">TypedResults</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Ok</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">response</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
此处 FastEndpoints 会自动将请求的 JSON 正文绑定到模型:CreateShipmentRequest
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">"number"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">"10000001"</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">"orderId"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">"11100001"</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">"carrier"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">"Modern Delivery"</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">"receiverEmail"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">"TODO: SET EMAIL HERE"</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">"address"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">"street"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">"123 Main St"</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">"city"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">"Springfield"</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">"zip"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">"12345"</span>
<span style="color:var(--syntax-text-color)">},</span>
<span style="color:var(--syntax-text-color)">"items"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">[</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">"product"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">"Acer Nitro 5"</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">"quantity"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">7</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">]</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
对于返回我使用并在终端节点中指定的响应:TypedResults.ConflictTypedResults.Ok
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">class</span> <span style="color:var(--syntax-name-color)">CreateShipmentEndpoint</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">IShipmentRepository</span> <span style="color:var(--syntax-text-color)">repository</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">ILogger</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">CreateShipmentEndpoint</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-text-color)">logger</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">Endpoint</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">CreateShipmentRequest</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">Results</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Ok</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">ShipmentResponse</span><span style="color:var(--syntax-text-color)">>,</span> <span style="color:var(--syntax-text-color)">Conflict</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">>>></span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Results</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Ok</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">ShipmentResponse</span><span style="color:var(--syntax-text-color)">>,</span> <span style="color:var(--syntax-text-color)">Conflict</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">>>></span> <span style="color:var(--syntax-name-color)">ExecuteAsync</span><span style="color:var(--syntax-text-color)">(</span>
<span style="color:var(--syntax-text-color)">CreateShipmentRequest</span> <span style="color:var(--syntax-text-color)">request</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">CancellationToken</span> <span style="color:var(--syntax-text-color)">cancellationToken</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
这可确保您从终端节点返回正确的类型;否则将引发编译错误。
对于验证,FastEndpoints 具有对 FluentValidation 的内置支持。
您需要创建从基类继承的验证器:Validator
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">class</span> <span style="color:var(--syntax-name-color)">CreateShipmentRequestValidator</span> <span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">Validator</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">CreateShipmentRequest</span><span style="color:var(--syntax-text-color)">></span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-name-color)">CreateShipmentRequestValidator</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">RuleFor</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">shipment</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-text-color)">shipment</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">OrderId</span><span style="color:var(--syntax-text-color)">).</span><span style="color:var(--syntax-name-color)">NotEmpty</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-name-color)">RuleFor</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">shipment</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-text-color)">shipment</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Carrier</span><span style="color:var(--syntax-text-color)">).</span><span style="color:var(--syntax-name-color)">NotEmpty</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-name-color)">RuleFor</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">shipment</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-text-color)">shipment</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">ReceiverEmail</span><span style="color:var(--syntax-text-color)">).</span><span style="color:var(--syntax-name-color)">NotEmpty</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-name-color)">RuleFor</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">shipment</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-text-color)">shipment</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Items</span><span style="color:var(--syntax-text-color)">).</span><span style="color:var(--syntax-name-color)">NotEmpty</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-name-color)">RuleFor</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">shipment</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-text-color)">shipment</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Address</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Cascade</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">CascadeMode</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Stop</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">NotNull</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">WithMessage</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Address must not be null"</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">SetValidator</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">AddressValidator</span><span style="color:var(--syntax-text-color)">());</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
调用 “Create” 终端节点时,FastEndpoint 将自动执行模型验证,并按以下格式返回:BadRequest
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">"StatusCode"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">400</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">"Message"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">"One or more errors occured!"</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">"Errors"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">"ReceiverEmail"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-string-color)">"Email is required!"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-string-color)">"Email is invalid!"</span><span style="color:var(--syntax-text-color)">],</span>
<span style="color:var(--syntax-text-color)">"Carrier"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-string-color)">"Carrier is required!"</span><span style="color:var(--syntax-text-color)">]</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
让我们探索一下 GET “Get Shipment by Number” 端点实现:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">record</span> <span style="color:var(--syntax-name-color)">GetShipmentByNumberRequest</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">string</span> <span style="color:var(--syntax-text-color)">ShipmentNumber</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">class</span> <span style="color:var(--syntax-name-color)">GetShipmentByNumberEndpoint</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">IShipmentRepository</span> <span style="color:var(--syntax-text-color)">repository</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">ILogger</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">GetShipmentByNumberEndpoint</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-text-color)">logger</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">Endpoint</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">GetShipmentByNumberRequest</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">ShipmentResponse</span><span style="color:var(--syntax-text-color)">></span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">void</span> <span style="color:var(--syntax-name-color)">Configure</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">Get</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"/api/shipments/{ShipmentNumber}"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-name-color)">AllowAnonymous</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">HandleAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">GetShipmentByNumberRequest</span> <span style="color:var(--syntax-text-color)">request</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">CancellationToken</span> <span style="color:var(--syntax-text-color)">cancellationToken</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">shipment</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">repository</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">GetByNumberWithItemsAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">request</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">ShipmentNumber</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">cancellationToken</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">shipment</span> <span style="color:var(--syntax-declaration-color)">is</span> <span style="color:var(--syntax-declaration-color)">null</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">logger</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">LogDebug</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Shipment with number {ShipmentNumber} not found"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">request</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">ShipmentNumber</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">SendNotFoundAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">cancellationToken</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">return</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">response</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">shipment</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">MapToResponse</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">SendAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">response</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">cancellation</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">cancellationToken</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
这里 FastEndpoints 会自动将 route 参数绑定到模型:GetShipmentByNumberRequest
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>GET /api/shipments/74119066
</code></span></span>
现在,我们来探索如何映射此 POST“更新货件状态”请求:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>POST /api/shipments/update-status/74119066
Content-Type: application/json
{
"status": "WaitingCustomer"
}
</code></span></span>
ShipmentStatus 是请求的 JSON 正文的一部分,它映射到:UpdateShipmentStatusRequest
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">sealed</span> <span style="color:var(--syntax-declaration-color)">record</span> <span style="color:var(--syntax-name-color)">UpdateShipmentStatusRequest</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">ShipmentStatus</span> <span style="color:var(--syntax-text-color)">Status</span><span style="color:var(--syntax-text-color)">);</span>
</code></span></span>
您可以在 or 方法中获取的路线参数 “ShipmentNumber”:ExecuteAsyncHandleAsync
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">void</span> <span style="color:var(--syntax-name-color)">Configure</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">Post</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"/api/shipments/update-status/{ShipmentNumber}"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-name-color)">AllowAnonymous</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Results</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">NoContent</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">NotFound</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">>>></span> <span style="color:var(--syntax-name-color)">ExecuteAsync</span><span style="color:var(--syntax-text-color)">(</span>
<span style="color:var(--syntax-text-color)">UpdateShipmentStatusRequest</span> <span style="color:var(--syntax-text-color)">request</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">CancellationToken</span> <span style="color:var(--syntax-text-color)">cancellationToken</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">shipmentNumber</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">Route</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">>(</span><span style="color:var(--syntax-string-color)">"ShipmentNumber"</span><span style="color:var(--syntax-text-color)">)!;</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
总结
FastEndpoints 是一个出色的库,可简化 Web API 实现,允许您使用最少的代码定义终端节点,并具有出色的性能。
FastEndpoints 为您的终端节点提供了现成的代码结构,设计精美,因此您无需使用最少的 API 实现自己的代码结构。