《Spring Boot编程实战与面试指南》

06-02:Spring Boot集成Swagger3

 


前言

Swagger是一个开源框架,也是一个用于为消费者创建、更新和共享“OpenAPI定义”的单独工具。和传统方式相比,Swagger开源工具集能够大幅度简化API开发过程,从而帮助个人用户、团队和企业大规模高效地生产API。

官网地址:https://swagger.io/

wxjava 集成springboot springboot集成swagger3_aitegu

案例地址:https://petstore.swagger.io/

wxjava 集成springboot springboot集成swagger3_AT阿宝哥_02

在Spring Boot项目中集成Swagger和Spring Mvc中集成相比,更加的简单,必要工作只有两个:

  1. 在pox.xml中添加Swagger3的starter依赖包;
  2. 在Spring Boot主程序类上添加@EnableOpenApi开关注解。

1、创建项目

在Idea中,通过Spring Initializr创建项目。

wxjava 集成springboot springboot集成swagger3_wxjava 集成springboot_03

wxjava 集成springboot springboot集成swagger3_wxjava 集成springboot_04

wxjava 集成springboot springboot集成swagger3_wxjava 集成springboot_05

wxjava 集成springboot springboot集成swagger3_编程自学与面试指南_06


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>

wxjava 集成springboot springboot集成swagger3_艾特谷_07


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

wxjava 集成springboot springboot集成swagger3_wxjava 集成springboot_08

wxjava 集成springboot springboot集成swagger3_aitegu_09

wxjava 集成springboot springboot集成swagger3_编程自学与面试指南_10


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、浏览器

wxjava 集成springboot springboot集成swagger3_aitegu_11


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