场景
-
同一个 Controller 可能对不同环境有不同的行为,通过路由的 env 参数调整注入的参数
-
每一次请求上下文可能会根据 env 的值修改 repo 的具体参数
-
系统会提供默认的 env, 因此此处的路由参数为可选参数
-
swagger 根据 OpenApi v3.x 规范,即使使用 ? 标注为可选,ui 中仍然为 required
C#[Route("api/{env:env?}/[controller]")] [ApiController] public class DemoController(IDemoRepo repo) : ControllerBase { /* other codes */ }
解决方法
- 增加自定的方法修改已经生成的 swagger 文档
C#
builder.Services.AddSwaggerGen(opt =>
{
opt.OperationFilter<ApplyOptionalRouteParameterOperationFilter>();
/* other codes */
}
public partial class ApplyOptionalRouteParameterOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.Parameters.Count == 0) return;
// 获取方法以及类上面的 RouteAttribute
var attrs = context.MethodInfo.GetCustomAttributes(true)
.Concat(context.MethodInfo.DeclaringType?.GetCustomAttributes(true) ?? [])
.OfType<RouteAttribute>().Where(x => x.Template.Contains('?')).ToList();
// 遍历所有 RouteAttribute,如果包含可选参数,则对应修改 OpenAPI 参数定义
foreach (var route in attrs)
{
var matches = RouteTemplateRegex().Matches(route.Template);
foreach (Match match in matches)
{
var name = match.Groups["name"].Value;
var parameter = operation.Parameters.FirstOrDefault(x => x.Name == name);
if (parameter is not null)
{
parameter.AllowEmptyValue = true;
parameter.Required = false;
parameter.Schema.Nullable = true;
}
}
}
}
[GeneratedRegex(@"\{(?<name>\w+)(\:\w+)*\?\}")]
private static partial Regex RouteTemplateRegex();
}