目錄
- 一、Swagger 簡介
- 1. 什么是 Swagger?
- 2. 如何使用 Swagger
- 3. Springboot 中swagger的使用示例
- 1. maven 引入安裝
- 2. java配置
- 二、Swagger UI存在的缺點
- 1.不夠方便直觀
- 2.請求的參數沒有緩存
- 3.不夠美觀
- 4.如果是JWT 無狀態登錄,Swagger使用起來就沒有那么絲滑了
- 三、封裝以及討論
- 封裝組件
- 代碼封裝
- 解決無狀態登錄問題
- 1. 我們往swagger-bootstrap-ui的右上角加一個按鈕,點擊出現彈框來配置自動獲取登錄token
- 2.javascript 腳本部分實現:
- 總結
一、Swagger 簡介
Swagger 是一款用于 API 設計、構建、文檔化和測試的開源工具。它提供了一整套的解決方案,幫助開發者更好地構建和管理 RESTful APIs。以下是對 Swagger 的簡要介紹:
1. 什么是 Swagger?
Swagger 是一個規范和完整的框架,用于生成、描述、調用和可視化 RESTful Web 服務。它基于 OpenAPI 規范(以前稱為 Swagger 規范),該規范定義了 API 的結構。
2. 如何使用 Swagger
- 定義 API:使用 Swagger Editor 編寫 OpenAPI 規范文件,描述 API 的端點、請求參數、響應格式等。
- 生成文檔:通過 Swagger UI 或 Swagger Codegen,生成可視化的 API 文檔。
- 集成到項目:在項目中集成 Swagger 的相關工具,比如在 Spring Boot 項目中使用
springfox-swagger2
和springfox-swagger-ui
依賴,自動生成和托管 API 文檔。
3. Springboot 中swagger的使用示例
以下是一個使用 Swagger 注解的簡單示例,展示了如何在 Spring Boot 應用中集成 Swagger:
1. maven 引入安裝
<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version>
</dependency>
<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.9.2</version>
</dependency>
2. java配置
@EnableSwagger2
@Configuration
public class SwaggerConfig {@Beanpublic Docket createRestApi() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select().apis(RequestHandlerSelectors.basePackage("com.efficientnotes.boot.web")).paths(PathSelectors.any()).build();}private ApiInfo apiInfo() {return new ApiInfoBuilder().title("Efficient notes api platform").description("apis").termsOfServiceUrl("http://localhost:9000/").contact("yh4494@sina.com").version("1.0").build();}}
title
title就是swagger頁面上展示的title,比如交ERP api平臺
description
詳情介紹
termsOfServiceUrl
機構首頁地址
contact
聯系方式
version
版本號
Spring security 排除攔截:
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/swagger-ui.html").permitAll().antMatchers("/webjars/**").permitAll().antMatchers("/swagger-resources/**").permitAll().antMatchers("/v2/*").permitAll().antMatchers("/csrf").permitAll().antMatchers("/").permitAll().anyRequest().authenticated().and().formLogin();}
}
控制器示例:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;@RestController
@Api(value = "示例API", description = "這是一個示例API")
public class ExampleController {@GetMapping("/hello")@ApiOperation(value = "問候接口", notes = "通過傳遞名字來獲得問候語")public String hello(@ApiParam(value = "用戶的名字", required = true) @RequestParam String name) {return "Hello, " + name + "!";}
}
在完成上述步驟后,啟動 Spring Boot 應用并訪問 http://localhost:8080/swagger-ui.html
,即可查看自動生成的 API 文檔。
Swagger 是一個強大且靈活的工具集,為開發者提供了方便、高效的 API 管理解決方案。通過標準化和自動化,它極大地簡化了 API 開發和維護的過程。
圖 ①
二、Swagger UI存在的缺點
1.不夠方便直觀
swagger ui 布局是上下瀑布式的,比如我訪問完A接口,想訪問B接口,訪問完B接口想繼續訪問A接口就必須往上翻,接口少還好操作。接口多的話來回就很煩。
2.請求的參數沒有緩存
比如我想掉一個post接口來偽造一條數據,第一次訪問完成之后,刷新頁面后第二次還要重新造數據,就很麻煩,命名我只需要改部分字段重新請求就行,結果每次都要重新填寫報文。字段躲起來兼職就是折磨。
3.不夠美觀
不用多說,當然一個工具類產品美觀并不重要,但是美觀的產品還是能給人帶來心情愉悅的體驗,就像你旁邊坐著一位漂亮女孩,你整天心情都會好很多。不管如何對我來說工具顏值還是挺重要的。
4.如果是JWT 無狀態登錄,Swagger使用起來就沒有那么絲滑了
因為JWT無狀態登錄這種需要每次在請求的Header中帶上TOKEN,Swagger可沒那么只能給你登錄接口返回的token帶過去,這樣就導致無狀態session的情況下Swagger的調試功能等于癱瘓狀態。
三、封裝以及討論
我們來討論下如何解決上述一些缺陷給Swagger換一層皮,并且將Swagger封裝成組件
很久以前我就關注過一些swaggerui的項目了,比較優秀的是swagger-bootstrap-ui。現在好像升級過不叫這個名字了,但我保存了一份之前的swagger-bootstrap-ui的代碼。github的地址是:https://github.com/xiaoymin/swagger-bootstrap-ui,直接可以下載的地址: swagger-bootstrap-ui。
這份UI解決了上述中前三個問題不夠方便直觀、請求的參數沒有緩存、不夠美觀,雖然不是太好看,但也還好吧!如果對外觀還是不夠滿意的話可以自己修改下樣式,蓋起來也很方便,我會在下面的文章中進行討論和介紹。那么我們先看下具體的ui長什么樣子。
圖②
看起來是不是比圖 ①要舒服多了。那么我們在下面異步異步的討論如何將這個ui封裝成一個組件可以直接引用以及如何解決缺陷的第四點:JWT無狀態登錄如何自動拼裝token。
封裝組件
現在我們有了前端代碼,可以參考swagger原生的ui就是做成的一個jar包,引入的這個jar包就擁有了swagger-ui。我們也參考下這種方式,前后端代碼結合自動配置,項目引入我們的依賴就能直接擁有提供接口文檔的能力。
代碼封裝
我們想讓我們的組件提供完整的swagger能力,那么首先我們自己的封裝的swagger組件中要引入所有的swagger的依賴。
<dependencies><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version><exclusions><exclusion><artifactId>spring-context</artifactId><groupId>org.springframework</groupId></exclusion><exclusion><artifactId>spring-aop</artifactId><groupId>org.springframework</groupId></exclusion><exclusion><artifactId>jackson-annotations</artifactId><groupId>com.fasterxml.jackson.core</groupId></exclusion><exclusion><artifactId>slf4j-api</artifactId><groupId>org.slf4j</groupId></exclusion><exclusion><artifactId>spring-beans</artifactId><groupId>org.springframework</groupId></exclusion></exclusions></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>
</dependencies>
swagger2是通過EnableSwagger2來啟用swagger,我們也可以寫一個注解來做這件事情,引用我們自己定義的注解就可以自動配置啟用我們的swagger組件。
EnableAllensSwagger
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(SwaggerConfig.class)
public @interface EnableAllensSwagger {
}
然后我們需要將所有的配置都配置好,提供我們自己自定義的配置出去,然需要引入的應用項目配置我們提供的配置來自定義Swagger。
SwaggerProperties
/*** SwaggerProperties** @author allens* @since 2024/5/21*/
@Component
@ConfigurationProperties(prefix = "allens.swagger")
@Setter
@Getter
public class SwaggerProperties {private String packages;private String title;private String description;private String termsOfServiceUrl;private String version;private String contact;
}
SwaggerConfig
通過這個類來做組件的autoconfig,當Springboot中啟用@EnableAllensSwagger注解的時候,@Import(SwaggerConfig.class)就會自動加載注入 SwaggerConfig
,SwaggerConfig回去掃描com.allens.swagger
下的所有bean來自動加載組件。
@EnableSwagger2
@Configuration
@ComponentScan("com.allens.swagger")
@EnableConfigurationProperties(SwaggerProperties.class)
public class SwaggerConfig {@ResourceSwaggerProperties swaggerProperties;@Beanpublic Docket createRestApi() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select().apis(RequestHandlerSelectors.basePackage(swaggerProperties.getPackages())).paths(PathSelectors.any()).build();}private ApiInfo apiInfo() {return new ApiInfoBuilder().title(swaggerProperties.getTitle()).description(swaggerProperties.getDescription()).termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl()).contact(swaggerProperties.getContact()).version(swaggerProperties.getVersion()).build();}}
接著我們需要把前端的代碼copy到項目中,同時項目打包的時候可以把swagger-bootstrap-ui打包到jar包中,同時我們需要訪問/${servletContextPath}/doc.html的時候可以正常訪問到前端界面。
① 拷貝swagger-boot-ui 到項目中
② 配置路由映射規則,如果是SpringSecurity也是要忽略doc.html
//webjars/**
這兩個地址。
@Configuration
public class SwaggerWebMvcConfig implements WebMvcConfigurer {@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("doc.html").addResourceLocations("classpath:/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/resources/webjars/");}
}
@Override
protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().and().csrf().disable().exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers(" "/doc.html","/webjars/**","/v2/api-docs/**","/swagger-resources/**").anonymous().anyRequest().authenticated();http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);// 添加CORS filterhttp.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);http.addFilterBefore(corsFilter, LogoutFilter.class);
}
然后我們就可以直接到項目中引入我們的組件并且配置了
<dependency><groupId>com.efficientnotes</groupId><artifactId>allens-swagger</artifactId><version>1.0-SNAPSHOT</version>
</dependency>
yml文件配置:
allens:swagger:packages: com.efficientnotes.boot.webtitle: Efficient notes api platformdescription: apistermsOfServiceUrl: http://localhost:9000/version: 1.0contact: allens@sina.com
啟動服務并訪問: http://localhost:9000/${servletContextPath}/doc.html
解決無狀態登錄問題
如果說你的項目是Session模式的方案,那么到這里就結束了。但假如說你是無狀態登錄的方案,那么就要繼續往下看了。無狀態登錄我們需要把token塞進header中進行請求,那么我們就需要攔截截取登錄接口的報文中的token。而且每次請求的時候都要把header中的token拼接上去。
注意只有無狀態JWT模式才需要這樣做!!!
1. 我們往swagger-bootstrap-ui的右上角加一個按鈕,點擊出現彈框來配置自動獲取登錄token
點擊之后是一個彈窗,這個彈窗會要求輸入登錄接口路徑、登錄token提取路徑以及Token名稱。如果走自動獲取HeaderJSON是不需要填的。如果想手動塞Header,那么可以使用JSON格式塞TOKEN塞進行去,這種方式不需要配置登錄接口路徑和登錄token提取路徑以及token名稱。
登錄接口路徑
你系統的登錄接口路徑登錄接口提取路徑
json path,比如登錄接口返回的是{“data”: {token: “xxx”, “other”: “ok”},“msg”: “”},那么登錄接口提取路徑為:data.token
TOKEN名稱
接口請求需要攜帶的Header Token名稱Header JSON
有兩種情況,如果不填上述三個配置,header JSON就可以手動設置,設置完成之后,每次請求都會自動攜帶你配置的Header上去。如果配置了上述三個配置,那么每次自動獲取完登錄的TOKEN之后會自動帶出來。
來看看代碼怎么實現的:
doc.html
<div class="sbu-header-right" style="margin-top:12px"><div class="col-sm-6"><button style="background: #0d5aa7" onclick="showPromoteDefaultHeader()" type="button" class="btn btn-info">通用頭設置</button></div>
</div>
2.javascript 腳本部分實現:
function showPromoteDefaultHeader() {var test = layer.open({type: 1, // page 層類型area: ['700px', '500px'],title: '配置頭信息',shade: 0.6, // 遮罩透明度shadeClose: true, // 點擊遮罩區域,關閉彈層maxmin: true, // 允許全屏最小化anim: 0, // 0-6 的動畫形式,-1 不開啟content:`<div class="allens-dialog"><div class="element"><span>登錄接口路徑</span><input id="allens-dialog-login-interface-name" name="loginInterfaceName" /></div><div class="element"><span>登錄token提取路徑</span><input id="allens-dialog-json-path" name="jsonPath" /></div><div class="element"><span>TOKEN名稱</span><input id="allens-dialog-token-name" name="tokenName" /></div><div class="element"><span>Header JSON</span><textarea id="allens-dialog-header-json" style="width: 100%;" cols="10" rows="8" name="" id=""></textarea></div><button class="btn btn-primary btn-lg" id="allens-dialog-submit">提交</button></div>`});function notEmpty (variable) {if (variable === null || variable === undefined || variable === '') {// 變量為空return false;}return true;}var button = document.querySelector('#layui-layer' + test + ' #allens-dialog-submit');var loginInterFaceName = document.querySelector('#layui-layer' + test + ' #allens-dialog-login-interface-name');var jsonPath = document.querySelector('#layui-layer' + test + ' #allens-dialog-json-path');var headerJson = document.querySelector('#layui-layer' + test + ' #allens-dialog-header-json');var tokenName = document.querySelector('#layui-layer' + test + ' #allens-dialog-token-name');button.onclick = () => {if (notEmpty(loginInterFaceName)) {localStorage.setItem('LOGININTERFACENAME', loginInterFaceName.value)}if (notEmpty(jsonPath)) {localStorage.setItem('JSONPATH', jsonPath.value)}if (notEmpty(headerJson)) {localStorage.setItem('HEADERJSON', headerJson.value)}if (notEmpty(tokenName)) {localStorage.setItem('TOKENNAME', tokenName.value)}layer.close(test);}// text.setAttribute('placeholder', '格式為JSON:{"headerName": "headerValue"}')var data = localStorage.getItem('HEADERJSON');if (data) {const jsonObject = JSON.parse(data);const jsonString = JSON.stringify(jsonObject, null, 2);headerJson.value = jsonString;}loginInterFaceName.value = localStorage.getItem('LOGININTERFACENAME')jsonPath.value = localStorage.getItem('JSONPATH')tokenName.value = localStorage.getItem('TOKENNAME')
}
可以下載下源碼看下具體的實現,暫時只支持CSDN下載,后續我會傳到github。https://download.csdn.net/download/yh4494/89335880
總結
至此我們討論的四個swagger的缺點就全部解決了,當然你也可以自己去修改樣式,源碼都有的怎么該都可以,只要不要有大的bug就行。甚至可以上傳到中央倉庫給大家使用。