.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
相关产品推荐
相关产品推荐

