用于在 .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 实现自己的代码结构。

相关推荐
uhakadotcom31 分钟前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom35 分钟前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom36 分钟前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom1 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom1 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试
LaoZhangAI2 小时前
2025最全GPT-4o图像生成API指南:官方接口配置+15个实用提示词【保姆级教程】
前端
ONE_Gua2 小时前
chromium魔改——CDP(Chrome DevTools Protocol)检测01
前端·后端·爬虫
LaoZhangAI2 小时前
2025最全Cherry Studio使用MCP指南:8种强大工具配置方法与实战案例
前端
咖啡教室2 小时前
前端开发日常工作每日记录笔记(2019至2024合集)
前端·javascript
溪饱鱼2 小时前
Nuxt3能上生产吗?
前端