一、概述
在阅读我这篇文章之前,请先看我的上一篇文章有承接关系,好了,这篇主要还是手把手带大家在项目中优雅的生成和管理api文档,废话不多说(框架三部曲走你),肝就完了。 技术点: swagger-ui
一、基础理论
swagger-ui是一个大包好的module,可以通过引入依赖的方式引用它,它是HTML、JavaScript、CSS的一个集合,可以通过代码的方式动态地根据指定注解生成在线文档,也就是说,只要你在项目中引入swagger,并在指定接口上加入指定注解就不用写一堆接口文档啦。 常用注解:
@API:用于修饰Controller类,生成Controler相关文档信息
@ApiOperation:用于修饰Controller类中的方法,生成接口方法相关信息
@ApiParam:用于修饰接口中的参数,生成接口参数相关文档信息
@ApiModelProperty:用于修饰实体类的属性,当实体类是请求参数或返回结果时,直接生成相关文档信息
二、环境搭建
pom文件中引入Swagger-UI依赖
<!--Swagger
-UI API文档生产工具
-->
<dependency>
<groupId>io
.springfox
</groupId
>
<artifactId>springfox
-swagger2
</artifactId
>
<version>2.7.0</version
>
</dependency
>
<dependency>
<groupId>io
.springfox
</groupId
>
<artifactId>springfox
-swagger
-ui
</artifactId
>
<version>2.7.0</version
>
</dependency
>
添加Swagger-UI的配置
package com
.rzx
.mall
.mallmylearning
.config
;
import io
.swagger
.annotations
.Api
;
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
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket
createRestApi() {
return new Docket(DocumentationType
.SWAGGER_2
)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors
.withClassAnnotation(Api
.class))
.apis(RequestHandlerSelectors
.withMethodAnnotation(ApiOperation
.class))
.paths(PathSelectors
.any())
.build()
;
}
private ApiInfo
apiInfo() {
return new ApiInfoBuilder()
.title("SwaggerUI项目测试演示")
.description("这个是一个用于演示的测试项目")
.contact(new Contact("wiki", "https://blog.csdn.net/xfxfxfxfxf666", "wiki@163.com"))
.version("1.0")
.build();
}
}
使用Swagger-UI 在控制器中添加注解配置
package com
.rzx
.mall
.mallmylearning
.controller
;
import com
.rzx
.mall
.mallmylearning
.common
.api
.CommonPage
;
import com
.rzx
.mall
.mallmylearning
.common
.api
.CommonResult
;
import com
.rzx
.mall
.mallmylearning
.mbg
.model
.PmsBrand
;
import com
.rzx
.mall
.mallmylearning
.service
.PmsBrandService
;
import io
.swagger
.annotations
.Api
;
import io
.swagger
.annotations
.ApiOperation
;
import io
.swagger
.annotations
.ApiParam
;
import org
.slf4j
.Logger
;
import org
.slf4j
.LoggerFactory
;
import org
.springframework
.beans
.factory
.annotation
.Autowired
;
import org
.springframework
.stereotype
.Controller
;
import org
.springframework
.validation
.BindingResult
;
import org
.springframework
.web
.bind
.annotation
.*
;
import javax
.validation
.Valid
;
import java
.util
.List
;
@Controller
@RequestMapping("/brand")
@Api(tags
= "PmsBrandController",description
= "品牌管理模块")
public class PmsBrandController {
@Autowired
private PmsBrandService pmsBrandService
;
private static final Logger LOGGER
= LoggerFactory
.getLogger(PmsBrandController
.class);
@RequestMapping(value
= "/listAll", method
= RequestMethod
.GET
)
@ResponseBody
@ApiOperation(value
= "获取品牌列表")
public CommonResult
<List
<PmsBrand>> getBrandList() {
return CommonResult
.success(pmsBrandService
.listAllBrand());
}
@RequestMapping(value
= "/create", method
= RequestMethod
.POST
)
@ResponseBody
@ApiOperation("创建品牌")
public CommonResult
createBrand(@RequestBody @ApiParam PmsBrand pmsBrand
) {
CommonResult commonResult
;
int count
= pmsBrandService
.createBrand(pmsBrand
);
if (count
== 1) {
commonResult
= CommonResult
.success(pmsBrand
);
LOGGER
.debug("createBrand success:{}", pmsBrand
);
} else {
commonResult
= CommonResult
.failed("操作失败");
LOGGER
.debug("createBrand failed:{}", pmsBrand
);
}
return commonResult
;
}
@RequestMapping(value
= "/update/{id}", method
= RequestMethod
.POST
)
@ResponseBody
@ApiOperation("更新品牌")
public CommonResult
updateBrand(@PathVariable("id") Long id
, @RequestBody @Valid PmsBrand pmsBrand
, BindingResult bindingResult
) {
CommonResult commonResult
;
if (bindingResult
.hasErrors()) {
commonResult
= CommonResult
.failed(bindingResult
.getFieldError().getDefaultMessage());
return commonResult
;
}
int count
= pmsBrandService
.updateBrand(id
, pmsBrand
);
if (count
== 1) {
commonResult
= CommonResult
.success(pmsBrand
);
LOGGER
.debug("updateBrand success:{}", pmsBrand
);
} else {
commonResult
= CommonResult
.failed("操作失败");
LOGGER
.debug("updateBrand failed:{}", pmsBrand
);
}
return commonResult
;
}
@RequestMapping(value
= "/delete/{id}",method
= RequestMethod
.GET
)
@ResponseBody
@ApiOperation("删除品牌")
public CommonResult
deleteBrand(@PathVariable(value
= "id", required
= true) Long id
) {
CommonResult commonResult
;
int count
= pmsBrandService
.deleteBrand(id
);
if (count
== 1) {
commonResult
= CommonResult
.success(null
);
LOGGER
.debug("deleteBrand success :id={}", id
);
} else {
commonResult
= CommonResult
.failed("操作失败");
LOGGER
.debug("deleteBrand failed :id={}",id
);
}
return commonResult
;
}
@RequestMapping(value
= "/list",method
= RequestMethod
.GET
)
@ResponseBody
@ApiOperation("根据分页获取匹配列表")
public CommonResult
<CommonPage
<PmsBrand>> listBrand(@RequestParam(value
= "pageNum",defaultValue
= "1")int pageNum
,@RequestParam(value
= "pageSize",defaultValue
= "10") int pageSize
){
List
<PmsBrand> pmsBrands
= pmsBrandService
.listBrand(pageSize
, pageNum
);
return CommonResult
.success(CommonPage
.restPage(pmsBrands
));
}
@RequestMapping(value
= "/get/{id}",method
= RequestMethod
.GET
)
@ResponseBody
@ApiOperation("根据id获取品牌")
public CommonResult
getBrand(@PathVariable(value
= "id",required
= true) Long id
){
return CommonResult
.success(pmsBrandService
.getBrand(id
));
}
}
修改mybatis generator注释生成规则(上一篇中CommentGenerator类仅仅用于生成普通属性注释,这一篇中要对这个类进行改造)
项目中CommentGenerator类为MyBatis Generator的自定义注释生成器,修改addFieldComment方法使其生成Swagger的@ApiModelProperty注解来取代原来的方法注释,添加addJavaFileComment方法,使其能在import中导入@ApiModelProperty,否则需要手动导入该类,在需要生成大量实体类时,是一件非常麻烦的事。
改造后的代码:
package com
.rzx
.mall
.mallmylearning
.mbg
;
import org
.mybatis
.generator
.api
.IntrospectedColumn
;
import org
.mybatis
.generator
.api
.IntrospectedTable
;
import org
.mybatis
.generator
.api
.dom
.java
.CompilationUnit
;
import org
.mybatis
.generator
.api
.dom
.java
.Field
;
import org
.mybatis
.generator
.api
.dom
.java
.FullyQualifiedJavaType
;
import org
.mybatis
.generator
.internal
.DefaultCommentGenerator
;
import org
.mybatis
.generator
.internal
.util
.StringUtility
;
import java
.util
.Properties
;
public class CommentGenerator extends DefaultCommentGenerator {
private boolean addRemarkComments
= false;
private static final String EXAMPLE_SUFFIX
="Example";
private static final String API_MODEL_PROPERTY_FULL_CLASS_NAME
="io.swagger.annotations.ApiModelProperty";
@Override
public void addConfigurationProperties(Properties properties
) {
super.addConfigurationProperties(properties
);
this.addRemarkComments
= StringUtility
.isTrue(properties
.getProperty("addRemarkComments"));
}
@Override
public void addFieldComment(Field field
, IntrospectedTable introspectedTable
,
IntrospectedColumn introspectedColumn
) {
String remarks
= introspectedColumn
.getRemarks();
if(addRemarkComments
&&StringUtility
.stringHasValue(remarks
)){
if(remarks
.contains("\"")){
remarks
= remarks
.replace("\"","'");
}
field
.addJavaDocLine("@ApiModelProperty(value = \""+remarks
+"\")");
}
}
private void addFieldJavaDoc(Field field
, String remarks
) {
field
.addJavaDocLine("/**");
String
[] remarkLines
= remarks
.split(System
.getProperty("line.separator"));
for(String remarkLine
:remarkLines
){
field
.addJavaDocLine(" * "+remarkLine
);
}
addJavadocTag(field
, false);
field
.addJavaDocLine(" */");
}
@Override
public void addJavaFileComment(CompilationUnit compilationUnit
) {
super.addJavaFileComment(compilationUnit
);
if(!compilationUnit
.isJavaInterface()&&!compilationUnit
.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX
)){
compilationUnit
.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME
));
}
}
}
删除之前生成的mapper.xml(如果不删除,生成的不会覆盖,有问题)重新运行Generator类,生成代码
package com
.rzx
.mall
.mallmylearning
.mbg
;
import org
.mybatis
.generator
.api
.MyBatisGenerator
;
import org
.mybatis
.generator
.config
.Configuration
;
import org
.mybatis
.generator
.config
.xml
.ConfigurationParser
;
import org
.mybatis
.generator
.internal
.DefaultShellCallback
;
import java
.io
.InputStream
;
import java
.util
.ArrayList
;
import java
.util
.List
;
public class Generator {
public static void main(String
[] args
) throws Exception
{
List
<String> warnings
= new ArrayList<String>();
boolean overwrite
= true;
InputStream is
= Generator
.class.getResourceAsStream("/generatorConfig.xml");
ConfigurationParser cp
= new ConfigurationParser(warnings
);
Configuration config
= cp
.parseConfiguration(is
);
is
.close();
DefaultShellCallback callback
= new DefaultShellCallback(overwrite
);
MyBatisGenerator myBatisGenerator
= new MyBatisGenerator(config
, callback
, warnings
);
myBatisGenerator
.generate(null
);
for (String warning
: warnings
) {
System
.out
.println(warning
);
}
}
}
生成结果
三、测试验证
重启项目,在浏览器中输入地址:http://ip:port/swagger-ui.html,最终能看到的效果:
四、总结
到这里swagger的大体集成流程我想应该讲明白了,当然一个新东西的出现,不可能就这点东西,如果需要了解跟多的配置方式,更多的注解使用可以自己查资料进行学习,笔者这里就不啰嗦了,还有一点需要注意,生产环境再怎么强调安全都不为过,上面的案例中没有对/swagger-ui.html地址的访问进行权限控制,后面讲到springSecurity和shiro时会做说明,我们线上环境是这样处理的,在一个Controller方法中重定向到/swagger-ui.html,然后我们在控制器上进行访问权限控制即可解决。