如何让Swashbuckle识别路由模板中未显式声明的参数类型?
解决Swagger未识别基控制器路由参数类型的问题
问题场景
我定义了基控制器TenantControllerBase,其预定义路由模板为api/s/{serviceId:int}/[controller],通过中间件提取serviceId并提供给所有继承该基类的控制器使用,无需在Action方法中显式声明该参数。但Swashbuckle生成的Swagger规范中,serviceId的类型被错误设置为string,而非路由模板指定的int。目前仅在Action方法签名中显式添加serviceId参数才能让Swagger识别其正确类型,希望找到无需添加参数的解决办法。
基控制器代码:
[Authorize] [ApiController] [Produces(MediaTypeNames.Application.Json)] public abstract class TenantControllerBase : ControllerBase { public const string DefaultRoute = "api/s/{serviceId:int}/[controller]"; private readonly ITenantContext _tenantContext; // 中间件中提取serviceId的值 protected int ServiceId => _tenantContext.ServiceId; protected TenantControllerBase(ITenantContext tenantContext) { _tenantContext = tenantContext ?? throw new ArgumentNullException(nameof(tenantContext)); } }
继承控制器示例:
[Route(DefaultRoute)] public class SomeTenantController : TenantControllerBase { public SomeTenantController(ITenantContext tenantContext) : base(tenantContext) { } [HttpGet("{type}")] public async Task<IActionResult> Get(string type, CancellationToken cancellationToken) { var serviceId = this.ServiceId; // 业务逻辑 return Ok(); } }
解决方案
方法一:自定义Swagger操作过滤器修正参数类型
Swashbuckle默认从Action方法的参数列表解析路由参数类型,对于基控制器中定义但未在Action中声明的路由参数,需要手动解析路由模板的约束并修正Swagger的参数元数据。
- 创建自定义
IOperationFilter:
using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using System.Text.RegularExpressions; public class RouteParameterTypeFixFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { var routeTemplate = context.ApiDescription.ActionDescriptor.AttributeRouteInfo?.Template; if (string.IsNullOrEmpty(routeTemplate)) return; // 匹配路由中的参数约束格式:{参数名:类型} var paramMatches = Regex.Matches(routeTemplate, @"{(\w+):(\w+)}"); foreach (Match match in paramMatches) { var paramName = match.Groups[1].Value; var paramTypeConstraint = match.Groups[2].Value; // 找到Swagger中对应的路径参数 var swaggerParam = operation.Parameters .FirstOrDefault(p => p.Name == paramName && p.In == ParameterLocation.Path); if (swaggerParam == null) continue; // 根据路由约束设置正确的Swagger类型和格式 swaggerParam.Schema.Type = paramTypeConstraint switch { "int" => "integer", "long" => "integer", "bool" => "boolean", "decimal" => "number", _ => swaggerParam.Schema.Type }; if (paramTypeConstraint is "int" or "long") { swaggerParam.Schema.Format = paramTypeConstraint == "int" ? "int32" : "int64"; } } } }
- 在Swagger配置中注册该过滤器:
builder.Services.AddSwaggerGen(c => { c.OperationFilter<RouteParameterTypeFixFilter>(); });
方法二:使用动态路由令牌转换器(.NET 5+)
通过自定义DynamicRouteValueTransformer,在路由解析阶段明确参数的类型,Swashbuckle会自动识别转换后的路由元数据,从而生成正确的参数类型定义。
- 创建路由转换器:
using Microsoft.AspNetCore.Routing; public class TenantRouteTransformer : DynamicRouteValueTransformer { public override ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values) { if (values.TryGetValue("serviceId", out var serviceIdObj) && int.TryParse(serviceIdObj.ToString(), out int serviceId)) { // 将字符串类型的serviceId转换为int,更新路由值字典 values["serviceId"] = serviceId; } return new ValueTask<RouteValueDictionary>(values); } }
- 注册路由转换器:
app.UseEndpoints(endpoints => { endpoints.MapControllers() .AddDynamicRouteValueTransformer<TenantRouteTransformer>(); });
效果验证
应用上述任一方案后,Swagger规范中serviceId的参数定义会自动修正为正确的类型:
{ "name": "serviceId", "in": "path", "required": true, "style": "simple", "schema": { "type": "integer", "format": "int32" } }
内容的提问来源于stack exchange,提问作者Mouad
相关产品推荐
相关产品推荐

