一、Swagger介绍
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Api生成文档工具。在项目中集成这个工具,可以使其根据我们自己配置得信息自动为我们生成一个api文档展示页,可以在浏览器中直接访问查看项目中的接口信息,同时也可以测试每个api接口。Swagger生成的api文档是实时更新的,你写的api接口有任何的改动都会在文档中及时的表现出来。
二、SpringMVC整合Swagger2
1、添加Swagger2 jar包依赖
<properties>
<swagger2.version>2.9.2</swagger2.version>
<spring.version>5.1.5.RELEASE</spring.version>
<servlet-api.version>2.5</servlet-api.version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>${servlet-api.version}</version>
<scope>provided</scope>
</dependency>
<!-- Swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger2.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger2.version}</version>
</dependency>
</dependencies>
2、创建Swagger2Config配置类
package com.swagger.config;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 //定义配置类,让Spring来加载该类的配置,等价于XML中配置beans;
@EnableSwagger2 //表示启用Swagger2
public class Swagger2Config {
@Bean //用@Bean标注方法等价于XML中配置bean。
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
//初始化并返回一个API选择构造器
.select()
//表示只扫描被@ApiOperation注解标识过的api接口 (即:方法)
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
//设置路径筛选
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("SpringMVC中使用Swagger2构建RESTful APIs")
.description("Swagger2 Api文档")
//联系人信息
.contact(new Contact("用户名","网站url","邮箱"))
.version("1.0.0")
.build();
}
}
这里需要注意这行代码:apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)),这行代码说明我们需要扫描哪些接口,我这行意思是 表示扫描被@ApiOperation注解标识过的api接口 (即:方法)。其扫描方式总结如下:
apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) 表示扫描被@ApiOperation注解标识过的api接口 (即:方法)
apis(RequestHandlerSelectors.withClassAnnotation(Api.class)) 表示扫描带@Api注解的接口类
apis(RequestHandlerSelectors.basePackage("com.swagger.controller")) 表示扫描包下的所有接口类
3、配置application-swagger2.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd">
<!--1、开启springmvc注解-->
<mvc:annotation-driven/>
<!--2、扫描controller 和 config(把 UserController服务 和 Swagger2Config配置 注入到Spring) -->
<context:component-scan base-package="com.swagger"/>
<!--3、swagger2 静态资源交由 spring 管理映射(springfox-swagger-ui.jar 为静态资源包)-->
<mvc:resources mapping="swagger-ui.html" location="classpath:/META-INF/resources/"/>
<mvc:resources mapping="/webjars/**" location="classpath:/META-INF/resources/webjars/"/>
</beans>
4、编写web.xml (注意:这里的swagger2.properties配置文件是放在config配置文件夹下,如果在config配置文件夹又创建了一个用于存放的文件夹,那么classpath:“你创建的文件夹名称/配置文件名称”)
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app version="2.5"
id="WebApp_ID"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>Archetype Created Web Application</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- post乱码过滤器 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 前端控制器 -->
<servlet>
<servlet-name>SwaggerProject</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- contextConfigLocation不是必须的, 如果不配置contextConfigLocation, springmvc的配置文件默认在:WEB-INF/servlet的name+"-servlet.xml" -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-swagger2.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SwaggerProject</servlet-name>
<!-- 拦截所有请求jsp除外 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
5、编写Controller
package com.swagger.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Api(tags = "UserController",description = "用户管理Controller")
public class UserController {
@RequestMapping(value="/getUserName",method = RequestMethod.GET,produces= "text/plain;charset=UTF-8")
@ApiOperation(value="根据用户编号获取用户姓名",notes="仅1 和 2 有正确返回值")
@ApiImplicitParam(name="userNumber",value="用户编号",required = true,dataType = "int",paramType = "query")
public String getUserName(@RequestParam(name = "userNumber")Integer userNumber){
if(userNumber == 1){
return "张三丰";
}
else if(userNumber == 2){
return "慕容复";
}
else{
return "未知";
}
}
@RequestMapping(value="/updatePassword", method = RequestMethod.GET,produces= "text/plain;charset=UTF-8")
@ApiOperation(value = "修改用户密码",notes="根据用户id修改密码")
@ApiImplicitParams({
@ApiImplicitParam(name="userId",value="用户ID",required = true,dataType = "int",paramType = "query"),
@ApiImplicitParam(name="password",value="用户密码",required = true,dataType = "String",paramType = "query"),
@ApiImplicitParam(name="newPassword",value="用户新密码",required = true,dataType = "String",paramType = "query")
})
public String updatePassword(@RequestParam(value="userId") Integer userId, @RequestParam(value="password") String password,
@RequestParam(value="newPassword") String newPassword){
if(userId <= 0 || userId > 2){
return "未知的用户";
}
if(judgePassWord(password) || judgePassWord(newPassword)){
return "密码不能为空";
}
if(password.equals(newPassword)){
return "新旧密码不能相同";
}
return "密码修改成功!";
}
public Boolean judgePassWord(String password){
if(password == null || password == ""){
return true;
}
return false;
}
}
6、Swagger-UI界面展示
在浏览器地址栏中输入 localhost:8080/swagger-ui.html (注意:如果IP地址不是localhost 或者 端口号不是8080,换成你自己项目的配置即可),即可访问Swagger-UI界面
7、如何设置在开发和测试时开启Swagger2,在上线时禁用Swagger2,避免服务接口暴露
7.1、创建一个swagger2.properties配置文件,添加如下内容到swagger2.properties配置文件中 (如果不想禁用,设置值为true)
swagger2.enable=false
7.2、在application-swagger2.xml配置文件中,添加如下配置信息,加载 swagger2.properties配置文件 (注意:这里的swagger2.properties配置文件是放在config配置文件夹下,如果在config配置文件夹又创建了一个用于存放的文件夹,那么localtion=“你创建的文件夹名称/配置文件名称”)
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:swagger2.properties"/>
7.3、在你编写的Swagger2配置类中添加如下配置,用于读取swagger2.properties配置文件中swagger2.enable的值
@Value("${swagger2.enable}")
private boolean tag;
7.4、在你编写的Swagger2配置类中,找到创建并返回Docket方法 (即Swagger2配置类中createRestApi()方法),在此方法中的 apiInfo(apiInfo()) 后面添加 .enable(tag)
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(tag)
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}
7.5、启动项目,再次访问Swagger-UI界面,即可看到Swagger-UI界面被禁止访问
8、SpringMVC整合Swagger2的常见问题
8.1、访问Swagger-UI界面,出现弹出框,其弹出框信息如下所示
Unable to infer base url. This is common when using dynamic servlet registration or when
the API is behind an API Gateway. The base url is the root of where all the swagger
resources are served. For e.g. if the api is available at http://example.org/api/v2/api-
docs then the base url is http://example.org/api/. Please enter the location manually:
解决方案:
1、查看是否在Swagger2Config配置类中添加类了@EnableSwagger2注解
2、查看Swagger2Config配置类是否注册到Spring容器中,即:Swagger2Config配置类是否被<context:component-scan base-package="包路径"/> 扫描到
8.2、访问Swagger-UI界面,没有Controller类的相关API信息,界面中显示的内容如下所示
No operations defined in spec!
解决方案:
查看Controller类是否注册到Spring容器中,即:Controller类是否被<context:component-scan base-package="包路径"/> 扫描到
三、Swagger2注解介绍
1、@Api:用在Controller类上,做为swagger文档资源
@Api:"用在Controller类上,做为swagger文档资源"
tags="用来标注该类"
description="用来对该类进行相关描述"
示例:@Api(tags="UserController",description="用户管理Controller")
2、@ApiOperation:用在请求的方法上,说明方法的作用
@ApiOperation:"用在请求的方法上,说明方法的作用"
value="说明方法的作用"
notes="对方法的备注说明"
示例:@ApiOperation(value="根据用户编号获取用户姓名",notes="仅1 和 2 有正确返回值")
3、@ApiImplicitParams:用在请求的方法上,用来包含多个@ApiImplicitParam
@ApiImplicitParam:用在请求的方法上,用来对方法的参数进行描述,以及配置
@ApiImplicitParams:用在请求的方法上,用来包含多个@ApiImplicitParam
@ApiImplicitParam:用在请求的方法上,用来对方法的参数进行描述,以及配置
name:参数名
value:对参数的解释、说明
required:参数是否必须传入
paramType:参数放在放在请求的什么地方
· header --> 放在请求头,请求参数的获取:@RequestHeader
· query --> 用于get请求的参数拼接,请求参数的获取:@RequestParam
· path --> 用于restful接口,请求参数的获取:@PathVariable
· body --> 放在请求体,请求参数的获取:@RequestBody
· form(不常用)
dataType:参数的数据类型,默认String,其它值dataType="int" 注意:如果dataType要设置为基本类型时,不要使用其对应的类类型,因为有的类类型识别不了,比如:Integer就识别不了
defaultValue:参数的默认值
示例:@ApiImplicitParams({
@ApiImplicitParam(name="userId",value="用户ID",required=true,dataType="int",paramType="query"),
@ApiImplicitParam(name="password",value="用户密码",required=true,dataType="String",paramType="query"),
@ApiImplicitParam(name="newPassword",value="用户新密码",required=true,dataType="String",paramType="query")
})
4、@ApiResponses:用于请求的方法上,用来包含多个@ApiResponse
@ApiResponse:用于请求的方法上,用来表达一个方法的响应信息
@ApiResponses:用于请求的方法上,用来包含多个@ApiResponse
@ApiResponse:用于请求的方法上,用来表达一个方法的响应信息
code:状态码,例如400
message:信息描述,例如"请求参数没填好"
response:抛出异常的类
示例:@ApiOperation(value="根据用户编号获取用户姓名",notes="仅1 和 2 有正确返回值")
@ApiResponses({
@ApiResponse(code=400,message="请求参数没填好"),
@ApiResponse(code=404,message="请求路径没有或页面跳转路径不对")
})
5、@ApiModel:用于响应类上,用于对响应类进行描述
@ApiModelProperty:用在属性上,对响应类的属性进行描述
@ApiModel:用于响应类上,用于对响应类进行描述 (这种一般用在post创建的时候,使用@RequestBody这样的场景,请求参数无法使用@ApiImplicitParam注解进行描述的时候)
description:对响应类进行描述
@ApiModelProperty:用在属性上,对响应类的属性进行描述
value:对属性进行描述
example:传参示例
示例:@ApiModel(description = "用户请求表单")
public class User {
@ApiModelProperty(value = "姓名", example = "zhangsan")
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
四、SpringBoot整合Swagger2
1、添加Swagger2 jar包依赖
<properties>
<swagger2.version>2.9.2</swagger2.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>
<!-- Swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger2.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger2.version}</version>
</dependency>
</dependencies>
2、创建Swagger2Config配置类 (与SpringMVC整合Swagger2的Swagger2Config配置类一样)
package com.swagger.config;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 //定义配置类,让Spring来加载该类的配置,等价于XML中配置beans;
@EnableSwagger2 //表示启用Swagger2
public class Swagger2Config {
@Bean //用@Bean标注方法等价于XML中配置bean。
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
//初始化并返回一个API选择构造器
.select()
//表示只扫描被@ApiOperation注解标识过的api接口 (即:方法)
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
//设置路径筛选
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("SpringMVC中使用Swagger2构建RESTful APIs")
.description("Swagger2 Api文档")
//联系人信息
.contact(new Contact("用户名","网站url","邮箱"))
.version("1.0.0")
.build();
}
}
3、编写Controller (与SpringMVC整合Swagger2的Controller类一样)
package com.swagger.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Api(tags = "UserController",description = "用户管理Controller")
public class UserController {
@RequestMapping(value="/getUserName",method = RequestMethod.GET,produces= "text/plain;charset=UTF-8")
@ApiOperation(value="根据用户编号获取用户姓名",notes="仅1 和 2 有正确返回值")
@ApiImplicitParam(name="userNumber",value="用户编号",required = true,dataType = "int",paramType = "query")
public String getUserName(@RequestParam(name = "userNumber")Integer userNumber){
if(userNumber == 1){
return "张三丰";
}
else if(userNumber == 2){
return "慕容复";
}
else{
return "未知";
}
}
@RequestMapping(value="/updatePassword", method = RequestMethod.GET,produces= "text/plain;charset=UTF-8")
@ApiOperation(value = "修改用户密码",notes="根据用户id修改密码")
@ApiImplicitParams({
@ApiImplicitParam(name="userId",value="用户ID",required = true,dataType = "int",paramType = "query"),
@ApiImplicitParam(name="password",value="用户密码",required = true,dataType = "String",paramType = "query"),
@ApiImplicitParam(name="newPassword",value="用户新密码",required = true,dataType = "String",paramType = "query")
})
public String updatePassword(@RequestParam(value="userId") Integer userId, @RequestParam(value="password") String password,
@RequestParam(value="newPassword") String newPassword){
if(userId <= 0 || userId > 2){
return "未知的用户";
}
if(judgePassWord(password) || judgePassWord(newPassword)){
return "密码不能为空";
}
if(password.equals(newPassword)){
return "新旧密码不能相同";
}
return "密码修改成功!";
}
public Boolean judgePassWord(String password){
if(password == null || password == ""){
return true;
}
return false;
}
}
4、启动SpringBoot项目,需要注意的是:你创建的Swagger2Config配置类和Controller类需要与SpringBoot启动类在同一目录下 或者 在SpringBoot启动类同一级目录下的子目录下,这样SpringBoot才能扫描到你创建的Swagger2Config配置类和Controller类。如果不在同一目录或者子目录下,那么就在SpringBoot启动类上添加@ComponentScan("包路径") 或者 @ComponentScan({"包路径1","包路径2"})
package com.swagger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringbootSwaggerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootSwaggerApplication.class, args);
}
}
5、访问Swagger-UI界面,在浏览器地址栏中输入 localhost:8080/swagger-ui.html (注意:如果IP地址不是localhost 或者 端口号不是8080,换成你自己项目的配置即可)
6、这时查看IntelliJ IDEA的输出控制台,会发现报错:java.lang.NumberFormatException: For input string: ""
报错信息如下所示:
java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) ~[na:1.8.0_05]
at java.lang.Long.parseLong(Long.java:601) ~[na:1.8.0_05]
at java.lang.Long.valueOf(Long.java:803) ~[na:1.8.0_05]
at io.swagger.models.parameters.AbstractSerializableParameter.getExample(AbstractSerializableParameter.java:412) ~[swagger-models-1.5.20.jar:1.5.20]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_05]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_05]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_05]
at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_05]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:688) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:18) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:722) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:643) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:33) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3905) [jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3219) [jackson-databind-2.9.9.jar:2.9.9]
at springfox.documentation.spring.web.json.JsonSerializer.toJson(JsonSerializer.java:38) [springfox-spring-web-2.9.2.jar:null]
at springfox.documentation.swagger2.web.Swagger2Controller.getDocumentation(Swagger2Controller.java:105) [springfox-swagger2-2.9.2.jar:null]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_05]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_05]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_05]
at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_05]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) [spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) [spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) [spring-webmvc-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) [tomcat-embed-websocket-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) [spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) [spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) [spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) [spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) [spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) [spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) [spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:853) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.21.jar:9.0.21]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_05]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_05]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.21.jar:9.0.21]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_05]
7、排除io.springfox:springfox-swagger2:2.9.2版本中的 io.swagger:swagger-annotations:1.5.20 和 io.swagger:swagger-annotations:1.5.20
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger2.version}</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
8、添加io.swagger:swagger-annotations:1.5.22 和 io.swagger:swagger-annotations:1.5.22
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.22</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.22</version>
</dependency>
9、启动SpringBoot项目,再次访问Swagger-UI界面,输出控制台不再输出java.lang.NumberFormatException: For input string: "" 错误信息
10、如何设置在开发和测试时开启Swagger2,在上线时禁用Swagger2,避免服务接口暴露
10.1、方式一:在Swagger2Config配置类的上面添加@Profile({"环境"}) 或者 @Profile({"环境1","环境2"})注解,用来标明当前的运行环境
例如:
1、我们先在resources目录下创建三个用于不同环境下的SpringBoot配置文件,SpringBoot配置文件命名约定为:application-{profile}.properties
application-dev.properties //开发环境时,使用的配置文件
application-test.properties //测试环境时,使用的配置文件
application-pro.properties //生产环境时,使用的配置文件
2、在SpringBoot默认配置文件application.properties中,添加spring.profiles.active=dev 表明当前SpringBoot项目使用开发环境下的配置文件
3、在Swagger2Config配置类的上面添加@Profile({"dev","test"})注解,表示在开发环境 和 测试环境下开启Swagger2,其他环境下禁用Swagger2
10.2、方式二:在Swagger2Config配置类的上面添加@ConditionalOnProperty(prefix = "xx1",value = {"xx2"},havingValue = "true",matchIfMissing = true)注解,表示读取application.properties配置文件中前缀为xx1,属性值为xx2的值,再与havingValue中的值作比较,如果相等则为true,不相等则为false,如果没找到前缀为xx1,属性值为xx2的值,则默认为matchIfMissing中设置的值
例如:
1、在application.properties配置文件中设置 swagger2.enable=false
2、在Swagger2Config配置类的上面添加@ConditionalOnProperty(prefix="swagger2",value={"enable"},havingValue="true",matchIfMissing=true)注解 表示读取前缀为swagger2,属性值为enable的值,然后与havingValue中的值作比较,相等则为true,不相等则为false
10.3、方式三:通过@Value("${xxx}")注解来读取application.properties配置文件中设置的 xxx属性的值,再通过enable(boolean externallyConfiguredFlag)方法设置 开启或者禁用 Swagger2
例如:
1、在application.properties配置文件中设置 swagger2.enable=false
2、使用@Value注解用于读取application.properties配置文件中 swagger2.enable属性的值
@Value("${swagger2.enable}")
private boolean tag;
3、使用enable(boolean externallyConfiguredFlag)方法设置 开启或者禁用 Swagger2
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(tag)
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}