.NET 8 Minimal API中AsParameters无法加载查询字符串嵌套对象
问题:Minimal API结合MediatR时嵌套属性无法通过查询字符串绑定
问题场景
使用的查询字符串:
/api/Relogio?Name=&ProductRef.Code=asdsadsadsadsadasdsasadsa&ProductRef.Year=12&CurrentPage=1&PageSize=15
简单属性(如Name、CurrentPage)能正常赋值,但嵌套的ProductRef.Code、ProductRef.Year无法填充到请求对象的ProductRef属性中。
相关代码
请求类定义:
public class GetAllProductsRequest : IRequest<PaginatedList<GetProductResponse>> { public string? Name { get; set; } public ProductRef? ProductRef { get; set; } // 该对象无法被赋值 public int CurrentPage { get; set; } = 1; public int PageSize { get; set; } = 15; }
ProductRef类定义:
public class ProductRef { public string Code { get; set; } = string.Empty; public int? Year { get; set; } }
Minimal API Action:
group.MapGet("/", async (IMediator mediator, [AsParameters] GetAllProductsRequest request) => { var result = await mediator.Send(request); return result; });
使用环境:.NET 8.0.2 x64
原因
.NET Minimal API的[AsParameters]特性绑定逻辑仅支持扁平化的顶级属性映射,不会自动处理嵌套引用类型的实例化与属性填充。它的设计目标是将多个独立参数合并到一个对象中,而非处理复杂的嵌套模型绑定场景,因此无法识别ProductRef.Code这类点符号的嵌套参数结构。
解决方案
方案1:手动提取并赋值嵌套属性
直接从HttpRequest中获取查询参数,手动实例化ProductRef并赋值:
group.MapGet("/", async (IMediator mediator, [AsParameters] GetAllProductsRequest request, HttpRequest httpRequest) => { request.ProductRef = new ProductRef { Code = httpRequest.Query["ProductRef.Code"].ToString(), Year = int.TryParse(httpRequest.Query["ProductRef.Year"], out var year) ? year : null }; var result = await mediator.Send(request); return result; });
方案2:移除[AsParameters],使用默认模型绑定
去掉[AsParameters]特性,让Minimal API使用默认的模型绑定机制,该机制支持点符号的嵌套属性绑定:
group.MapGet("/", async (IMediator mediator, GetAllProductsRequest request) => { var result = await mediator.Send(request); return result; });
注:此方式要求嵌套类(ProductRef)存在无参构造函数(你的代码已满足,因为属性有默认值初始化),模型绑定会自动实例化嵌套对象并填充属性。
方案3:自定义绑定器
实现IBinder接口,为GetAllProductsRequest编写自定义绑定逻辑,处理嵌套属性的解析:
public class GetAllProductsRequestBinder : IBinder { public async ValueTask BindAsync(BinderContext context) { var query = context.HttpContext.Request.Query; var request = new GetAllProductsRequest { Name = query["Name"], CurrentPage = int.TryParse(query["CurrentPage"], out var page) ? page : 1, PageSize = int.TryParse(query["PageSize"], out var size) ? size : 15, ProductRef = new ProductRef { Code = query["ProductRef.Code"], Year = int.TryParse(query["ProductRef.Year"], out var year) ? year : null } }; context.Result = ModelBindingResult.Success(request); } }
然后在Action中使用自定义绑定器:
group.MapGet("/", async (IMediator mediator, [BinderType(typeof(GetAllProductsRequestBinder))] GetAllProductsRequest request) => { var result = await mediator.Send(request); return result; });
内容的提问来源于stack exchange,提问作者Ricardo
相关产品推荐
相关产品推荐

