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

Swashbuckle/Swagger错误将路由参数纳入请求体问题解决

解决方案

方案一:自定义Schema过滤器,移除请求体中带[FromRoute]的属性

通过实现ISchemaFilter,在Swashbuckle生成Schema时自动移除请求体中标记为路由来源的属性,同时保留模型绑定所需的属性定义:

public class RemoveRoutePropertiesFromBodySchemaFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (context.Type == null) return;

        // 筛选出所有带[FromRoute]特性的属性
        var routeProperties = context.Type.GetProperties()
            .Where(p => p.GetCustomAttributes(typeof(FromRouteAttribute), inherit: true).Any());

        foreach (var prop in routeProperties)
        {
            // 转换为Swagger默认的驼峰命名格式
            var swaggerPropName = char.ToLowerInvariant(prop.Name[0]) + prop.Name.Substring(1);
            
            // 从Schema中移除该属性
            if (schema.Properties.ContainsKey(swaggerPropName))
            {
                schema.Properties.Remove(swaggerPropName);
                // 同步移除必填项标记(如果存在)
                if (schema.Required.Contains(swaggerPropName))
                {
                    schema.Required.Remove(swaggerPropName);
                }
            }
        }
    }
}

在Swashbuckle配置中注册该过滤器:

services.AddSwaggerGen(c =>
{
    c.SchemaFilter<RemoveRoutePropertiesFromBodySchemaFilter>();
    // 其他Swagger配置...
});

方案二:自定义操作过滤器,修正参数位置

如果需要进一步调整参数的显示位置(比如确保CourseId只作为路径参数存在,而非查询参数),可以搭配IOperationFilter修正参数的位置分类:

public class CorrectRouteParameterLocationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        if (context.ApiDescription.ParameterDescriptions == null) return;

        foreach (var paramDesc in context.ApiDescription.ParameterDescriptions)
        {
            var swaggerParam = operation.Parameters.FirstOrDefault(p => 
                p.Name.Equals(paramDesc.Name, StringComparison.OrdinalIgnoreCase));
            if (swaggerParam == null) continue;

            // 标记路由来源的参数为Path类型
            if (paramDesc.Source == BindingSource.Path)
            {
                swaggerParam.In = ParameterLocation.Path;
                swaggerParam.Schema.ReadOnly = true;
            }
            // 移除请求体模型中带[FromRoute]属性对应的查询参数
            else if (paramDesc.ParameterType.GetProperties().Any(p => 
                p.GetCustomAttributes(typeof(FromRouteAttribute), true).Any()))
            {
                var routeProps = paramDesc.ParameterType.GetProperties()
                    .Where(p => p.GetCustomAttributes(typeof(FromRouteAttribute), true).Any());
                
                foreach (var prop in routeProps)
                {
                    var queryParam = operation.Parameters.FirstOrDefault(p => 
                        p.Name.Equals(prop.Name, StringComparison.OrdinalIgnoreCase));
                    if (queryParam != null)
                    {
                        operation.Parameters.Remove(queryParam);
                    }
                }
            }
        }
    }
}

注册该操作过滤器:

services.AddSwaggerGen(c =>
{
    c.SchemaFilter<RemoveRoutePropertiesFromBodySchemaFilter>();
    c.OperationFilter<CorrectRouteParameterLocationFilter>();
    // 其他Swagger配置...
});

方案三:针对[FromComposite]绑定器的特殊处理

如果使用自定义[FromComposite]绑定器导致Swashbuckle误将请求体转为查询参数,可以通过操作过滤器强制将该参数标记为请求体,并清理路由属性:

public class CompositeBindingSourceOperationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var compositeParams = context.MethodInfo.GetParameters()
            .Where(p => p.GetCustomAttributes(typeof(FromCompositeAttribute), inherit: true).Any());

        foreach (var param in compositeParams)
        {
            // 移除自动生成的查询参数
            var queryParamsToRemove = operation.Parameters.Where(p => p.In == ParameterLocation.Query).ToList();
            foreach (var qp in queryParamsToRemove)
            {
                operation.Parameters.Remove(qp);
            }

            // 生成请求体Schema并移除路由属性
            var schema = context.SchemaGenerator.GenerateSchema(param.ParameterType, context.SchemaRepository);
            var routeProps = param.ParameterType.GetProperties()
                .Where(p => p.GetCustomAttributes(typeof(FromRouteAttribute), true).Any());
            
            foreach (var prop in routeProps)
            {
                var swaggerPropName = char.ToLowerInvariant(prop.Name[0]) + prop.Name.Substring(1);
                if (schema.Properties.ContainsKey(swaggerPropName))
                {
                    schema.Properties.Remove(swaggerPropName);
                    if (schema.Required.Contains(swaggerPropName))
                    {
                        schema.Required.Remove(swaggerPropName);
                    }
                }
            }

            // 添加请求体定义
            operation.RequestBody = new OpenApiRequestBody
            {
                Content = new Dictionary<string, OpenApiMediaType>
                {
                    ["application/json"] = new OpenApiMediaType
                    {
                        Schema = schema
                    }
                }
            };
        }
    }
}

注册后,Swashbuckle会正确识别[FromComposite]标记的参数为请求体,同时自动移除其中的路由属性字段。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 13:35:00