Minimal API - 参数绑定

47 阅读1分钟

  • FromRoute 路由值
  • FromQuery url查询参数
  • FromHeader 请求标头
  • FromForm
  • FromBody
  • FromService 依赖注入提供的服务
  • custom 自定义
app.MapGet("/{id}", (int id, int page, [FromHeader(Name = "X-CUSTOM-HEADER")] string customHeader, IOrderService service) => {});
  • id 采用路由值绑定
  • page 采用查询参数绑定
  • customHeader 请求标头绑定
  • service 依赖注入

部分参数使用隐式绑定源,id、page customHeader、service。

AsParameters

这个可以理解为结构,当方法的参数绑定过多,或者参数可以复用如分页查询参数,可以使用classrecordstruct 包装。

public record OrderPageQueryParams(int page, int take, IOrderServcie service);

app.MapGet("orders", [AsParameters](OrderPageQueryParams queryParams) => {});

官方文档:将 struct 和 AsParameters 一起使用可能比使用 record 类型性能更佳

自定义绑定 官方文档

TryParse

TryParse 具有以下 API :

public static bool TryParse(string value, out T result);
public static bool TryParse(string value, IFormatProvider provider, out T result);

下面的代码显示带有 URI /map?Point=121.23,31.23 的 Point=121.23,31.23

app.MapGet("/map", (Point point) => $"Point: {point.X}, {point.Y}");

public class Point
{
    public double X { get; set;}
    public double Y { get; set;}
    
    public static bool TryParse(string? value, IFormatProvider? provider, out Point point)
    {
        var trimmedValue = value?.TrimStart('(').TrimEnd(')');
        var segments = trimmedValue?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        if (segments?.Length == 2 && double.TryParse(segments[0], out var x) && double.TryParse(segments[1], out var y)) 
        { 
            point = new Point { X = x, Y = y }; 
            return true; 
        } 
        point = null; 
        return false;
    }
}

BindAsync

BindAsync 具有以下 API :

public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo parameter);
public static ValueTask<T?> BindAsync(HttpContext context);

下面的代码显示带有 URI /products?SortBy=xyz&SortDir=Desc&Page=99 的 SortBy:xyz, SortDirection:Desc, CurrentPage:99

app.MapGet("/products", (PagingData pageData) => $"SortBy:{pageData.SortBy}, " + $"SortDirection:{pageData.SortDirection}, CurrentPage:{pageData.CurrentPage}");

public class PagingData 
{ 
    public string? SortBy { get; init; } 
    public SortDirection SortDirection { get; init; } 
    public int CurrentPage { get; init; } = 1; 
    public static ValueTask<PagingData?> BindAsync(HttpContext context, ParameterInfo parameter) 
    { 
        const string sortByKey = "sortBy"; 
        const string sortDirectionKey = "sortDir"; 
        const string currentPageKey = "page"; 
        
        Enum.TryParse<SortDirection(context.Request.Query[sortDirectionKey], ignoreCase: true, out var sortDirection); 
        int.TryParse(context.Request.Query[currentPageKey], out var page); 
        page = page == 0 ? 1 : page; 
        
        var result = new PagingData { 
            SortBy = context.Request.Query[sortByKey], 
            SortDirection = sortDirection, 
            CurrentPage = page }; 
            
        return ValueTask.FromResult<PagingData?>(result); 
  } 
} 

public enum SortDirection 
{ 
    Default, 
    Asc, 
    Desc 
}

绑定优先级

  • 绑定源规则
    • FromRoute
    • FormQuery
    • FromHeader
    • FromBody
    • FromForm
    • FromServices
    • AsParameters
  • 特殊类型
  • 参数类型具有有效的静态BindAsync方法
  • 参数类型为字符串或具有有效的静态 TryParse 方法
  • 如果参数类型为依赖项注入提供的服务,则它将该服务用作源
  • 参数来自正文