添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
逼格高的铅笔  ·  Git commit issue ...·  1 年前    · 
博学的山寨机  ·  antd ...·  1 年前    · 
  • 📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!
  • 📢本文作者:由webmote 原创,首发于 【掘金】
  • 📢作者格言: 生活在于折腾,当你不折腾生活时,生活就开始折腾你,让我们一起加油!💪💪💪
  • 1.扩展场景

    这是Swagger的的高阶应用篇,由于我以前项目中增加了框架级别的入参和出参定义,导致swagger无法识别外部的定义,仅仅识别为控制器方法的定义。

    如果追根溯源,这个跟swagger也没太大的关系,应该是asp提供的ApiExplorer的问题,毕竟是静态反射,你想让他支持动态的过滤,这的确是勉为其难了。

    2.swagger的结构

    我使用的环境是asp.net core 3.1,swagger的包是Swashbuckle.AspNetCore 5.0.0。如果仅仅是产生一个api文档,为什么要关注这个Swagger的包,甚至结构呢,你说是吧!我当时就有股放下的冲动,给前端介绍下,让他们自己理解去... ...

    作为一个接近产品的项目,我觉得有必要深入的理解下swagger的结构,完成这个转换就非常完美了。

    说起swagger的结构,其实不如说是OpenAPI的结构,这个规范应该起源自swagger,微软定义了一套实现。

    3. OPEN API 规范

    记住微软的github地址: OpenApi 这里简单过下规范的描述, 目前标准为 3.02 , OpenApi的根文档对象如下:

    # OpenAPI 规范版本号
    openapi: 3.0.2
    # API 元数据信息
    info:
    # 服务器连接信息
    servers:
    # API 的分组标签
    tags: 
    # 对所提供的 API 有效的路径和操作
    paths:
    # 一个包含多种纲要的元素,可重复使用组件
    components:
    # 声明 API 使用的安全机制
    security:
    # 附加文档
    externalDocs:
    

    其中为了修改入参和出参,我们需要更改的是paths对象以及其引用的组件对象,组件对象封装可重用对象,然后通过 $ref 标签进行引用。

    4. Path 对象的部分详解

    swagger在启动后通过ApiExplorer获取到所有的控制器和方法信息,并进行组装,最后规范化OpenApi后输出到Json文件。 因此当某些对象不会操作时可以看下微软OpenApi的例程。这方面资料非常少,几乎没有借鉴的东东。

    5. 我们的目标

    我们的目标简言之是这样的,有一个Api,其定义如下: Resp A (Req req),经过swagger解析后正常描述为 接口A,入参:Req,结果Resp,而经过框架过滤后是需要解析为 接口A,入参: Args,结果Result,也可以理解为需要把目标接口解析为形如Result<Resp> A (Args<Req> req)

    怎么干——之一

    能否硬上? 我们利用swagger的过滤器,把ActionDescriptor定义的参数、方法都改掉。尝试中我放弃了,动态生成一个ActionDescriptor对象,难度太高了,其中大部分方法都是只读的,因此无法进行修改,只能重新构造一个全新的对象。

    怎么干——之二

    修改生成的json文件?显然没有类似的接口不太靠谱。最后经过不断浏览Swagger的源代码,终于发现了有四个过滤器:

  • IDocumentFilter 文档过滤器,对文档描述进行修改
  • IOperationFilter Api级别过滤器,对不同的api进行定制化过滤
  • ISchemaFilter 模式过滤器,修改或增删组件定义的模型
  • IParameterFilter 参数过滤器,对特定参数进行过滤 经过筛选,有两类过滤器可以应用到本次活动中,IOperationFilter和ISchemaFilter,最后最合适的无非是 api级别的过滤器了!
  • 6. 实现思路

    上面已经确定了干活方向,那怎么实现呢?其实思路已经跃然纸上了,直接把入参和出参的Schema修改为泛型定义的入参和出参Schema即可。 代码如下:

    public class MyOperationFilter : IOperationFilter
            public void Apply(OpenApiOperation operation, OperationFilterContext context)
                if (context.MethodInfo == null) return;
                //对post的入参进行转换,get的没有好的转换,不支持get
                if(operation.RequestBody != null)
                    foreach(var c in operation.RequestBody.Content)
                        var oldRef = c.Value.Schema.Reference;
                        //如果body是引用object对象定义
                        if(oldRef != null)
                            var pt = context.MethodInfo.GetParameters().FirstOrDefault().ParameterType;                        
                            c.Value.Schema = context.SchemaGenerator.GenerateSchema(typeof(ApiArgs<>).MakeGenericType(pt), context.SchemaRepository);
                        else if (c.Value.Schema?.Type?.ToLower() == "array")
                            var pt = context.MethodInfo.ReturnType;
                            c.Value.Schema = context.SchemaGenerator.GenerateSchema(typeof(ApiResult<>).MakeGenericType(pt), context.SchemaRepository);
                if(operation.Responses != null)
                    foreach (var c in operation.Responses.Values)
                        var oldRef = c.Content.FirstOrDefault().Value?.Schema.Reference;
                        if (oldRef != null)
                            var pt = context.MethodInfo.ReturnType;
                            c.Content.First().Value.Schema = context.SchemaGenerator.GenerateSchema(typeof(ApiResult<>).MakeGenericType(pt), context.SchemaRepository);
                        else if(c.Content.FirstOrDefault().Value?.Schema.Type?.ToLower()=="array")
                            var pt = context.MethodInfo.ReturnType;
                            c.Content.First().Value.Schema = context.SchemaGenerator.GenerateSchema(typeof(ApiResult<>).MakeGenericType(pt), context.SchemaRepository);
    

    7.最关键的点

    c.Value.Schema = context.SchemaGenerator.GenerateSchema(typeof(ApiArgs<>).MakeGenericType(pt), context.SchemaRepository);
    

    该语句在openapi的组件类中增加了个新的泛型对象,然后赋值给原来的schema,无需改动别的东东,生成的json随之更新,太完美了!!!

    9. 小结

    噢噢噢,愉快的周一结束了!

    例行小结,理性看待!

    结的是啥啊,结的是我想你点赞而不可得的寂寞。😳😳😳

    👓都看到这了,还在乎点个赞吗?

    👓都点赞了,还在乎一个收藏吗?

    👓都收藏了,还在乎一个评论吗?

    分类:
    后端
  •