• 如何使用FastEndpoints构建简洁、高性能的API
  • 发布于 1个月前
  • 67 热度
    0 评论
  • 阳光
  • 1 粉丝 29 篇博客
  •   
FastEndpoints是一款友好的,开源的,免费的可替换Minimal APIs和MVC的第三方库,是基于REPR(Request-Endpoint-Response)设计模式设计的API,方便且易于维护没有模板。性能方面几乎媲美Minimal APIs,低内存使用,每秒请求处理要比MVC快35K。FastEndpoints库提供了如下功能:认证和授权,模型绑定,验证,依赖注入,实体映射,文件处理,响应缓存,限流,预处理器,事件总线,命令总线,任务队列,远程调用等。
下面是官方给出的测评:

TechEmpower Benchmark (Preliminary)

我们通过一个例子来了解一下该库
1. 创建项目并安装FastEndpoints包
添加FastEndpoints包:

2. 添加服务以及中间件
使用下面代码替换Program.cs
// 堆代码 duidaima.com
using FastEndpoints;

var bld = WebApplication.CreateBuilder();
bld.Services.AddFastEndpoints();

var app = bld.Build();
app.UseFastEndpoints();
app.Run();
3. 添加Request DTO
创建一个MyRequest类:
public class MyRequest
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}
4. 添加Response DTO
创建一个MyResponse类
public class MyResponse
{
    public string FullName { get; set; }
    public bool IsOver18 { get; set; }
}
5. 添加一个终结点类
添加一个终结点类
using FastEndpoint.Libaray.Models;
using FastEndpoints;
public class MyEndpoint : Endpoint<MyRequest, MyResponse>
{
    public override void Configure()
{
        Post("/api/user/create");
        AllowAnonymous();
    }

    public override async Task HandleAsync(MyRequest req, CancellationToken ct)
{
        await SendAsync(new()
        {
            FullName = req.FirstName + " " + req.LastName,
            IsOver18 = req.Age > 18
        });
    }
}
运行应用程序向/api/user/create发送一个POST请求

响应

你只需要在Configure()方法中配置终结点如何监听从客户端进入的请求。例如:Get(), Post(), AllowAnonymous() 等,然后重写HandleAsync()方法指定你处理逻辑。

在这个例子中,请求DTO自动从HTTP请求的body中获取数据并且传递处理。处理之后,调用SendAsync()方法将新的DTO实例发送到客户端。
6. 终结点类型
你可以继承4种不同类型的endpoint
Endpoint<TRequest>如果只有一个请求DTO,你可以继承这个类型。
Endpoint<TRequest,TResponse> - 如果您同时具有请求和响应DTO,请继承此类型。
EndpointWithoutRequest - 如果没有请求或响应DTO,继承此类型。
EndpointWithoutRequest<TResponse> 如果没有请求DTO但存在响应DTO,请继承此类型。
你也可以使用自定义EmptyRequest和EmptyResponse定义一个终结点
public class MyEndpoint : Endpoint<EmptyRequest,EmptyResponse> { }
7. 发送响应
FastEndpoints提供了多种请求响应方法。您还可以简单地填充端点的 Response 属性,并自动在响应主体中序列化 Response 属性的值,从而获得一个 200 OK 的响应。例如:
public class MyEndpoint : EndpointWithoutRequest<MyResponse>
{
    public override void Configure()
    {
        Get("/api/person");
        AllowAnonymous();
    }

    public override async Task HandleAsync(CancellationToken ct)
    {
        var person = await dbContext.GetFirstPersonAsync();
        Response.FullName = person.FullName;
        Response.Age = person.Age;
    }
}
也可以使用另外一种方式:
public override Task HandleAsync(CancellationToken ct)
{
    Response = new()
    {
        FullName = "john doe",
        Age = 124
    };
    return Task.CompletedTask;
}
8. 返回多个类型
可以调用TypedResult类的静态方法来返回不同的结果类型。根据需要的结果类型,通过调用TypedResults类的静态方法实例化结果对象。
FastEndpoint也支持响应DTO类型设置为泛型联合多个参数,并覆盖ExecuteAsync(…)处理程序方法,而不是常用的HandleAsync(…)方法。
public class MyEndpoint : Endpoint<MyRequest, 
                                   Results<Ok<MyResponse>, 
                                           NotFound, 
                                           ProblemDetails>>
{
    public override void Configure() { ... }

    public override async Task<Results<Ok<MyResponse>, NotFound, ProblemDetails>> ExecuteAsync(
        MyRequest req, CancellationToken ct)
    {
        await Task.CompletedTask; //simulate async work

        if (req.Id == 0) //condition for a not found response
        {
            return TypedResults.NotFound();
        }

        if (req.Id == 1) //condition for a problem details response
        {
            AddError(r => r.Id, "value has to be greater than 1");
            return new FastEndpoints.ProblemDetails(ValidationFailures);
        }
        // 堆代码 duidaima.com
        // 200 ok response with a DTO
        return TypedResults.Ok(new MyResponse
        {
            RequestedId = req.Id
        });
    }
}
如果只有一个输出类型,设置终结点泛型参数TResponse为想要IResult类型
public class MyEndpoint : EndpointWithoutRequest<NotFound>
{
    public override void Configure() { ... }

    public override async Task<NotFound> ExecuteAsync(CancellationToken ct)
    {
        await Task.CompletedTask;
        return TypedResults.NotFound();
    }
}
如果你想使用HandleAsync()发送TypedResults,你可以使用下面方式:
public class MyEndpoint : Endpoint<MyRequest, Results<Ok<MyResponse>, NotFound>>
{
    public override void Configure() { ... }

    public override async Task HandleAsync(MyRequest r, CancellationToken c)
{
        if (true)
            await SendResultAsync(TypedResults.Ok<MyResponse>(new(){ ... }));
        else
            await SendResultAsync(TypedResults.NotFound());
    }
}
9. 使用Attributes进行配置
同样的,FastEndpoints也提供了attributes方式来配置终结点:
[Http{VERB}("/route")] - 指定请求动词和路由
[AllowAnonymous] - 允许匿名访问
[Authorize(...)] - 使用角色和策略指定授权
[PreProcessor<TProcessor>] - 在管道中添加一个前置处理器
[PostProcessor<TProcessor>] - 在管道中添加一个post处理器
[HttpPost("/my-endpoint")]
[Authorize(Roles = "Admin,Manager")]
[PreProcessor<MyProcessor>]
public class MyEndpoint : Endpoint<MyRequest, MyResponse>
{
    ...
}
你在任何终结点上设置的attribute都会自动添加到终结点元数据集合,这种方式和在Configure()使用如下方法时等同的:
Options(b => b.WithMetadata(new MyCustomAttribute()));
10. Cancellation Token
终结点的HandleAsync方法提供了一个CancellationToken参数,你可以在自己的业务方法中使用这个参数。CancellationToken参数对于终结点Send*Async方法可选,你不需要像下面这样手动提供一个CancellationToken参数:
  await SendAsync(response, cancellation: ct);
因为默认会提供一下相同的CancellationToken给SendAsync方法

Demo的Github地址:https://github.com/bingbing-gui/Asp.Net-Core-Skill/tree/master/Third-Party.Library/FastEndpoint.Library/FastEndpoint.Library
用户评论