You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core 6 Minimal API基于OpenApi的请求验证问题咨询

问题解答

1. 此场景下处理验证的最佳实践

在.NET 6 Minimal API中,默认不会自动触发System.ComponentModel.DataAnnotations的验证逻辑,这就是案例1中超范围w值能返回200的核心原因。推荐两种在请求进入路由处理前完成校验的方案:

方案一:添加端点过滤器(官方推荐轻量方式)

直接在目标端点上绑定验证逻辑,代码侵入性低:

app.MapPut("/resolutions", (Resolution res) =>
{
    return Results.Ok(new { Message = "Resource updated successfully" });
})
.AddEndpointFilter(async (context, next) =>
{
    var resolution = context.Arguments.OfType<Resolution>().FirstOrDefault();
    var validationContext = new ValidationContext(resolution);
    var validationResults = new List<ValidationResult>();
    
    if (!Validator.TryValidateObject(resolution, validationContext, validationResults, validateAllProperties: true))
    {
        var errorDict = validationResults
            .GroupBy(r => r.MemberNames.FirstOrDefault() ?? "Unknown")
            .ToDictionary(g => g.Key, g => g.Select(r => r.ErrorMessage).ToArray());
        
        return Results.ValidationProblem(errorDict);
    }
    
    return await next(context);
})
.Produces(200)
.Produces(400) // 补充声明400状态码
.Produces(404)
.WithName("PutResolution");

配置后,案例1的超范围请求会返回400状态码,同时携带具体的字段错误信息。

方案二:注册全局验证过滤器

如果多个端点都需要验证逻辑,可全局注册过滤器避免重复代码:

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// 注册全局验证过滤器
builder.Services.AddScoped<EndpointFilterDelegate>(sp =>
{
    return async (context, next) =>
    {
        foreach (var arg in context.Arguments)
        {
            if (arg == null) continue;
            
            var validationContext = new ValidationContext(arg);
            var validationResults = new List<ValidationResult>();
            
            if (!Validator.TryValidateObject(arg, validationContext, validationResults, validateAllProperties: true))
            {
                var errorDict = validationResults
                    .GroupBy(r => r.MemberNames.FirstOrDefault() ?? "Unknown")
                    .ToDictionary(g => g.Key, g => g.Select(r => r.ErrorMessage).ToArray());
                
                return Results.ValidationProblem(errorDict);
            }
        }
        return await next(context);
    };
});

// 后续端点无需单独配置验证
app.MapPut("/resolutions", (Resolution res) =>
{
    return Results.Ok(new { Message = "Resource updated successfully" });
})
.Produces(200)
.Produces(400)
.Produces(404)
.WithName("PutResolution");

2. 是否可以基于动态生成的OpenApi契约验证请求?

可以实现,但无需额外复杂配置——默认Swashbuckle会根据DataAnnotations自动生成包含范围、类型、必填约束的OpenApi Schema,通过上面的端点过滤器就能间接实现“基于契约验证”的效果(因为过滤器的规则和OpenApi Schema的约束同源)。

如果要直接基于OpenApi Schema做校验,可借助Json Schema验证库(如Newtonsoft.Json.Schema)配合中间件实现:

  1. 从Swagger文档中匹配当前请求对应的OpenApi Schema;
  2. 对请求Body执行Schema校验;
  3. 校验失败时返回400状态码和具体错误信息。

不过这种方式复杂度较高,大部分场景下,基于DataAnnotations的端点过滤器已经能满足需求。

另外,针对案例2的数据类型错误,可通过全局异常处理统一返回友好提示:

app.UseExceptionHandler(exceptionHandlerApp =>
{
    exceptionHandlerApp.Run(async context =>
    {
        var exception = context.Features.Get<IExceptionHandlerFeature>()?.Error;
        if (exception is JsonException)
        {
            context.Response.StatusCode = StatusCodes.Status400BadRequest;
            context.Response.ContentType = "application/json";
            await context.Response.WriteAsJsonAsync(new { Message = "请求数据类型错误,请检查参数格式" });
        }
    });
});

内容的提问来源于stack exchange,提问作者Fuczak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 10:12:41