用于在 .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 包:

highlight 复制代码
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>dotnet add package FastEndpoints
</code></span></span>

以下是使用 FastEndpoints 创建 API 终端节点的方法:

csharp 复制代码
<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 定义端点:

csharp 复制代码
<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 提供了多种发送响应的方法,让我们来了解一下。

  1. 直接分配基类的属性,例如:Response``Endpoint
csharp 复制代码
<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>
  1. 直接返回类型:Response
csharp 复制代码
<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

  1. 在方法中使用 TypedResultsHandleAsync
csharp 复制代码
<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

  1. 在方法中使用 TypedResults 作为 Union-Type:ExecuteAsync
csharp 复制代码
<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" 端点实现:

csharp 复制代码
<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

highlight 复制代码
<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

csharp 复制代码
<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

csharp 复制代码
<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

highlight 复制代码
<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" 端点实现:

csharp 复制代码
<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

highlight 复制代码
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>GET /api/shipments/74119066
</code></span></span>

现在,我们来探索如何映射此 POST"更新货件状态"请求:

highlight 复制代码
<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

csharp 复制代码
<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

csharp 复制代码
<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 实现自己的代码结构。

相关推荐
2401_8576363917 分钟前
医疗服务品质提升:SSM 与 Vue 打造医院预约挂号系统方案
前端·javascript·vue.js
余生H1 小时前
前端的Python应用指南(一):快速构建 Web 服务器 - Flask vs Node.js 对比
服务器·前端·python
白瑕1 小时前
[JavaScript] 我该怎么去写一个canvas游戏
前端·javascript
m0_748248941 小时前
前端篇-Content-Type 详解
前端
m0_748255021 小时前
新手参加2025年CTF大赛——Web题目的基本解题流程
前端·网络·安全
憨憨小江1 小时前
Vue3 基础记录
前端
SANG嘻嘻嘻2 小时前
ES6中的map和set
前端·javascript·es6
fantasy_arch2 小时前
CPU性能优化--前端优化
前端·性能优化
粥里有勺糖2 小时前
视野修炼第114期 | 2024JS现状调查结果
前端·javascript·github
程序视点2 小时前
【安全漏洞】Vue UI库Vant组件遭恶意投毒,字节RspacK也中招!请紧急修复!
前端·vue.js·ui