使用 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 实现自己的代码结构。
版权归原作者 晨曦_子画 所有, 如有侵权,请联系我们删除。