Swagger简介与在Spring Boot中使用

什么是Swagger?

事一个强大的工具,功能有:
  1. 生成api文档:
    一个类有什么方法、什么属性?
    类里边某个方法的作用、属性的意义?
    一个或多个(重载)方法的参数是什么,返回值是什么,方法的作者对方法的描述又是什么?
  生成Mock.js模拟数据:
    提供给前端开发人员使用。
  生成API测试页:
    同时提供给前端、后端、测试人员。
  2. 情境:
    从前:
      只能预先写好/生成文档,但是如果有改动,API的调用者拿到的就不是最新的文档,可能会出错。
    伟大转折:
      路人A想,文档不应该提前写好的,而是在用户请求文档时才实时生成文档,如此必定保证用户拿到的是最新版的文档,但是该怎么做?
    自从有了Swagger:
      API调用文档和测试框架可以靠它生成了!

与Spring Boot整合

导入依赖

方法一:导入官方指定的包

<!-- swagger -->
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
 <groupId>io.springfox</groupId>
 <artifactId>springfox-swagger2</artifactId>
 <version>2.9.2</version>
</dependency>
<!-- swagger-ui -->
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
 <groupId>io.springfox</groupId>
 <artifactId>springfox-swagger-ui</artifactId>
 <version>2.9.2</version>
</dependency>
<!-- JSON API documentation for spring based applications -->
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-bean-validators -->
<dependency>
 <groupId>io.springfox</groupId>
 <artifactId>springfox-bean-validators</artifactId>
 <version>2.9.2</version>
</dependency>

方法二:使用Starter

<!-- https://mvnrepository.com/artifact/com.spring4all/swagger-spring-boot-starter -->
<dependency>
    <groupId>com.spring4all</groupId>
    <artifactId>swagger-spring-boot-starter</artifactId>
    <version>1.9.0.RELEASE</version>
</dependency>

配置

创建配置类SwaggerConfig:

package config;

import org.springframework.context.annotation.Configuration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * Swagger配置类
 */
@Configuration // 声明此类为配置类
@EnableSwagger2 // 启用Swagger
public class SwaggerConfig {

    /**
     * 配置生成RESTful api的测试接口
     * @return Docket
     */
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(this.apiInfo()).select().apis(RequestHandlerSelectors.basePackage("controller")).paths(PathSelectors.any()).build();
    }

    /**
     * 配置API上显示的信息
     * @return ApiInfo
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder().title("Swagger Test").contact(new Contact("creepyCaller", "https://github.com/creepyCaller", "")).version("0.0.1").description("API 描述文档").build();
    }

}

在Spring Boot的main方发上的@SpringBootApplication里添加属性scanBasePackages:

@SpringBootApplication(scanBasePackages = 这里填写包名)
public class BootStrap {

    public static void main(String[] args) {
        SpringApplication.run(BootStrap.class, args);
    }

}

测试是否导入成功

  1. 启动Spring Boot
  2. 访问地址http://localhost:8080/swagger-ui.html
  3. 看看是否加载成功:

使用对类/方法的注解令其自动生成文档(常用注解)

@Api()

@ApiOperation()

注解在方法上,说明方法的作用,每一个URI资源的定义

@ApiImplicitParams({...})

注解在方法上,包含一组ApiImplicitParam

@ApiImplicitParam()

注解在方法上(如果只需要一个参数)。
注解在@ApiImplicitParams中(多个参数)。
指定一个HTTP请求参数的配置信息。
name:参数名
value:参数的汉字说明、解释
required:参数是否必须传
paramType:参数放在哪个地方
 = header --> 请求参数的获取:@RequestHeader
 = query --> 请求参数的获取:@RequestParam
 = path --> 请求参数的获取:@PathVariable
dataType:参数类型,默认String,其它值dataType="Integer"  
defaultValue:参数的默认值

综合示例

对订单这个资源的五个HTTP动词具体实现、Swagger注解的示例:

package controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import domain.Want;
import model.ResultModel;
import service.WantService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;

@Controller
@RequestMapping("/wants")
@Api("订单控制器,用于对订单资源的GET、POST、PATCH、PUT、DELETE操作")
public class WantsController {

    private static final int PAGE_SIZE = 10;

    private final WantService wantService;

    private ObjectMapper mapper;

    public WantsController(WantService wantService) {
        this.mapper = new ObjectMapper();
        this.wantService = wantService;
    }

    /**
     * 异步加载want
     * @param page 第 {page} 页
     * @return 包含第 {page} 页的订单信息的JSON字符串
     * */
    @GetMapping(value = "/page/{page}")
    @ApiOperation(value = "获取第{page}页的订单列表", notes = "返回Page<T>对象")
    @ApiImplicitParam(name = "page", required = false, dataType = "Integer", paramType = "path")
    public ResponseEntity<ResultModel> getWantPage(@PathVariable(value = "page") Integer page) {
        Page<Want> pager = null;
        if (page < 1) {
            // 如果请求的页号小于1,则到第一页
            pager = wantService.findAll(0, PAGE_SIZE);
        } else {
            pager = wantService.findAll(page - 1, PAGE_SIZE);
            int total = pager.getTotalPages();
            if (page > total) {
                // 如果请求的页号大于总页数,则到最后一页
                pager = wantService.findAll(total - 1, PAGE_SIZE);
            }
        }
        return new ResponseEntity<>(ResultModel.ok(1, pager), HttpStatus.OK);
    }

    @GetMapping(value = "/{id}")
    @ApiOperation(value = "获取id为{id}的订单详情", notes = "返回更新后的订单,标准的返回格式:{\"id\":1,\"name\":\"62LT\",\"amount\":0,\"price\":0.0,\"remark\":\"无\",\"date\":\"2019-10-09T10:23:56.000+0000\",\"status\":0}")
    @ApiImplicitParam(name = "id", dataType = "Integer", paramType = "path") // paramType = "path"用于@PathVariable注解的参数的获取
    public ResponseEntity<ResultModel> getWant(@PathVariable(value = "id") Integer id) {
        return new ResponseEntity<>(ResultModel.ok(1, wantService.findById(id)), HttpStatus.OK);
    }

    @ApiOperation(value = "完全更新id为{id}的订单", notes = "传入一个完整描述订单的POJO,返回更新后的订单,标准的返回格式:{\"id\":1,\"name\":\"62LT\",\"amount\":0,\"price\":0.0,\"remark\":\"无\",\"date\":\"2019-10-09T10:23:56.000+0000\",\"status\":0}")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", dataType = "Integer", paramType = "path"),
            @ApiImplicitParam(name = "param", dataType = "String", paramType = "query")
    })
    @PutMapping(value = "/{id}")
    public ResponseEntity<ResultModel> updateWantComplete(@PathVariable(value = "id") Integer id, @RequestParam("param") String param) {
        Want want = null;
        try {
            want = mapper.readValue(param, Want.class); // 传入的JSON字符串转为对象
        } catch (IOException e) {
            return new ResponseEntity<>(ResultModel.error(0), HttpStatus.OK);
        }
        return new ResponseEntity<>(ResultModel.ok(1, wantService.update(id, want)), HttpStatus.OK);
    }

    @ApiOperation(value = "部分更新id为{id}的订单", notes = "传入部分描述订单的POJO,返回更新后的订单,标准的返回格式:{\"id\":1,\"name\":\"62LT\",\"amount\":0,\"price\":0.0,\"remark\":\"无\",\"date\":\"2019-10-09T10:23:56.000+0000\",\"status\":0}")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", dataType = "Integer", paramType = "path"),
            @ApiImplicitParam(name = "param", dataType = "String", paramType = "query")
    })
    @PatchMapping(value = "/{id}")
    public ResponseEntity<ResultModel> updateWantPart(@PathVariable(value = "id") Integer id, @RequestParam("param") String param) {
        Want want = null;
        try {
            want = mapper.readValue(param, Want.class); // 传入的JSON字符串转为对象
        } catch (IOException e) {
            return new ResponseEntity<>(ResultModel.error(0), HttpStatus.OK);
        }
        return new ResponseEntity<>(ResultModel.ok(1, wantService.update(id, want)), HttpStatus.OK);
    }

    @ApiOperation(value = "删除id为{id}的订单", notes = "无返回")
    @ApiImplicitParam(name = "id", dataType = "Integer", paramType = "path")
    @DeleteMapping(value = "/{id}")
    public ResponseEntity<ResultModel> deleteWant(@PathVariable(value = "id") Integer id) {
        wantService.delete(new Want(id));
        return new ResponseEntity<>(ResultModel.ok(1), HttpStatus.OK);
    }

}

Swagger使用导论

  1. 启动Spring Boot
  2. 访问地址http://localhost:8080/swagger-ui.html
  3. 点开wants-controller,这里就做PATCH的示范把
  4. 点开 [PATCH /wants/{id} ...]后,点击[Try it out]
  5. 因为这里是PATCH,所以不需要在param表示的JSON对象中表示所有属性
  6. 这里先去[GET /wants/{id}]里获取id = 1的表项的数据

    在id框的Description中输入1,表示请求{ServiceRoot}/wants/1,点击[Execute]

    可以看到,这个请求成功的完成了,响应报文的响应体中,是自定义的响应实体,id为1的订单的JSON对象在实体的content中可以找到。
  7. 在PATCH中修改它的名字

    根据返回格式酌情配置传入后端的JSON对象,点击[Execute]。

    可以看到,这个PATCH动作已经成功的完成了,返回了修改后的对应订单的JSON格式对象。
  8. 为了确定PATCH动作成功,笔者决定再访问一次id为1的订单
    与上述不同的是这次笔者决定直接在浏览器地址栏请求,访问http://localhost:8080/wants/1后,浏览器接收到了这些字符串:
{"code":"OK","timestamp":1571212229214,"status":1,"message":"success","content":{"id":1,"name":"62式轻型坦克","amount":0,"price":0.0,"remark":"无","date":"2019-10-09T10:23:56.000+0000","status":0}}

可以在它的content的name中看到,名字已经成功的修改,说明PATCH动作成功完成。

附0:不用Spring MVC的方法传入参数(..., HttpSession session)获取HttpSession的方法

因为使用Swagger测试需要手动指定传入的参数,但是session和model这种东西基本不可能靠手敲来实现,所以需要把他们从方法的参量表中移走,令辟一条路实现他们。

在控制器中使用构造器传入HttpSession对象:
private final HttpSession session;

public XXXController(HttpSession session) {
    this.session = session;
}

之后就可以在这个控制器中的任意类访问session了。

附1:不用Spring MVC的方法传入参数(..., Model model)将某个对象放入model中的方法

用注解"@ModelAttribute()",这里直接放代码,自己看吧。

package controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping(value = "/user")
@Api("访问用户的API")
public class UserController {

    /**
     * 访问{ServerRoot}/user/{username},
     * 转发至用户详情页,
     * 详情页用model传入的用户名在GET {ServerRoot}/users/{username},
     * 异步加载用户实体对应JSON对应对象,
     * 再由Vue.js绘制至DOM组件
     * @param username 地址传入的请求用户名
     * @return 需要映射的页面地址到视图解析器
     */
    @GetMapping(value = "/{username}")
    @ApiOperation(value = "获取用户名为{username}的详细信息")
    @ApiImplicitParam(name = "username", dataType = "String", paramType = "path")
    public String userInfo(@PathVariable(value = "username") @ModelAttribute(value = "username") String username) {
        // 怎么把username传到info页,info页再通过它使用Ajax从UsersController::getUserInfo加载用户信息,model?
        return "user/info";
    }

}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容