Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。
其中主要要项目如下:
-
Swagger-tools:提供各种与Swagger进行集成和交互的工具。例如模式检验、Swagger 1.2文档转换成Swagger 2.0文档等功能。
-
Swagger-core: 用于Java/Scala的的Swagger实现。与JAX-RS(Jersey、Resteasy、CXF…)、Servlets和Play框架进行集成。
-
Swagger-js: 用于JavaScript的Swagger实现。
-
Swagger-node-express: Swagger模块,用于node.js的Express web应用框架。
-
Swagger-ui:一个无依赖的HTML、JS和CSS集合,可以为Swagger兼容API动态生成优雅文档。
-
Swagger-codegen:一个模板驱动引擎,通过分析用户Swagger资源声明以各种语言生成客户端代码。
接下来就让我们开启集成之路吧!!
- 创建springboot 项目
- 引入swagger 依赖, 这里需要注意的springboot的版本跟swagger 版本直接的关系,不建议都用最新版本,很有可以会造成项目访问不了swagger UI。
<!--swagger2-->
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
- 创建swagger config类:
@Configuration
public class Swagger2Config {
@Bean
public Docket testApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("demo for test")
.genericModelSubstitutes(DeferredResult.class)
// .genericModelSubstitutes(ResponseEntity.class)
.useDefaultResponseMessages(false)
.forCodeGeneration(true)
.pathMapping("/")// base,最终调用接口后会和paths拼接在一起
.select()
.paths(regex("/api/.*"))//过滤的接口
.build()
.apiInfo(testApiInfo());
}
@Bean
public Docket demoApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("demo")
.genericModelSubstitutes(DeferredResult.class)
// .genericModelSubstitutes(ResponseEntity.class)
.useDefaultResponseMessages(false)
.forCodeGeneration(false)
.pathMapping("/")
.select()
.paths((regex("/demo/.*")))//过滤的接口
.build()
.apiInfo(demoApiInfo());
}
private ApiInfo testApiInfo() {
return new ApiInfoBuilder()
.title("SpringBoot 集成 Swagger2")//大标题
.description("Rest api document")//详细描述
.version("1.0")//版本
.termsOfServiceUrl("NO terms of service")
.contact(new Contact("Glen", "https://blog.csdn.net/qq_35623773", "[email protected]"))//作者
.license("The Apache License, Version 2.0")
.licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
.build();
}
private ApiInfo demoApiInfo() {
return new ApiInfoBuilder()
.title("SpringBoot 集成 Swagger2")//大标题
.description("Rest api document")//详细描述
.version("1.0")//版本
.termsOfServiceUrl("NO terms of service")
.contact(new Contact("Glen", "https://blog.csdn.net/qq_35623773", "[email protected]"))//作者
.license("The Apache License, Version 2.0")
.licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
.build();
}
@Bean
public Docket OnlyRestApi() {
Predicate<RequestHandler> predicate = new Predicate<RequestHandler>() {
@Override
public boolean apply(RequestHandler input) {
Class<?> declaringClass = input.declaringClass();
// 排除特定类
if (declaringClass == BasicErrorController.class)
return false;
// 包含特定的类
if(declaringClass.isAnnotationPresent(RestController.class))
return true;
// 包含特定注解的方法
if(input.isAnnotatedWith(GetMapping.class) || input.isAnnotatedWith(PostMapping.class))
return true;
return false;
}
};
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(restApiInfo())
.groupName("Only rest api.")
.useDefaultResponseMessages(false)
.select()
.apis(predicate)
.build();
}
private ApiInfo restApiInfo() {
return new ApiInfoBuilder()
.title("Only include rest api.")//大标题
.version("1.0")//版本
.build();
}
}
Docket 简介:
public Docket(DocumentationType documentationType) {
this.apiInfo = ApiInfo.DEFAULT;
this.groupName = "default";
this.enabled = true;
this.genericsNamingStrategy = new DefaultGenericTypeNamingStrategy();
this.applyDefaultResponseMessages = true;
this.host = "";
this.pathMapping = Optional.absent();
this.apiSelector = ApiSelector.DEFAULT;
this.enableUrlTemplating = false;
this.vendorExtensions = Lists.newArrayList();
this.documentationType = documentationType;
}
从构造方法中我们看出Docket 类包含11个属性,常用属性介绍:
apiInfo | API 文档相关信息,对应实体类ApiInfoBuilder, 其中包含了API title, description, termsOfServiceUrl, license, version等API 相关信息 |
---|---|
groupName | 分组名 |
applyDefaultResponseMessages | 解析response 信息, 默认为true |
host | url 前缀 |
pathMapping | 设置api根路径 |
apiSelector | 初始化并返回一个API选择构造器 ApiSelectorBuilder类的方法 |
documentationType | API content-type |
ApiSelectorBuilder类的方法:
ApiSelectorBuilder apis(Predicate selector):添加选择条件并返回添加后的ApiSelectorBuilder对象
ApiSelectorBuilder paths(Predicate selector):设置路径筛选,该方法中含一句pathSelector = and(pathSelector, selector);表明条件为相与
RequestHandlerSelectors类的方法:
Predicate any():返回包含所有满足条件的请求处理器的断言,该断言总为true
Predicate none():返回不满足条件的请求处理器的断言,该断言总为false
Predicate basePackage(final String basePackage):返回一个断言(Predicate),该断言包含所有匹配basePackage下所有类的请求路径的请求处理器
PathSelectors类的方法:
Predicate any():满足条件的路径,该断言总为true
Predicate none():不满足条件的路径,该断言总为false
Predicate regex(final String pathRegex):
当我们只需要restful 分格的api时, 我们可以对那些response是url或者vm模板的api进行相应的过滤, 可参考以下方法去实现:
@Bean
public Docket OnlyRestApi() {
Predicate<RequestHandler> predicate = new Predicate<RequestHandler>() {
@Override
public boolean apply(RequestHandler input) {
Class<?> declaringClass = input.declaringClass();
// 排除特定类
if (declaringClass == BasicErrorController.class)
return false;
// 包含特定的类
if(declaringClass.isAnnotationPresent(RestController.class))
return true;
// 包含特定注解的方法
if(input.isAnnotatedWith(GetMapping.class) || input.isAnnotatedWith(PostMapping.class))
return true;
return false;
}
};
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(restApiInfo())
.groupName("Only rest api.")
.useDefaultResponseMessages(false)
.select()
.apis(predicate)
.build();
}
private ApiInfo restApiInfo() {
return new ApiInfoBuilder()
.title("Only include rest api.")//大标题
.version("1.0")//版本
.build();
}
- 修改restController
@RestController
@Api("查询公司信息")//用来说明当前 controller 的作用
public class CompanyManagerController {
private final static Logger logger = LoggerFactory.getLogger(CompanyManagerController.class);
@Autowired
private CompanyService companyService;
@GetMapping("/search/{pageNum}/{pageSize}/company")
//api 具体描述
@ApiOperation(value = "根据关键字或者区域查询多个公司", notes = "pageSize 为零时查询所有", tags = {"公司查询"}, httpMethod = "GET")
//参数描述
//@ApiImplicitParam(value = "pageNum", defaultValue = "不能为空", required = true)
//多个参数时
@ApiImplicitParams({
@ApiImplicitParam(paramType="query", name = "pageNum", value = "页数", required = true, dataType = "int"),
@ApiImplicitParam(paramType="query", name = "pageSize", value = "页面大小", required = true, dataType = "int"),
@ApiImplicitParam(paramType="query", name = "keyword", value = "公司名称关键字", required = true, dataType = "String"),
@ApiImplicitParam(paramType="query", name = "arCode", value = "区域编号", dataType = "String"),
@ApiImplicitParam(paramType = "query", name = "arName", value = "区域名字", dataType = "String")
})
@ApiResponses({
@ApiResponse(code = 404, message = "page not found")
})
public ResponseEntity<JsonResult> findCompany(@PathVariable("pageNum") int pageNum, @PathVariable("pageSize") int pageSize,
@RequestParam(value = "keyword", required = false, defaultValue = "") String keyword,
@RequestParam(value = "arCode", required = false) String arCode,
@RequestParam(value = "arName", required = false) String arName){
logger.info(String.format("get one page company, pageSize: %d, page: %d", pageNum, pageSize));
JsonResult r = new JsonResult();
try {
verifyParames(keyword, arCode, arName);
List<CMCompany> cmCompanies = companyService.findCompany(pageNum, pageSize, keyword, arCode, arName);
r.setResult(new CompaniesListResource(cmCompanies));
r.setStatus("ok");
} catch (Exception e) {
r.setResult(e.getClass().getName() + ":" + e.getMessage());
r.setStatus("error");
e.printStackTrace();
}
return ResponseEntity.ok(r);
}
private void verifyParames(String keyword, String arCode, String arName) throws Exception {
if (StringUtils.isEmpty(keyword) && StringUtils.isEmpty(arCode) && StringUtils.isEmpty(arName)) {
logger.error("keyword, arCode and arName is all null!!!");
throw new Exception("params error!!");
}
}
@GetMapping("/company/detail")
//api 具体描述
@ApiOperation(value = "根据id查询公司具体信息", notes = "查询公司所有信息及送货地址", tags = {"公司查询"}, httpMethod = "GET")
//参数描述
@ApiImplicitParam(value = "coid", defaultValue = "不能为空", required = true)
@ApiResponses({
@ApiResponse(code = 404, message = "page not found"),
@ApiResponse(code = 400, message = "内部参数异常")
})
public ResponseEntity<JsonResult> findCompanyDetail(@RequestParam(value = "coid") String coid){
logger.info("findCompanyDetail");
JsonResult r = new JsonResult();
try {
CMCompany cmCompany = companyService.findCompany(coid);
r.setResult(new CompanyResource(cmCompany));
r.setStatus("ok");
} catch (Exception e) {
r.setResult(e.getClass().getName() + ":" + e.getMessage());
r.setStatus("error");
e.printStackTrace();
}
return ResponseEntity.ok(r);
}
}
Swagger使用的注解及其说明:
@Api:用在类上,说明该类的作用。
@ApiOperation:注解来给API增加方法说明。
@ApiImplicitParams : 用在方法上包含一组参数说明。
@ApiImplicitParam:用来注解来给方法入参增加说明。
@ApiResponses:用于表示一组响应
@ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息
l code:数字,例如400
l message:信息,例如"请求参数没填好"
l response:抛出异常的类
@ApiModel:描述一个Model的信息(一般用在请求参数无法使用@ApiImplicitParam注解进行描述的时候)
l @ApiModelProperty:描述一个model的属性
注意:@ApiImplicitParam的参数说明:
参数名 | 介绍 |
---|---|
paramType: | 指定参数放在哪个地方 |
header: | 请求参数放置于Request Header,使用@RequestHeader获取 |
query: | 请求参数放置于请求地址,使用@RequestParam获取 |
path: | (用于restful接口)–>请求参数的获取:@PathVariable |
name: | 参数名 |
dataType: | 参数类型 |
required: | 参数是否必须传 true |
value: | 说明参数的意思 |
defaultValue: | 参数的默认值 |
5.启动springBoot, 访问: http://localhost:8080/wx/swagger-ui.html#/
- 测试某一个具体的API(输入对应的参数, 点击 try it):
到这里,集成就结束了, 附上源码: https://github.com/MrGlen/wxwl
参考: https://blog.csdn.net/sanyaoxu_2/article/details/80555328
官网:https://swagger.io/
swagger 汉化:https://www.jianshu.com/p/7e543f0f0bd8
这里直接使用汉化时有问题的,需要配置静态文件资源路径:
#配置静态资源访问
spring.mvc.static-path-pattern=/static/**
spring.resources.static-locations=classpath: META-INF/resources