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

.NET 8中如何基于Swashbuckle实现Swagger参数联动下拉?

实现Swagger中依赖路由参数的动态下拉列表

可行,但Swashbuckle本身没有开箱即用的功能,需要通过自定义文档过滤器+Swagger UI脚本扩展来实现。以下是具体实现步骤:

1. 定义参数依赖关系的自定义属性

首先创建一个自定义特性,用来标记param2的可选值与param1枚举值的关联:

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class DependentEnumAttribute : Attribute
{
    public Dictionary<Enum, string[]> DependentValues { get; }

    public DependentEnumAttribute(params object[] dependencies)
    {
        DependentValues = new Dictionary<Enum, string[]>();
        for (int i = 0; i < dependencies.Length; i += 2)
        {
            if (dependencies[i] is Enum key && dependencies[i + 1] is string[] values)
            {
                DependentValues[key] = values;
            }
        }
    }
}

在API控制器中使用该特性标记param2:

public enum MyEnum
{
    x,
    y,
    z
}

[ApiController]
[Route("[controller]")]
public class DemoController : ControllerBase
{
    [HttpGet("{param1}/{param2}")]
    public IActionResult Get(
        [FromRoute] MyEnum param1, 
        [FromRoute, DependentEnum(
            MyEnum.x, new[] { "1", "2", "3" },
            MyEnum.y, new[] { "4", "5", "6" },
            MyEnum.z, new[] { "7", "8", "9" }
        )] string param2)
    {
        return Ok(new { param1, param2 });
    }
}

2. 编写Swashbuckle文档过滤器

创建参数过滤器,将依赖关系写入OpenAPI规范的扩展字段中,供后续Swagger UI脚本读取:

public class DependentParameterFilter : IParameterFilter
{
    public void Apply(OpenApiParameter parameter, ParameterFilterContext context)
    {
        var dependentAttr = context.ParameterInfo.GetCustomAttribute<DependentEnumAttribute>();
        if (dependentAttr == null) return;

        // 将依赖序列化为JSON,存入OpenAPI扩展
        var dependencyMap = dependentAttr.DependentValues
            .ToDictionary(kvp => kvp.Key.ToString(), kvp => kvp.Value);
        var json = JsonSerializer.Serialize(dependencyMap);
        parameter.Extensions.Add("x-dependent-values", new OpenApiString(json));

        // 标记param2为字符串枚举,确保Swagger UI初始显示下拉(或输入框+数据列表)
        parameter.Schema.Type = "string";
        parameter.Schema.Enum = new List<IOpenApiAny>();
    }
}

在Program.cs中注册该过滤器:

builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Demo API", Version = "v1" });
    // 注册自定义参数过滤器
    c.ParameterFilter<DependentParameterFilter>();
});

3. 扩展Swagger UI实现动态切换

通过注入自定义JavaScript脚本,监听param1的选择变化,动态更新param2的可选值:

3.1 配置Swagger UI注入脚本

在Program.cs中修改Swagger UI配置,添加自定义脚本引用:

app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "Demo API v1");
    // 注入自定义脚本(需将脚本放在wwwroot目录下)
    c.InjectJavascript("/swagger-custom.js");
});

3.2 编写自定义脚本

在wwwroot目录下创建swagger-custom.js,实现动态切换逻辑:

window.addEventListener('load', () => {
    // 监听所有param1参数的变化事件
    document.addEventListener('change', (e) => {
        if (!e.target.name?.includes('param1')) return;

        const param1Value = e.target.value;
        // 定位对应的param2输入/选择元素
        const param2Container = e.target.closest('.parameter').nextElementSibling;
        if (!param2Container) return;
        const param2Element = param2Container.querySelector('select, input[type="text"]');
        if (!param2Element) return;

        // 获取当前接口的param2依赖配置
        const param2Config = getParam2Config(e.target.closest('.opblock'));
        if (!param2Config) return;

        const allowedValues = param2Config[param1Value] || [];
        updateParam2Options(param2Element, allowedValues);
    });

    // 初始化时根据默认param1值设置param2选项
    document.querySelectorAll('.parameter select[name*="param1"]').forEach(select => {
        if (select.value) {
            const event = new Event('change');
            select.dispatchEvent(event);
        }
    });

    // 辅助函数:获取当前接口的param2依赖配置
    function getParam2Config(opblock) {
        const operationId = opblock.querySelector('.opblock-summary-operation-id').textContent;
        const spec = window.swaggerUIRoot.spec;
        const pathKey = Object.keys(spec.paths).find(path => {
            const operation = spec.paths[path][opblock.dataset.method];
            return operation?.operationId === operationId;
        });
        if (!pathKey) return null;
        const param2 = spec.paths[pathKey][opblock.dataset.method].parameters.find(p => p.name === 'param2');
        return param2?.extensions['x-dependent-values'] ? JSON.parse(param2.extensions['x-dependent-values']) : null;
    }

    // 辅助函数:更新param2的可选值
    function updateParam2Options(element, values) {
        if (element.tagName === 'SELECT') {
            // 清空现有选项并添加新值
            element.innerHTML = '';
            values.forEach(val => {
                const option = document.createElement('option');
                option.value = val;
                option.textContent = val;
                element.appendChild(option);
            });
        } else if (element.tagName === 'INPUT') {
            // 使用datalist实现输入提示+可选值限制
            const datalistId = `dependent-options-${element.name}`;
            let datalist = document.getElementById(datalistId);
            if (!datalist) {
                datalist = document.createElement('datalist');
                datalist.id = datalistId;
                document.body.appendChild(datalist);
            }
            datalist.innerHTML = '';
            values.forEach(val => {
                const option = document.createElement('option');
                option.value = val;
                datalist.appendChild(option);
            });
            element.setAttribute('list', datalistId);
        }
    }
});

注意事项

  • Swagger UI的DOM结构可能随版本变化,若脚本失效需调整选择器逻辑。
  • 若参数是查询参数而非路由参数,需修改DOM定位逻辑。
  • 可根据需求扩展脚本,比如添加输入值校验,禁止输入不在可选列表中的值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 22:43:18