《Spring Boot编程实战与面试指南》
06-02:Spring Boot集成Swagger3
前言
Swagger是一个开源框架,也是一个用于为消费者创建、更新和共享“OpenAPI定义”的单独工具。和传统方式相比,Swagger开源工具集能够大幅度简化API开发过程,从而帮助个人用户、团队和企业大规模高效地生产API。
官网地址:https://swagger.io/
案例地址:https://petstore.swagger.io/
在Spring Boot项目中集成Swagger和Spring Mvc中集成相比,更加的简单,必要工作只有两个:
- 在pox.xml中添加Swagger3的starter依赖包;
- 在Spring Boot主程序类上添加@EnableOpenApi开关注解。
1、创建项目
在Idea中,通过Spring Initializr创建项目。
2、pox.xml
在pom.xml中增加springfox-boot-starter 3.0.0的依赖。
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.company</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.8</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-asciidoctor</artifactId>
<version>${spring-restdocs.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
3、主程序
在Spring Boot的主启动程序上,添加注解@EnableOpenApi。
package com.company.project;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.oas.annotations.EnableOpenApi;
@EnableOpenApi
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
在application.properties中,修改服务器端口号。
### Server
server.port = 8888
4、浏览器
访问地址:http://localhost:8888/swagger-ui/index.html
5、自定义接口
5.1、控制器
package com.company.project.controller;
import com.company.project.model.ao.DivisionAo;
import com.company.project.model.vo.DivisionVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@Api(tags = "01、行政区划",hidden = true)
@RestController
@RequestMapping("/division")
public class DivisionController {
private static final Log log = LogFactory.getLog(DivisionController.class);
@ApiOperation(value = "创建一个行政区划")
@ResponseStatus(HttpStatus.CREATED)
@PostMapping("")
public DivisionVo create(@RequestBody DivisionAo divisionAo) {
log.info(divisionAo);
//业务代码
DivisionVo divisionVo = new DivisionVo();
return divisionVo;
}
@ApiOperation(value = "修改一个行政区划")
@ApiImplicitParam(name = "id", value = "行政区划ID", paramType = "path", required = true, dataType = "String", defaultValue = "610101")
@ResponseStatus(HttpStatus.ACCEPTED)
@PutMapping("/{id}")
public DivisionVo save(@PathVariable String id, @RequestBody DivisionAo divisionAo) {
return new DivisionVo();
}
@ApiOperation(value = "根据ID删除一个行政区划")
@ApiImplicitParam(name = "id", value = "行政区划ID", paramType = "path", required = true, dataType = "String", defaultValue = "610101")
@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping("/{id}")
public void del(String id) {
}
@ApiOperation(value = "获取一个行政区划")
@ApiImplicitParam(name = "id", value = "行政区划ID", paramType = "path", required = true, dataType = "String", defaultValue = "610000")
@ResponseStatus(HttpStatus.OK)
@GetMapping("/{id}")
public DivisionVo get(@PathVariable String id) {
return new DivisionVo();
}
@ApiOperation(value = "分页获取多个行政区划")
@ApiImplicitParams({
@ApiImplicitParam(name = "start", value = "起始位置", paramType = "query", required = true, dataType = "Integer", example = "1"),
@ApiImplicitParam(name = "end", value = "结束位置", paramType = "query", required = true, dataType = "Integer", example = "10")
})
@ResponseStatus(HttpStatus.OK)
@GetMapping("")
public List<DivisionVo> list(Integer start, Integer end) {
DivisionVo divisionVo = new DivisionVo();
List<DivisionVo> divisionVoList = new ArrayList<>();
divisionVoList.add(divisionVo);
return divisionVoList;
}
@ApiOperation(value = "根据经度和纬度定位出一个区划")
@ApiImplicitParams({
@ApiImplicitParam(name = "lat", value = "经度", paramType = "query", required = true, dataType = "String", defaultValue = "34.263161"),
@ApiImplicitParam(name = "lng", value = "纬度", paramType = "query", required = true, dataType = "String", defaultValue = "108.948024")
})
@ResponseStatus(HttpStatus.OK)
@GetMapping("/current-Lat-Lng")
public DivisionVo currentLatAndLng(Double lat, Double lng) {
return new DivisionVo();
}
@ApiOperation(value = "根据桌台ID定位出一个区划")
@ApiImplicitParam(name = "shopTableId", value = "桌台ID", paramType = "query", required = true, dataType = "String", defaultValue = "108")
@ResponseStatus(HttpStatus.OK)
@GetMapping("/current-shop-table")
public DivisionVo currentShopTable(String shopTableId) {
return new DivisionVo();
}
@ApiOperation(value = "根据ID获取子级行政区划")
@ApiImplicitParam(name = "parentId", value = "父级编码", paramType = "path", required = true, dataType = "String", defaultValue = "610000")
@ResponseStatus(HttpStatus.OK)
@GetMapping("/children")
public List<DivisionVo> getChildren(String parentId) {
DivisionVo divisionVo = new DivisionVo();
List<DivisionVo> divisionVoList = new ArrayList<>();
divisionVoList.add(divisionVo);
return divisionVoList;
}
}
5.2、vo
package com.company.project.model.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@Data
public class DivisionVo {
@ApiModelProperty(value = "1、行政区划ID(code)", position = 1, required = true, dataType = "String", example = "610100")
private String id;
@ApiModelProperty(value = "2、编码", position = 2, required = true, dataType = "String", example = "610100")
private String code;
@ApiModelProperty(value = "3、父级编码", position = 3, required = true, dataType = "String", example = "610000")
private String parent_id;
@ApiModelProperty(value = "4、名称", position = 4, required = true, dataType = "String", example = "西安市")
private String name;
@ApiModelProperty(value = "5、简称", position = 5, required = true, dataType = "String", example = "西安")
private String shortName;
@ApiModelProperty(value = "6、合并名称", position = 6, required = true, dataType = "String", example = "中国,陕西省,西安市")
private String mergerName;
@ApiModelProperty(value = "7、级别", position = 7, required = true, dataType = "String", example = "2")
private String levelType;
@ApiModelProperty(value = "8、经度", position = 8, required = true, dataType = "String", example = "34.263161")
private String lat;
@ApiModelProperty(value = "9、纬度", position = 9, required = true, dataType = "String", example = "108.948024")
private String lng;
@ApiModelProperty(value = "10、拼音", position = 10, required = false, dataType = "String", example = "Xi'an")
private String pinYin;
@ApiModelProperty(value = "11、城市区号", position = 11, required = false, dataType = "String", example = "029")
private String cityCode;
@ApiModelProperty(value = "12、邮政编码", position = 12, required = false, dataType = "String", example = "710003")
private String zipCode;
@ApiModelProperty(value = "13、序号", position = 13, required = false, dataType = "String", example = "0")
private String serialNumber;
@ApiModelProperty(value = "14、状态", position = 14, required = false, dataType = "String", example = "0")
private int status;
@ApiModelProperty(value = "15、版本", position = 15, required = false, dataType = "String", example = "1")
private int version;
@ApiModelProperty(value = "16、创建时间", position = 16, required = false, dataType = "String", example = "2021-08-06 12:31:50,041")
private Date createTime;
@ApiModelProperty(value = "17、更新时间", position = 17, required = false, dataType = "String", example = "2021-08-06 12:31:50,041")
private Date updateTime;
}
5.3、ao
package com.company.project.model.ao;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class DivisionAo {
@ApiModelProperty(value = "1、行政区划ID(code)", position = 1, required = true, dataType = "String", example = "610100")
private String id;
@ApiModelProperty(value = "2、编码", position = 2, required = true, dataType = "String", example = "610100")
private String code;
@ApiModelProperty(value = "3、父级编码", position = 3, required = true, dataType = "String", example = "610000")
private String parent_id;
@ApiModelProperty(value = "4、名称", position = 4, required = true, dataType = "String", example = "西安市")
private String name;
@ApiModelProperty(value = "5、简称", position = 5, required = true, dataType = "String", example = "西安")
private String shortName;
@ApiModelProperty(value = "6、合并名称", position = 6, required = true, dataType = "String", example = "中国,陕西省,西安市")
private String mergerName;
@ApiModelProperty(value = "7、级别", position = 7, required = true, dataType = "String", example = "2")
private String levelType;
@ApiModelProperty(value = "8、经度", position = 8, required = true, dataType = "String", example = "34.263161")
private String lat;
@ApiModelProperty(value = "9、纬度", position = 9, required = true, dataType = "String", example = "108.948024")
private String lng;
@ApiModelProperty(value = "10、拼音", position = 10, required = false, dataType = "String", example = "Xi'an")
private String pinYin;
@ApiModelProperty(value = "11、城市区号", position = 11, required = false, dataType = "String", example = "029")
private String cityCode;
@ApiModelProperty(value = "12、邮政编码", position = 12, required = false, dataType = "String", example = "710003")
private String zipCode;
@ApiModelProperty(value = "13、序号", position = 13, required = false, dataType = "String", example = "0")
private String serialNumber;
}
5.4、浏览器
6、项目首页
在resources/static目录下创建一个index.html文件,作为我们的项目首页。
访问地址:
- http://localhost:8888/
- http://localhost:8888/index.html
<!--
Created by IntelliJ IDEA.
User: goldenunion@qq.com
Date: 2021/8/7
Time: 17:19
To change this template use File | Settings | File Templates.
-->
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="author" content="goldenunion@qq.com"/>
<meta name="Keywords" content="WWW,HTML,CSS,Javascript,XML,Json,JavaEE,Jsp,SpringMvc"/>
<meta name="Description" content="该文档演示了JSP文档的基本结构。"/>
<title>项目首页</title>
</head>
<body bgcolor="deepskyblue">
<h2>项目首页</h2>
<dl>
<dt>
<a href="http://localhost:8888/swagger-ui/index.html">Swagger-ui</a>
</dt>
<dd>
<code>
...
</code>
</dd>
<dt>
<a href="http://localhost:8888/v3/api-docs">Api-docs</a>
</dt>
<dd>
<code>
...
</code>
</dd>
</dl>
</body>
</html>
7、常见问题
7.1、Unable to interpret the implicit parameter configuration with dataType: Integer, dataTypeClass: class java.lang.Void
...
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.2)
2021-08-07 15:31:55.698 INFO 17728 --- [ main] com.company.project.MainApplication : Starting MainApplication using Java 1.8.0_281 on goldenunion with PID 17728 (E:\Projects-Idea\SpringBoot1001Swagger3\target\classes started by Knight in E:\Projects-Idea\SpringBoot1001Swagger3)
2021-08-07 15:31:55.703 INFO 17728 --- [ main] com.company.project.MainApplication : No active profile set, falling back to default profiles: default
2021-08-07 15:31:57.144 INFO 17728 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8888 (http)
2021-08-07 15:31:57.152 INFO 17728 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-08-07 15:31:57.152 INFO 17728 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.48]
2021-08-07 15:31:57.244 INFO 17728 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-08-07 15:31:57.245 INFO 17728 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1460 ms
2021-08-07 15:31:57.733 INFO 17728 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page: class path resource [static/index.html]
2021-08-07 15:31:57.827 INFO 17728 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8888 (http) with context path ''
2021-08-07 15:31:58.199 WARN 17728 --- [ main] d.s.r.o.OperationImplicitParameterReader : Unable to interpret the implicit parameter configuration with dataType: String, dataTypeClass: class java.lang.Void
2021-08-07 15:31:58.203 WARN 17728 --- [ main] d.s.r.o.OperationImplicitParameterReader : Unable to interpret the implicit parameter configuration with dataType: String, dataTypeClass: class java.lang.Void
2021-08-07 15:31:58.208 WARN 17728 --- [ main] d.s.r.o.OperationImplicitParameterReader : Unable to interpret the implicit parameter configuration with dataType: String, dataTypeClass: class java.lang.Void
2021-08-07 15:31:58.212 WARN 17728 --- [ main] d.s.r.o.OperationImplicitParameterReader : Unable to interpret the implicit parameter configuration with dataType: String, dataTypeClass: class java.lang.Void
2021-08-07 15:31:58.217 WARN 17728 --- [ main] d.s.r.o.OperationImplicitParameterReader : Unable to interpret the implicit parameter configuration with dataType: String, dataTypeClass: class java.lang.Void
2021-08-07 15:31:58.225 WARN 17728 --- [ main] d.s.r.o.OperationImplicitParameterReader : Unable to interpret the implicit parameter configuration with dataType: String, dataTypeClass: class java.lang.Void
2021-08-07 15:31:58.232 WARN 17728 --- [ main] d.s.r.o.OperationImplicitParameterReader : Unable to interpret the implicit parameter configuration with dataType: Integer, dataTypeClass: class java.lang.Void
2021-08-07 15:31:58.232 WARN 17728 --- [ main] d.s.r.o.OperationImplicitParameterReader : Unable to interpret the implicit parameter configuration with dataType: Integer, dataTypeClass: class java.lang.Void
2021-08-07 15:31:58.237 WARN 17728 --- [ main] d.s.r.o.OperationImplicitParameterReader : Unable to interpret the implicit parameter configuration with dataType: String, dataTypeClass: class java.lang.Void
2021-08-07 15:31:58.254 INFO 17728 --- [ main] com.company.project.MainApplication : Started MainApplication in 3.158 seconds (JVM running for 5.519)
原因分析:
@ApiOperation(value = "根据ID删除一个行政区划")
@ApiImplicitParam(name = "id", value = "行政区划ID", paramType = "path", required = true, dataType = "String", defaultValue = "610101")
@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping("/{id}")
public void del(String id) {
}
一般对于integer、long、string这样的类型,会遇到Unable to interpret the implicit parameter configuration with dataType: long, dataTypeClas的错误。
解决办法:
@ApiOperation(value = "根据ID删除一个行政区划")
@ApiImplicitParam(name = "id", value = "行政区划ID", paramType = "path", required = true, dataTypeClass = java.lang.String.class, defaultValue = "610101")
@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping("/{id}")
public void del(String id) {
}
使用dataTypeClass = java.lang.String.class代替原来的dataType = "String" 。
8、参考网址
Swagger 2.X Getting started
swagger.io
Swagger Open Source Tools
Swagger UI
SpringFox
Springfox Reference Documentation