不要在 Swashbuckle (Swagger) 中显示端点
Don't show endpoints in Swashbuckle (Swagger)
我在 SwaggerUI 中遇到显示端点问题。
例如
[ApiVersion("5.0")]
[RoutePrefix("mobile/v{version:apiVersion}/persons")]
public class LessonController : ApiController
{
-------------------------------------
[HttpGet]
[Route("{personId}/groups/{groupId}/lessons/{lessonId}/lessonDetails")]
public LessonDetailsModel LessonDetails(long personId, long groupId, long lessonId)
{
----------------------------------
它的端点不显示,但如果我将 RoutePrefix 更改为
[RoutePrefix("mobile/v{version:apiVersion}/persons1")]
端点展示
更新:
Swagger配置
GlobalConfiguration.Configuration
.EnableSwagger("mobile/swagger/docs/{apiVersion}", c => {
c.MultipleApiVersions(
ResolveVersionSupportByRouteConstraint,
(vc) =>
{
vc.Version("v5.0", "Api version 5");
vc.Version("v4.0", "Api version 4");
------------------------------------------------------
});
c.UseFullTypeNameInSchemaIds();
c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
c.OperationFilter<RemoveVersionParameters>();
c.OperationFilter<InjectCustomApiParameters>();
c.DocumentFilter<SetVersionInPaths>();
c.IncludeXmlComments(Path.ChangeExtension($"{Assembly.GetExecutingAssembly().GetName().CodeBase}", ".xml"));
})
.EnableSwaggerUi("mobile/swagger/ui/{*assetPath}", c => {
c.DocumentTitle("Mobile Api Swagger");
c.DisableValidator();
c.EnableDiscoveryUrlSelector();
});
我找到了解决问题的方法。我实现了 IApiExplorer。
public class VersionedApiExplorer : IApiExplorer
{
private readonly IApiExplorer _innerApiExplorer;
private readonly HttpConfiguration _configuration;
private readonly Lazy<Collection<ApiDescription>> _apiDescriptions;
private MethodInfo _apiDescriptionPopulator;
public VersionedApiExplorer(IApiExplorer apiExplorer, HttpConfiguration configuration)
{
_innerApiExplorer = apiExplorer;
_configuration = configuration;
_apiDescriptions = new Lazy<Collection<ApiDescription>>(Init);
}
public Collection<ApiDescription> ApiDescriptions => _apiDescriptions.Value;
private Collection<ApiDescription> Init()
{
var descriptions = _innerApiExplorer.ApiDescriptions;
var controllerSelector = _configuration.Services.GetHttpControllerSelector();
var controllerMappings = controllerSelector.GetControllerMapping();
var flatRoutes = FlattenRoutes(_configuration.Routes).ToList();
var result = new Collection<ApiDescription>();
foreach (var description in descriptions)
{
result.Add(description);
if (controllerMappings == null || !description.Route.Constraints.Any()) continue;
var matchingRoutes = flatRoutes.Where(r =>
r.RouteTemplate == description.Route.RouteTemplate && r != description.Route);
foreach (var route in matchingRoutes)
GetRouteDescriptions(route, result);
}
return result;
}
private void GetRouteDescriptions(IHttpRoute route, IReadOnlyCollection<ApiDescription> apiDescriptions)
{
if (route.DataTokens["actions"] is IEnumerable<HttpActionDescriptor> actionDescriptor &&
actionDescriptor.Any())
GetPopulateMethod().Invoke(_innerApiExplorer,
new object[] {actionDescriptor.First(), route, route.RouteTemplate, apiDescriptions});
}
private MethodInfo GetPopulateMethod()
{
if (_apiDescriptionPopulator == null)
_apiDescriptionPopulator = _innerApiExplorer.GetType()
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(
m => m.Name == "PopulateActionDescriptions" && m.GetParameters().Length == 4);
return _apiDescriptionPopulator;
}
public static IEnumerable<IHttpRoute> FlattenRoutes(IEnumerable<IHttpRoute> routes)
{
foreach (var route in routes)
{
switch (route)
{
case HttpRoute _:
yield return route;
break;
case IReadOnlyCollection<IHttpRoute> subRoutes:
{
foreach (var subRoute in FlattenRoutes(subRoutes))
yield return subRoute;
break;
}
}
}
}
}
我在 SwaggerUI 中遇到显示端点问题。 例如
[ApiVersion("5.0")]
[RoutePrefix("mobile/v{version:apiVersion}/persons")]
public class LessonController : ApiController
{
-------------------------------------
[HttpGet]
[Route("{personId}/groups/{groupId}/lessons/{lessonId}/lessonDetails")]
public LessonDetailsModel LessonDetails(long personId, long groupId, long lessonId)
{
----------------------------------
它的端点不显示,但如果我将 RoutePrefix 更改为
[RoutePrefix("mobile/v{version:apiVersion}/persons1")]
端点展示
更新: Swagger配置
GlobalConfiguration.Configuration
.EnableSwagger("mobile/swagger/docs/{apiVersion}", c => {
c.MultipleApiVersions(
ResolveVersionSupportByRouteConstraint,
(vc) =>
{
vc.Version("v5.0", "Api version 5");
vc.Version("v4.0", "Api version 4");
------------------------------------------------------
});
c.UseFullTypeNameInSchemaIds();
c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
c.OperationFilter<RemoveVersionParameters>();
c.OperationFilter<InjectCustomApiParameters>();
c.DocumentFilter<SetVersionInPaths>();
c.IncludeXmlComments(Path.ChangeExtension($"{Assembly.GetExecutingAssembly().GetName().CodeBase}", ".xml"));
})
.EnableSwaggerUi("mobile/swagger/ui/{*assetPath}", c => {
c.DocumentTitle("Mobile Api Swagger");
c.DisableValidator();
c.EnableDiscoveryUrlSelector();
});
我找到了解决问题的方法。我实现了 IApiExplorer。
public class VersionedApiExplorer : IApiExplorer
{
private readonly IApiExplorer _innerApiExplorer;
private readonly HttpConfiguration _configuration;
private readonly Lazy<Collection<ApiDescription>> _apiDescriptions;
private MethodInfo _apiDescriptionPopulator;
public VersionedApiExplorer(IApiExplorer apiExplorer, HttpConfiguration configuration)
{
_innerApiExplorer = apiExplorer;
_configuration = configuration;
_apiDescriptions = new Lazy<Collection<ApiDescription>>(Init);
}
public Collection<ApiDescription> ApiDescriptions => _apiDescriptions.Value;
private Collection<ApiDescription> Init()
{
var descriptions = _innerApiExplorer.ApiDescriptions;
var controllerSelector = _configuration.Services.GetHttpControllerSelector();
var controllerMappings = controllerSelector.GetControllerMapping();
var flatRoutes = FlattenRoutes(_configuration.Routes).ToList();
var result = new Collection<ApiDescription>();
foreach (var description in descriptions)
{
result.Add(description);
if (controllerMappings == null || !description.Route.Constraints.Any()) continue;
var matchingRoutes = flatRoutes.Where(r =>
r.RouteTemplate == description.Route.RouteTemplate && r != description.Route);
foreach (var route in matchingRoutes)
GetRouteDescriptions(route, result);
}
return result;
}
private void GetRouteDescriptions(IHttpRoute route, IReadOnlyCollection<ApiDescription> apiDescriptions)
{
if (route.DataTokens["actions"] is IEnumerable<HttpActionDescriptor> actionDescriptor &&
actionDescriptor.Any())
GetPopulateMethod().Invoke(_innerApiExplorer,
new object[] {actionDescriptor.First(), route, route.RouteTemplate, apiDescriptions});
}
private MethodInfo GetPopulateMethod()
{
if (_apiDescriptionPopulator == null)
_apiDescriptionPopulator = _innerApiExplorer.GetType()
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(
m => m.Name == "PopulateActionDescriptions" && m.GetParameters().Length == 4);
return _apiDescriptionPopulator;
}
public static IEnumerable<IHttpRoute> FlattenRoutes(IEnumerable<IHttpRoute> routes)
{
foreach (var route in routes)
{
switch (route)
{
case HttpRoute _:
yield return route;
break;
case IReadOnlyCollection<IHttpRoute> subRoutes:
{
foreach (var subRoute in FlattenRoutes(subRoutes))
yield return subRoute;
break;
}
}
}
}
}