最近在基于Swagger
进行二次开发, 来对项目的接口进行管理,功能实现了,但是不清楚swagger的工作原理,为了后续能更好利用Swagger
来管理接口,而且能借鉴Swagger
的原理,将项目中其他信息可视化展示,决定Debug
下Swagger
的流程。版本使用的是springdoc-openapi
,并不是旧版的swagger
,但是口语上还是用swagger
表示。
Swagger
使用分两个阶段,第一个阶段是注册一些Bean
,初始化一些配置。第二阶段是页面访问、接口调用,将数据填充到HTML页面展示在浏览器上。
注册静态资源
WebMvcConfigurationSupport
类会实例化HandlerMapping
这个Bean
实例化过程中,将所有的WebMvcConfigurer
中定义的ResourceHandler
放到ResourceHandlerRegistry
中去。所有的WebMvcConfigurer
包括系统默认的和自定义的,其中和Swagger
相关的就是WebMvcAutoConfiguration
类的内部类WebMvcAutoConfigurationAdapter
中注册的ResourceHandler
,处理的URL是/webjars/**
,locations是classpath:/META-INF/resources/webjars/
另外一个和Swagger
相关的是SwaggerWebMvcConfigurer
,里面会处理/swagger-ui*/**
路径,locations是classpath:/META-INF/resources/webjars/
ResourceHandlerRegistry
注册完后,会调用ResourceHandlerRegistry
的getHandlerMapping
方法,实例化AbstractHandlerMapping
。
对于Swagger
静态资源,对应的是ResourceHttpRequestHandler
。还需要注意到,最后生成AbstractHandlerMapping
是SimpleUrlHandlerMapping
,这在后面请求时会用到。
访问Swagger静态资源
首先访问Swagger
首页地址,/swagger-ui/index.html
,进入DispatcherServlet
的doDispatch
方法,根据Request对象获取mappedHandler
getHandler
方法里面就是从不同的handlerMappings
中获取HandlerExecutionChain
,实际走的就是SimpleUrlHandlerMapping
。
往SimpleUrlHandlerMapping
的getHandler
方法里看,最终是在SimpleUrlHandlerMapping
父类AbstractUrlHandlerMapping
的lookupHandler
方法中,根据URL匹配出ResourceHttpRequestHandler
,构造成HandlerExecutionChain
对象返回。
再往下根据mappedHandler
获取HandlerAdapter
,实际获取到的就是HttpRequestHandlerAdapter
接着就会调用HttpRequestHandlerAdapter
的handler
方法,handler
方法会将请求交给HttpRequestHandler
的handleRequest
方法,最终是交给ResourceHttpRequestHandler
的handleRequest
方法。在该方法中,首先就是获取Resource
对象。
实际上是通过DefaultResourceResolverChain
里面的WebJarsResourceResolver
和PathResourceResolver
得到最终的Resource
对象,首先是将swagger-ui/index.html
通过WebJarsResourceResolver
转成swagger-ui/4.14.0/index.html
,在将转化后的URL通过PathResourceResolver
解析出Resource
对象。
Swagger
的静态资源是在org.webjars:swagger-ui:4.14.0
中resources目录下
最后是将Resource
对象的内容,写入HttpServletResponse
中返回。
访问Swagger Rest接口
访问Swagger
首页时,除了一些静态资源如html
、css
、js
,Swagger
还会访问/v3/api-docs/
开头的后台Rest接口,这些接口会返回数据供页面使用,共同实现Swagger
的功能。
像上图的swagger-config
接口完整路径是/v3/api-docs/swagger-config
,这个接口是在SwaggerConfigResource
类中定义的。功能就是获取所有的group和对应的URL。
下面主要介绍另外一个接口,/v3/api-docs/{group}
,这个接口是根据group名称获取该组下的所有接口。
接口定义是这样的
首先获取OpenApiResource
对象,实际获取的是OpenApiWebMvcResource
对象
接着调用openapiJson
方法,最终调用的是AbstractOpenApiResource
的getOpenApi
方法
这个方法就是主要扫描接口的方法了,在openAPIService.build(finalLocale)
那一句,就是扫描Controller的接口
当然还有其他处理,处理参数、返回值、执行一些自定义的customisers等等(后面有时间再每一步进去看下是Swagger是如何解析接口、请求数据、返回值的),最终返回一个OpenAPI
对象,然后以Json格式的字符串返回给前端。
OpenAPI这个实体类的结构如下,从官网截取部分,以便理解,有了这个结构,可以自定义一些OpenApiCustomiser
,对OpenAPI对象进行改动,进行二次开发,实现一些自定义功能。
openapi: 3.0.3
info:
title: Swagger Petstore - OpenAPI 3.0
description: |-
This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about
Swagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!
You can now help us improve the API whether it's by making changes to the definition itself or to the code.
That way, with time, we can improve the API in general, and expose some of the new features in OAS3.
termsOfService: http://swagger.io/terms/
contact:
email: apiteam@swagger.io
license:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
version: 1.0.11
externalDocs:
description: Find out more about Swagger
url: http://swagger.io
servers:
- url: https://petstore3.swagger.io/api/v3
tags:
- name: pet
description: Everything about your Pets
externalDocs:
description: Find out more
url: http://swagger.io
- name: store
description: Access to Petstore orders
externalDocs:
description: Find out more about our store
url: http://swagger.io
paths:
/pet:
put:
tags:
- pet
summary: Update an existing pet
description: Update an existing pet by Id
operationId: updatePet
requestBody:
description: Update an existent pet in the store
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
application/xml:
schema:
$ref: '#/components/schemas/Pet'
application/x-www-form-urlencoded:
schema:
$ref: '#/components/schemas/Pet'
required: true
responses:
'200':
description: Successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
application/xml:
schema:
$ref: '#/components/schemas/Pet'
'400':
description: Invalid ID supplied
'404':
description: Pet not found
'405':
description: Validation exception
security:
- petstore_auth:
- write:pets
- read:pets
post:
tags:
- pet
summary: Add a new pet to the store
description: Add a new pet to the store
operationId: addPet
requestBody:
description: Create a new pet in the store
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
application/xml:
schema:
$ref: '#/components/schemas/Pet'
application/x-www-form-urlencoded:
schema:
$ref: '#/components/schemas/Pet'
required: true
responses:
'200':
description: Successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
application/xml:
schema:
$ref: '#/components/schemas/Pet'
'405':
description: Invalid input
security:
- petstore_auth:
- write:pets
- read:pets
总结
流程还是比较清晰,Swagger提供一些Jar包,包含Rest接口和静态资源,应用启动时先注册静态资源、确定访问的URL,访问Swagger首页时,通过SpringMVC
将Jar包中的静态资源取出,静态资源中请求后台Rest接口,由后台接口解析项目的Controller接口,返回接口信息给前端展示在页面上。
写这篇文章除了介绍Swagger原理,在Swagger基础上进行二次开发,另外一个目的就是是借鉴这种设计思路,和项目相关的信息可视化展示。比如项目脚本数据和数据表结构变更记录、Git文件提交记录等等。