Swagger是一個有用web界面的提供實體模型結構展現,接口展現,調測等的一個工具,使用它能夠提升開發者開發效率,特別是先後端配合開發時,大大省去了溝通接口耗費的時間:服務端開發完接口發佈後,UI端直接就能夠經過Swagger提供的文檔信息就能很容易理解哪些接口須要傳遞哪些參數,以及參數是否必填,參數類型等。html
上邊只是提到了我的使用過程當中,感覺到的優點,就這些優點足夠吸引開發者使用該工具,使用Swagger須要完整一下幾步操做:java
1)新建SpringBoot工程,並還引入swagger ui依賴包;git
2)在SpringBoot中添加Swagger配置類,啓用swagger,並設置配置項;github
3)定義實體類,實體類加上文檔註解;web
4)定義Restful Api接口,並添加文檔註解;spring
5)發佈訪問。數據庫
下邊針對上邊的步驟進行詳細介紹:apache
修改maven工程後,引入springboot parent,引入swagger ui相關依賴包。修改後的pom.xml以下:json
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.dx.sort</groupId> <artifactId>springboot-with-fastjson</artifactId> <version>1.0-SNAPSHOT</version> <name>springboot-with-fastjson</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <!-- springboot web組件依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.56</version> </dependency> <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> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>swagger-bootstrap-ui</artifactId> <version>1.9.6</version> </dependency> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-models</artifactId> <version>1.5.24</version> </dependency> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-annotations</artifactId> <version>1.5.24</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies> <build> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle --> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> </plugin> <plugin> <artifactId>maven-jar-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle --> <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> </plugin> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> </plugins> </pluginManagement> </build> </project>
修改springboot入口函數:bootstrap
package com.dx.test.app; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * Hello world! */ @SpringBootApplication(scanBasePackages = {"com.dx.test"}) public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
在src/main下添加resources資源文件夾,在resources文件夾下添加application.yml文件,文件內容爲:
server: port: 8080
配置服務器運行端口爲8080。
在項目中新建Swagger2Configuration類,並修改該類爲以下:
package com.dx.test.web.config; import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration @EnableSwagger2 @EnableSwaggerBootstrapUI public class Swagger2Configuration implements WebMvcConfigurer { @Bean public Docket createDocApi() { return new Docket(DocumentationType.SWAGGER_2) .groupName("default") // 默認就是 default .apiInfo(createApiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.dx.test")) .paths(PathSelectors.any()) .build(); } private ApiInfo createApiInfo(){ return new ApiInfoBuilder() .title("Dx系統Restful Apis") .description("Dx系統Restful Apis詳細說明") .license("") .licenseUrl("") .termsOfServiceUrl("") .contact(new Contact("dx","http://xxx.com","dx@xx.com.cn")) .version("1.0.0") .build(); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**") .addResourceLocations("classpath:/static/"); registry.addResourceHandler("swagger-ui.html") .addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); registry.addResourceHandler("doc.html") .addResourceLocations("classpath:/META-INF/resources/"); } }
添加文章實體Article類,並對文章實體屬性添加swagger文檔註解,以及對字段顯示順序進行排序。
package com.dx.test.module; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiSort; import java.io.Serializable; import java.util.Date; @ApiModel(value = "文章實體") public class Article implements Serializable { @ApiModelProperty(value = "文章id", position = 0) private Long id; @ApiModelProperty(value = "標題", position = 1) private String title; @ApiModelProperty(value = "做者", position = 2) private String author; @ApiModelProperty(value = "內容", position = 3) private String content; @ApiModelProperty(value = "建立人", position = 4) private String createUser; @ApiModelProperty(value = "建立人id", position = 5) private String createUserId; @ApiModelProperty(value = "建立時間", position = 6) private Date createTime; @ApiModelProperty(value = "修改人", position = 7) private String modifyUser; @ApiModelProperty(value = "修改人id", position = 8) private String modifyUserId; @ApiModelProperty(value = "修改時間", position = 9) private Date modifyTime; public Article(Long id, String title, String author, String content, Date createTime) { this.id = id; this.title = title; this.author = author; this.content = content; this.createTime = createTime; } /** * @return the id */ public Long getId() { return id; } /** * @param id the id to set */ public void setId(Long id) { this.id = id; } /** * @return the title */ public String getTitle() { return title; } /** * @param title the title to set */ public void setTitle(String title) { this.title = title; } /** * @return the author */ public String getAuthor() { return author; } /** * @param author the author to set */ public void setAuthor(String author) { this.author = author; } /** * @return the content */ public String getContent() { return content; } /** * @param content the content to set */ public void setContent(String content) { this.content = content; } /** * @return the createTime */ public Date getCreateTime() { return createTime; } /** * @param createTime the createTime to set */ public void setCreateTime(Date createTime) { this.createTime = createTime; } /** * @return the modifyTime */ public Date getModifyTime() { return modifyTime; } /** * @param modifyTime the modifyTime to set */ public void setModifyTime(Date modifyTime) { this.modifyTime = modifyTime; } /** * @return the createUser */ public String getCreateUser() { return createUser; } /** * @param createUser the createUser to set */ public void setCreateUser(String createUser) { this.createUser = createUser; } /** * @return the createUserId */ public String getCreateUserId() { return createUserId; } /** * @param createUserId the createUserId to set */ public void setCreateUserId(String createUserId) { this.createUserId = createUserId; } /** * @return the modifyUser */ public String getModifyUser() { return modifyUser; } /** * @param modifyUser the modifyUser to set */ public void setModifyUser(String modifyUser) { this.modifyUser = modifyUser; } /** * @return the modifyUserId */ public String getModifyUserId() { return modifyUserId; } /** * @param modifyUserId the modifyUserId to set */ public void setModifyUserId(String modifyUserId) { this.modifyUserId = modifyUserId; } }
其中字段屬性註解@ApiModelProperty的value值就是文檔顯示該字段的意義,position是該文檔屬性顯示順序。
爲了能正常訪問接口,對接口中date類型參數、@RequestHeader的header參數中文進行解碼、對返回接口數據爲json數據(採用fastjson)須要添加WebMvcConfig配置類:
package com.dx.test.web.config; import com.dx.test.web.converter.HandlerHeaderMethodArgumentResolver; import com.dx.test.web.converter.RequestHeaderDecodeConverter; import com.dx.test.web.converter.StringToDateConverter; import com.dx.test.web.converter.StringToEnumConverterFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; import org.springframework.context.annotation.*; import org.springframework.http.converter.AbstractHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.stereotype.Controller; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import org.springframework.http.MediaType; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; /** * WebMvcConfigurerAdapter 這個類在SpringBoot2.0已過期,官方推薦直接實現 WebMvcConfigurer 這個接口 */ @Configuration @Import({WebMvcAutoConfiguration.class}) @ComponentScan( value = "com.dx.test.web", includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class) }) public class WebMvcConfig implements WebMvcConfigurer { @Bean public RequestHeaderDecodeConverter requestHeaderDecodeConverter() { return new RequestHeaderDecodeConverter(null); } @Bean public StringToDateConverter stringToDateConverter() { return new StringToDateConverter(); } /** * 使用 fastjson 代替 jackson * * @param converters */ @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { /* 先把JackSon的消息轉換器刪除. 備註:(1)源碼分析可知,返回json的過程爲: Controller調用結束後返回一個數據對象,for循環遍歷conventers,找到支持application/json的HttpMessageConverter,而後將返回的數據序列化成json。 具體參考org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor的writeWithMessageConverters方法 (2)因爲是list結構,咱們添加的fastjson在最後。所以必需要將jackson的轉換器刪除,否則會先匹配上jackson,致使沒使用 fastjson */ // for (int i = converters.size() - 1; i >= 0; i--) { // if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) { // converters.remove(i); // } // } // // FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); // //自定義fastjson配置 // FastJsonConfig config = new FastJsonConfig(); // config.setSerializerFeatures( // SerializerFeature.WriteMapNullValue, // 是否輸出值爲null的字段,默認爲false,咱們將它打開 // SerializerFeature.WriteNullListAsEmpty, // 將Collection類型字段的字段空值輸出爲[] // SerializerFeature.WriteNullStringAsEmpty, // 將字符串類型字段的空值輸出爲空字符串 // SerializerFeature.WriteNullNumberAsZero, // 將數值類型字段的空值輸出爲0 // SerializerFeature.WriteDateUseDateFormat, // SerializerFeature.DisableCircularReferenceDetect // 禁用循環引用 // ); // fastJsonHttpMessageConverter.setFastJsonConfig(config); // // // 添加支持的MediaTypes;不添加時默認爲*/*,也就是默認支持所有 // // 可是MappingJackson2HttpMessageConverter裏面支持的MediaTypes爲application/json // // 參考它的作法, fastjson也只添加application/json的MediaType // List<MediaType> fastMediaTypes = new ArrayList<>(); // fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8); // fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes); // converters.add(fastJsonHttpMessageConverter); } /** * +支持fastjson的HttpMessageConverter * * @return HttpMessageConverters */ @Bean public HttpMessageConverters fastJsonHttpMessageConverters() { AbstractHttpMessageConverter abstractHttpMessageConverter = null; //1.須要定義一個convert轉換消息的對象; FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); //2:添加fastJson的配置信息; FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures( SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat, SerializerFeature.DisableCircularReferenceDetect); fastJsonConfig.setCharset(Charset.forName("utf-8")); //3處理中文亂碼問題 List<MediaType> fastMediaTypes = new ArrayList<>(); fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8); //4.在convert中添加配置信息. fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes); fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig); HttpMessageConverter<?> converter = fastJsonHttpMessageConverter; return new HttpMessageConverters(converter); } }
WebMvcConfig中的StringToDateConverter(對date參數類型轉化)在上篇文章中有它的源碼;RequestHeaderDecodeConverter是實現對header中中文進行解碼使用,在上篇文章中也有其源碼。
添加ArticleController類,並在接口上添加文檔註解:
package com.dx.test.web.controller; import com.dx.test.module.Article; import com.dx.test.module.ArticleType; import com.dx.test.web.annonations.HeaderTestArgument; import io.swagger.annotations.*; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.stream.Collectors; @Api(value = "文章管理", tags = {"文章管理:增、刪、改、查"}) @ApiSort(value = 1) @RestController @RequestMapping(value = "/api/v1") public class ArticleController { @ApiOperation(value = "根據文章id,查詢文章詳情", code = 200, httpMethod = "GET", produces = "application/json", notes = "queryById方法定義說明:根據數據庫中article表的自增id,查詢article信息。") @ApiImplicitParams(value = { @ApiImplicitParam(name = "userId", paramType = "header", value = "操做用戶id", required = false, dataType = "String"), @ApiImplicitParam(name = "userName", paramType = "header", value = "操做用戶", required = false, dataType = "String"), @ApiImplicitParam(name = "id", paramType = "path", value = "文章id", required = true, dataType = "Long") }) @RequestMapping(value = {"/article/{id}"}, method = {RequestMethod.GET}, produces = {MediaType.APPLICATION_JSON_VALUE}) @ResponseBody public Article getById( @RequestHeader(value = "userId", required = false) String userId, @RequestHeader(value = "userName", required = false) String userName, @PathVariable(value = "id", required = true) Long id, Article article) { List<Article> articles = new ArrayList<>(); articles.add(new Article(1L, "文章1", "", "", new Date())); articles.add(new Article(2L, "文章2", "", "", new Date())); articles.add(new Article(3L, "文章3", "", "", new Date())); articles.add(new Article(4L, "文章4", "", "", new Date())); System.out.println(userName); return articles.get(0); } @ApiOperation(value = "查詢文章列表", code = 200, httpMethod = "GET", produces = "application/json", notes = "queryById方法定義說明:根據title檢索文章,返回文章列表。") @ApiImplicitParams(value = { @ApiImplicitParam(name = "userId", paramType = "header", value = "操做用戶id", required = false, dataType = "String"), @ApiImplicitParam(name = "userName", paramType = "header", value = "操做用戶", required = false, dataType = "String"), @ApiImplicitParam(name = "title", paramType = "query", value = "文章標題檢索值", required = false, dataType = "String"), @ApiImplicitParam(name = "articleType", paramType = "query", value = "文章類型", required = false, dataType = "ArticleType"), @ApiImplicitParam(name = "createTime", paramType = "query", value = "文章發佈時間", required = false, dataType = "Date") }) @RequestMapping(value = {"/articles"}, method = {RequestMethod.GET}, produces = {MediaType.APPLICATION_JSON_VALUE}) @ResponseBody public List<Article> queryList( @RequestHeader(value = "userId", required = false) String userId, @RequestHeader(value = "userName", required = false) String userName, @RequestParam(value = "title", required = false) String title, @RequestParam(value = "articleType",required = false) ArticleType articleType, @RequestParam(value = "createTime", required = false) Date createTime) { System.out.println(createTime); List<Article> articles = new ArrayList<>(); articles.add(new Article(1L, "文章1", "", "", new Date())); articles.add(new Article(2L, "文章2", "", "", new Date())); articles.add(new Article(3L, "文章3", "", "", new Date())); articles.add(new Article(4L, "文章4", "", "", new Date())); return articles.stream().filter(s -> s.getTitle().contains(title)).collect(Collectors.toList()); } @ApiOperation(value = "刪除文章", code = 200, httpMethod = "HEAD", produces = "application/json", notes = "queryById方法定義說明:刪除文章") @ApiImplicitParams(value = { @ApiImplicitParam(name = "userId", paramType = "header", value = "操做用戶id", required = false, dataType = "String"), @ApiImplicitParam(name = "userName", paramType = "header", value = "操做用戶", required = false, dataType = "String"), @ApiImplicitParam(name = "id", paramType = "query", value = "文章id", required = true, dataType = "Long") }) @RequestMapping(value = {"/article/delete"}, method = {RequestMethod.HEAD}, produces = {MediaType.APPLICATION_JSON_VALUE}) @ResponseBody public Integer delete(@RequestHeader(value = "userId", required = false) String userId, @RequestHeader(value = "userName", required = false) String userName, @RequestParam(value = "id", required = true) Long id) { return 1; } }
打開App入口類,右鍵運行,運行起來後,在瀏覽器中輸入http://localhost:8080/doc.html,回車會顯示以下界面:
到此本篇文章已經結束,應該按照這些步驟均可以配置成功。
若是swagger啓動後,點擊swagger頁面出現錯誤「java.lang.NumberFormatException: For input string: ""」,解決方案請參考《https://www.bookstack.cn/read/swagger-bootstrap-ui/39.md》