Asp.Net Core遇到Swagger(四)-Swashbuckle技巧c篇
1
前言
接Swashbuckle技巧b篇
,与Action
相关的配置和操作,此处为c
篇内容。
2
实践技巧
2.1 忽略过时控制器和过时Action
1)配置服务
编辑ConfigureServices
函数中的AddSwaggerGen
函数:
services.AddSwaggerGen(c =>
{
...
#region 自定义DocInclusionPredicate判定规则
//options.DocInclusionPredicate((docName, apiDesc) => {
// //判定当前执行是否为函数对象
// if (!apiDesc.TryGetMethodInfo(out MethodInfo methodInfo))
// return false;
// //获取函数对应的自定义特性ApiVersionAttribute对应的版本集合
// var versions = methodInfo.GetCustomAttributes(true)
// .OfType<ApiVersionAttribute>()
// .SelectMany(x => x.Versions);
// //判定版本集合中是否与当前文档匹配
// return versions.Any(x => $"v{x}" == docName);
//});
#endregion
#region 忽略过时特性
//忽略标记过时的Action
options.IgnoreObsoleteActions();
//忽略标记过时的Properties
options.IgnoreObsoleteProperties();
#endregion
...
};
2)添加案例控制器
新建控制器IgnoreObsoleteController
,内容如下:
namespace swaggertestbase.Controllers.v1
{
/// <summary>
/// 忽略函数控制器
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class IgnoreObsoleteController : ControllerBase
{
/// <summary>
/// 获取忽略函数数据
/// </summary>
/// <returns>返回结果</returns>
[Obsolete]
[HttpGet]
public string Get()
{
return "IgnoreObsolete";
}
[HttpGet("{id}")]
//直接直接使用ApiExplorerSettings设置属性IgnoreApi = true
//在当前项目中该配置无效
[ApiExplorerSettings(IgnoreApi = true)]
public string GetById(int id)
{
return "IgnoreObsolete";
}
}
}
运行效果如下,在v1
组中并不存在对应的控制器以及对应过时的函数。
2.2 按照约定选择Action函数
1)自定义约定实现类
新建类ApiExplorerGetsOnlyConvention
,实现IActionModelConvention
接口,进行ActionModelConvention
的自定义规则实现。
/// <summary>
/// 自定义实现只显示Get请求的Action模型
/// </summary>
public class ApiExplorerGetsOnlyConvention : IActionModelConvention
{
public void Apply(ActionModel action)
{
//依据请求是否为get请求处理是是否参与apijson的数据接口生成
action.ApiExplorer.IsVisible = action.Attributes.OfType<HttpGetAttribute>().Any();
}
}
2)添加约定到服务配置
services.AddControllers(configure => {
//添加自定义apiexplor.groupname控制器模型属性
//configure.Conventions.Add(new ApiExplorerGroupVersionConvention());
//自定义实现只显示Get请求的Action模型
configure.Conventions.Add(new ApiExplorerGetsOnlyConvention());
});
在控制器WeatherForecastController
中添加一个Post
请求函数。
/// <summary>
/// 获取Post数据
/// </summary>
/// <returns>处理结果</returns>
[HttpPost]
public string Post()
{
return "处理结果";
}
运行效果:
2.3 自定义操作标签
默认Tag
分组是以控制器名称
为依据进行划分,也可以通过TagActionsBy
函数进行配置。
1)默认分组
2)自定义Tags分组
修改ConfigureServices
中AddSwaggerGen
对应配置,以下为依据Action
的请求方法进行分组的案例,需要注意的是IncludeXmlComments
函数,需要使用默认配置,而不是设置IncludeXmlComments
第二参数为true
。
services.AddSwaggerGen(options =>
{
...
#region 添加xml注释文件描述性信息
//获取当前执行程序集名称+.xml,作为实际xml注释文件名称
string filename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
//拼接路径-路径间隔符由系统决定
string path = System.IO.Path.Combine(System.AppContext.BaseDirectory, filename);
//添加xml注释文件到swaggergen中用于生成api json
//此时需要选择使用该函数的默认配置
options.IncludeXmlComments(path);
#endregion
#region 自定义Tags
options.TagActionsBy(apidescr => {
return new string[] {
apidescr.HttpMethod
};
});
#endregion
...
};
services.AddControllers(configure => {
//添加自定义apiexplor.groupname控制器模型属性
//configure.Conventions.Add(new ApiExplorerGroupVersionConvention());
//自定义实现只显示Get请求的Action模型
//configure.Conventions.Add(new ApiExplorerGetsOnlyConvention());
});
运行效果:
2.4 自定义Action排序操作
默认情况下,Action
能够按Swagger
规范,在添加到分组之前进行排序操作,也可自定义排序规则,修改ConfigureServices
中AddSwaggerGen
对应配置,此处以自定义排序规则Controller
与请求方法
进行排序。
#region 自定义Tags
options.TagActionsBy(apidescr => {
return new string[] {
apidescr.HttpMethod
};
});
#endregion
#region 自定义Tags排序
options.OrderActionsBy(apidesc =>
{
//自定义Tag内的Action排序
return $"{apidesc.ActionDescriptor.RouteValues["controller"]}_{apidesc.HttpMethod}";
});
#endregion
运行效果:
2.5 自定义架构id
如果在文档生成器遇到复杂的传入参数或响应类型,生成器会自动生成相应Json Schema
,并将其添加到全局Components/Schemas
字典中。
还是以天气类WeatherForecast
,默认情况下,Api Json
对应请求链接http://localhost:5000/v1/swaggerapi.json
内容大致如下:
{
"paths": {
"/WeatherForecast": {
"get": {
"tags": [
"GET"
],
"summary": "获取天气预报信息",
"responses": {
"201": {
"description": "请求成功并且服务器创建了新的资源",
"content": {
"text/plain": {
"schema": {
"type": "array",
"items": {
// #/components/schemas/{schemasid}
"$ref": "#/components/schemas/WeatherForecast"
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"WeatherForecast": {
"type": "object",
"properties": {
"date": {
"type": "string",
"description": "日期",
"format": "date-time"
}
},
"additionalProperties": false,
"description": "天气预报实体"
}
}
}
}
修改服务中ConfigureServices
的配置,自定义标识的生成规则CustomSchemaIds。
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen(
options =>
{
#region 自定义架构Id
// 输出对应类型全名称
options.CustomSchemaIds(schema => schema.FullName);
#endregion
}
);
}
运行后,对应输出的Json
结构如下:
{
"paths": {
"/WeatherForecast": {
"get": {
"tags": [
"GET"
],
"summary": "获取天气预报信息",
"responses": {
"201": {
"description": "请求成功并且服务器创建了新的资源",
"content": {
"text/plain": {
"schema": {
"type": "array",
"items": {
// #/components/schemas/{schemasid}
"$ref": "#/components/schemas/swaggertestbase.WeatherForecast"
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {
// {schemasid}
"swaggertestbase.WeatherForecast": {
"type": "object",
"properties": {
"date": {
"type": "string",
"description": "日期",
"format": "date-time"
}
},
"additionalProperties": false,
"description": "天气预报实体"
}
}
}
}
2.6 覆盖特定类型架构[不常用]
需要对特定类型进行指定序列化处理时,可以通过自定义架构序列化类型处理。
数据实体类:
/// <summary>
/// 天气预报实体
/// </summary>
public class WeatherForecast
{
/// <summary>
/// 日期
/// </summary>
public DateTime Date { get; set; }
/// <summary>
/// 温度-摄氏度
/// </summary>
public int TemperatureC { get; set; }
/// <summary>
/// 温度-华摄氏度
/// </summary>
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
/// <summary>
/// 描述
/// </summary>
public string Summary { get; set; }
}
请求http://localhost:5000/v1/swaggerapi.json
,原始Json
架构如下:
"components": {
"schemas": {
"WeatherForecast": {
"type": "object",
"properties": {
"date": {
"type": "string",
"description": "日期",
"format": "date-time"
},
"temperatureC": {
"type": "integer",
"description": "温度-摄氏度",
"format": "int32"
},
"temperatureF": {
"type": "integer",
"description": "温度-华摄氏度",
"format": "int32",
"readOnly": true
},
"summary": {
"type": "string",
"description": "描述",
"nullable": true
}
},
"additionalProperties": false,
"description": "天气预报实体"
}
}
}
修改ConfigureServices
中的AddSwaggerGen
,设定类型序列化的架构为字符串。
services.AddSwaggerGen(
options =>
{
#region 覆盖特定类型架构
// 指定对应的实体类型,类型架构配置
options.MapType<WeatherForecast>(() => new Microsoft.OpenApi.Models.OpenApiSchema { Type = "string" });
#endregion
}
);
设置以后,对应的类型在架构中,无生成,目前无考虑深究原因,运行效果如下:
2.7 使用过滤器扩展生成器
1)操作过滤器
Swashbuckle
会检索每一个Asp.Net Core
中的每一个ApiDescription
,从中提取到对应的OpenApiOperation
,对应OpenApiOperation
类型和ApiDescription
能够通过操作过滤器列表实现传递。以包含权限认证
特性,响应中401
响应状态的操作过滤器为例。
创建类AuthResponsesOperationFilter
实现IOperationFilter。
public class AuthResponsesOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var customAttributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
.Union(context.MethodInfo.GetCustomAttributes(true))
.OfType<AuthorizeAttribute>();
if (customAttributes.Any())
{
operation.Responses.Add("401", new OpenApiResponse { Description = "UnAuthorized" });
}
}
}
给控制器中WeatherForecastController
对应Post
函数添加特性Authorize。
/// <summary>
/// 天气预报服务
/// </summary>
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
........
/// <summary>
/// 获取Post数据
/// </summary>
/// <returns>处理结果</returns>
[HttpPost]
[Authorize]
public string Post()
{
return "处理结果";
}
........
}
添加到动作过滤器列表中,
services.AddSwaggerGen(
options =>
{
#region 添加权限认证响应操作过滤器
options.OperationFilter<AuthResponsesOperationFilter>();
#endregion
}
);
运行效果如下:
需要注意的是,过滤器管道是DI
感知的。可以使用构造函数参数创建过滤器,只要参数类型已在DI
框架中注册,则它们将在过滤器实例化时自动注入。
2)架构过滤器
Swashbuckle
为控制器操作公开的每个参数、响应和属性类型生成 Swagger 样式的 JSONSchema
。生成后,它通过配置的架构过滤器列表传递架构和类型。
创建枚举类型。
/// <summary>
/// 自定义枚举
/// </summary>
public enum MyEnum
{
A,
B,
C
}
创建SchemaFilterController
控制器。
[Route("api/[controller]")]
[ApiController]
public class SchemaFilterController : ControllerBase
{
/// <summary>
/// 获取SchemaFilter过滤器数据
/// </summary>
/// <param name="my">枚举参数</param>
[HttpGet]
public void Get(MyEnum my)
{
}
}
自定义类AutoRestSchemaFilter
,实现ISchemaFilter。
/// <summary>
/// 自动设置架构过滤器
/// </summary>
public class AutoRestSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
var type = context.Type;
if (type.IsEnum)
{
schema.Extensions.Add("x-ms-enum", new OpenApiObject()
{
["name"] = new OpenApiString(type.Name),
["modelAsString"] = new OpenApiBoolean(true)
});
}
}
}
添加到架构过滤器中:
....
services.AddSwaggerGen(
options =>
{
#region 添加自定义架构过滤器
options.SchemaFilter<AutoRestSchemaFilter>();
#endregion
});
....
访问http://localhost:5000/v1/swaggerapi.json
,可以看到对应结构如下如下,x-ms-enum
在对应类型的架构尾部。
{
"components": {
"schemas": {
"MyEnum": {
"enum": [
0,
1,
2
],
"type": "integer",
"description": "自定义枚举",
"format": "int32",
"x-ms-enum": {
"name": "MyEnum",
"modelAsString": true
}
}
}
}
}
通过上述例子,可以看出,对于枚举等特殊类型,实际可以通过自定义Json schema
的方式实现对应字段值的备注含义。
3)文档过滤器
Document Filters
在遵守OpenApi
的规范前提下,可以通过文档过滤器对文档进行任意有效的Swagger Json
。例如为当前文档添加额外的Tags
,自定义过滤器TagDescriptionsDocumentFilter
,实现IDocumentFilter。
/// <summary>
/// Tag描述文档过滤器
/// </summary>
public class TagDescriptionsDocumentFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
swaggerDoc.Tags = new List<OpenApiTag> {
new OpenApiTag{
Name ="Pages",
Description = "分页类"
},
new OpenApiTag{
Name ="Tests",
Description = "测试类"
}
};
}
}
添加到现有的文档过滤器中:
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen(
options =>
{
//忽略部分内容
#region 添加自定义文档过滤器
options.DocumentFilter<TagDescriptionsDocumentFilter>();
#endregion
//忽略部分内容
}
);
}
请求http://localhost:5000/v1/swaggerapi.json
,在返回结果中能够看到如下部分结构:
{
//忽略无关项
"tags": [
{
"name": "Pages",
"description": "分页类"
},
{
"name": "Tests",
"description": "测试类"
}
]
}
后续文章将讲解为Swagger
添加安全验证。