前言: 作为一个以前后端分离为模式开发小组, 我们每隔一段时间都进行这样一个场景: 前端人员和后端开发在一起热烈的讨论 "哎, 你这参数又变了啊","接口怎么又请求不通了啊","你再试试, 我打个断点调试一下..". 可以看到在前后端沟通中出现了不少问题. 对于这样的问题, 之前一直没有很好的解决方案, 直到它的出现, 没错... 这就是我们今天要讨论的神器: swagger, 一款致力于解决接口规范化, 标准化, 文档化的开源库, 一款真正的开发神器.
目录
一: swagger 是什么?
二: 为什么要使用 swaager?
三: 如何搭一个 swagger?
四: 如何在项目中集成 swagger
五: 使用 swagger 需要注意的问题
六: 总结
一: swagger 是什么?
Swagger 是一款 RESTFUL 接口的文档在线自动生成 + 功能测试功能软件. Swagger 是一个规范和完整的框架, 用于生成, 描述, 调用和可视化 RESTful 风格的 web 服务. 目标是使客户端和文件系统作为服务器以同样的速度来更新文件的方法, 参数和模型紧密集成到服务器. 这个解释简单点来讲就是说, swagger 是一款可以根据 resutful 风格生成的生成的接口开发文档, 并且支持做测试的一款中间软件.
二: 为什么要使用 swaager?
2.1: 对于后端开发人员来说
1: 不用再手写 WiKi 接口拼大量的参数, 避免手写错误
2: 对代码侵入性低, 采用全注解的方式, 开发简单
3: 方法参数名修改, 增加, 减少参数都可以直接生效, 不用手动维护
4: 缺点: 增加了开发成本, 写接口还得再写一套参数配置
2.2: 对于前端开发来说
1: 后端只需要定义好接口, 会自动生成文档, 接口功能, 参数一目了然
2: 联调方便, 如果出问题, 直接测试接口, 实时检查参数和返回值, 就可以快速定位是前端还是后端的问题
2.3: 对于测试
1对于某些没有前端界面 UI 的功能, 可以用它来测试接口
2操作简单, 不用了解具体代码就可以操作
3操作简单, 不用了解具体代码就可以操作
三: 如何搭一个 swagger
3.1: 引入 swagger 的依赖, 目前推荐使用 2.7.0 版本, 因为 2.6.0 版本有 bug, 而其他版本又没有经过验证
一: 引入 Swagger 依赖库
- <!-- 引入 swagger-->
- <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>
3.2:springBoot 整合 swagger
- @Configuration
- @EnableSwagger2
- public class SwaggerConfig {
- @Bean
- public Docket productApi() {
- return new Docket(DocumentationType.SWAGGER_2)
- .apiInfo(apiInfo())
- .select()
- .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) // 添加 ApiOperiation 注解的被扫描
- .paths(PathSelectors.any())
- .build();
- }
- private ApiInfo apiInfo() {
- return new ApiInfoBuilder().title("swagger 和 springBoot 整合").description("swagger 的 API 文档")
- .version("1.0").build();
- }
- }
3.3:swagger 的注解
swagger 的核心在于注解, 接下来就着重讲一下 swagger 的注解:
四: 在项目中集成 swagger
4.1: 在 controller 中使用注解
- package com.youjia.swagger.controller;
- import com.youjia.swagger.constants.CommonConstants;
- import com.youjia.swagger.model.Film;
- import com.youjia.swagger.model.ResultModel;
- import com.youjia.swagger.service.FilmService;
- import io.swagger.annotations.API;
- import io.swagger.annotations.ApiImplicitParam;
- import io.swagger.annotations.ApiImplicitParams;
- import io.swagger.annotations.ApiOperation;
- import io.swagger.annotations.ApiParam;
- import io.swagger.annotations.ApiResponse;
- import io.swagger.annotations.ApiResponses;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.util.CollectionUtils;
- import org.springframework.util.StringUtils;
- import org.springframework.Web.bind.annotation.GetMapping;
- import org.springframework.Web.bind.annotation.PathVariable;
- import org.springframework.Web.bind.annotation.PostMapping;
- import org.springframework.Web.bind.annotation.RequestBody;
- import org.springframework.Web.bind.annotation.RequestMapping;
- import org.springframework.Web.bind.annotation.RequestParam;
- import org.springframework.Web.bind.annotation.RestController;
- import javax.servlet.http.HttpServletRequest;
- import java.text.DateFormat;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.List;
- import java.util.Objects;
- /**
- * @Auther: wyq
- * @Date: 2018/12/29 14:50
- */
- @RestController
- @API(value = "电影 Controller", tags = { "电影访问接口" })
- @RequestMapping("/film")
- public class FilmController {
- @Autowired
- private FilmService filmService;
- /**
- * 添加一个电影数据
- *
- * @param
- * @return
- */
- @ApiOperation(value = "添加一部电影")
- @PostMapping("/addFilm")
- @ApiResponses(value = { @ApiResponse(code = 1000, message = "成功"), @ApiResponse(code = 1001, message = "失败"),
- @ApiResponse(code = 1002, response = Film.class,message = "缺少参数") })
- public ResultModel addFilm(@ApiParam("电影名称") @RequestParam("filmName") String filmName,
- @ApiParam(value = "分数", allowEmptyValue = true) @RequestParam("score") Short score,
- @ApiParam("发布时间") @RequestParam(value = "publishTime",required = false) String publishTime,
- @ApiParam("创建者 id") @RequestParam("creatorId") Long creatorId) {
- if (Objects.isNull(filmName) || Objects.isNull(score) || Objects.isNull(publishTime) || StringUtils
- .isEmpty(creatorId)) {
- return new ResultModel(ResultModel.failed, "参数错误");
- }
- Film filmPOM = new Film();
- filmPOM.setFilmName(filmName);
- filmPOM.setScore(score);
- DateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
- Date publishTimeDate = null;
- try {
- publishTimeDate = simpleDateFormat.parse(publishTime);
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- filmPOM.setPublishTime(publishTimeDate);
- filmPOM.setCreatorId(creatorId);
- Boolean result = filmService.addFilm(filmPOM);
- if (result) {
- return new ResultModel(CommonConstants.SUCCESSMSG);
- }
- return new ResultModel(CommonConstants.FAILD_MSG);
- }
- /**
- * 根据电影名字获取电影
- *
- * @param fileName
- * @return
- */
- @GetMapping("/getFilms")
- @ApiOperation(value = "根据名字获取电影")
- @ApiResponses(value = { @ApiResponse(code = 1000, message = "成功"), @ApiResponse(code = 1001, message = "失败"),
- @ApiResponse(code = 1002, message = "缺少参数") })
- public ResultModel getFilmsByName(@ApiParam("电影名称") @RequestParam("fileName") String fileName) {
- if (StringUtils.isEmpty(fileName)) {
- return CommonConstants.getErrorResultModel();
- }
- List<Film> films = filmService.getFilmByName(fileName);
- if (!CollectionUtils.isEmpty(films)) {
- return new ResultModel(films);
- }
- return CommonConstants.getErrorResultModel();
- }
- /**
- * 根据电影名更新
- *
- * @return
- */
- @PostMapping("/updateScore")
- @ApiOperation(value = "根据电影名修改分数")
- @ApiResponses(value = { @ApiResponse(code = 1000, message = "成功"), @ApiResponse(code = 1001, message = "失败"),
- @ApiResponse(code = 1002, message = "缺少参数") })
- public ResultModel updateFilmScore(@ApiParam("电影名称") @RequestParam("fileName") String fileName,
- @ApiParam("分数") @RequestParam("score") Short score) {
- if (StringUtils.isEmpty(fileName) || Objects.isNull(score)) {
- return CommonConstants.getErrorResultModel();
- }
- filmService.updateScoreByName(fileName, score);
- return CommonConstants.getSuccessResultModel();
- }
- /**
- * 根据电影名删除电影
- *
- * @param request
- * @return
- */
- @PostMapping("/delFilm")
- @ApiOperation(value = "根据电影名删除电影")
- @ApiImplicitParams({ @ApiImplicitParam(name = "filmName",
- value = "电影名",
- dataType = "String",
- paramType = "query",
- required = true), @ApiImplicitParam(name = "id", value = "电影 id", dataType = "int", paramType = "query") })
- public ResultModel deleteFilmByNameOrId(HttpServletRequest request) {
- // 电影名
- String filmName = request.getParameter("filmName");
- // 电影 id
- Long filmId = Long.parseLong(request.getParameter("id"));
- filmService.deleteFilmOrId(filmName,filmId);
- return CommonConstants.getSuccessResultModel();
- }
- /**
- * 根据 id 获取电影
- *
- * @param id
- * @return
- */
- @PostMapping("/{id}")
- @ApiOperation("根据 id 获取电影")
- @ApiImplicitParam(name = "id", value = "电影 id", dataType = "long", paramType = "path", required = true)
- public ResultModel getFilmById(@PathVariable Long id) {
- if (Objects.isNull(id)) {
- return CommonConstants.getLessParamResultModel();
- }
- Film film = filmService.getFilmById(id);
- if (Objects.nonNull(film)) {
- return new ResultModel(film);
- }
- return CommonConstants.getErrorResultModel();
- }
- /**
- * 修改整个电影
- *
- * @param film
- * @return
- */
- @PostMapping("/insertFilm")
- @ApiOperation("插入一部电影")
- public ResultModel insertFilm(@ApiParam("电影实体对象") @RequestBody Film film) {
- if (Objects.isNull(film)) {
- return CommonConstants.getLessParamResultModel();
- }
- Boolean isSuccess = filmService.insertFilm(film);
- if (isSuccess) {
- return CommonConstants.getSuccessResultModel();
- }
- return CommonConstants.getErrorResultModel();
- }
- }
4.2: 访问本地链接 http://localhost:8080/swagger-ui.html#/
可以看出访问的 url 都很清晰的展示在它最终的页面上, 我们打开一个方法: 可以看出方法的请求参数清晰的的罗列出来, 包括方法的返回值. 并且有一个很重要的功能, 只需要点下方的 try it out 就可以进行接口测试,
五: 使用 swagger 需要注意的问题
1: 对于只有一个 HttpServletRequest 参数的方法, 如果参数小于 5 个, 推荐使用 @ApiImplicitParams 的方式单独封装每一个参数; 如果参数大于 5 个, 采用定义一个对象去封装所有参数的属性, 然后使用 @APiParam 的方式
2默认的访问地址: ip:port/swagger-ui.HTML#/, 但是在 shiro 中, 会拦截所有的请求, 必须加上默认访问路径(比如项目中, 就是 ip:port/context/swagger-ui.HTML#/), 然后登陆后才可以看到
3在 GET 请求中, 参数在 Body 体里面, 不能使用 @RequestBody. 在 POST 请求, 可以使用 @RequestBody 和 @RequestParam, 如果使用 @RequestBody, 对于参数转化的配置必须统一
4controller 必须指定请求类型, 否则 swagger 会把所有的类型 (6 种) 都生成出来
5: swagger 在生产环境不能对外暴露, 可以使用 @Profile({"dev","prod","pre"})指定可以使用的环境
六: 总结
swagger 作为一款辅助性的工具, 能大大提升我们的和前端的沟通效率, 接口是一个非常重要的传递数据的媒介, 每个接口的签名, 方法参数都非常重要. 一个良好的文档非常重要, 如果采用手写的方式非常容易拼写错误, 而 swagger 可以自动化生成参数文档, 这一切都加快了我们的沟通效率. 并且可以替代 postman 的作用. 实在是开发编程必备良品啊.
来源: https://www.cnblogs.com/wyq178/p/10291447.html