Asp.Net Core遇到Swagger(四)-Swashbuckle技巧c篇

美男子玩编程

共 22463字,需浏览 45分钟

 ·

2021-09-16 17:34

点击上方蓝色字体,关注我们


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分组


修改ConfigureServicesAddSwaggerGen对应配置,以下为依据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规范,在添加到分组之前进行排序操作,也可自定义排序规则,修改ConfigureServicesAddSwaggerGen对应配置,此处以自定义排序规则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添加安全验证。


往期推荐



点击阅读原文,更精彩~
浏览 46
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报