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

相关推荐
桂月二二4 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
hunter2062066 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb6 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角6 小时前
CSS 颜色
前端·css
步、步、为营6 小时前
解锁.NET配置魔法:打造强大的配置体系结构
数据库·oracle·.net
九酒6 小时前
从UI稿到代码优化,看Trae AI 编辑器如何帮助开发者提效
前端·trae
浪浪山小白兔7 小时前
HTML5 新表单属性详解
前端·html·html5
lee5767 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm
2401_897579657 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter
limit for me8 小时前
react上增加错误边界 当存在错误时 不会显示白屏
前端·react.js·前端框架