springboot+ResFul 风格API接口+MockMvc测试+Swagger2

  • ResFul 风格API接口
  • @GetMapping
  • @PostMapping
  • @DeleteMapping
  • @PutMapping
  • 模拟数据库进行测试(详细注释)
  • Get 请求
  • Post 请求
  • 剩下的接口测试
  • 小问题
  • 使用 Swagger2 测试接口
  • 1、导包
  • 2、编写 swagger2 的配置类
  • 3、swagger 的基础注解介绍
  • 4、Controller 例子
  • 5、操作流程
  • 6、自行测试
  • 文章说明


ResFul 风格API接口

@GetMapping

获取信息

@PostMapping

增加信息

@DeleteMapping

删除信息

@PutMapping

编辑信息

模拟数据库进行测试(详细注释)

// 增删改直接返回 “success” , 先方便测试
@RestController
@RequestMapping(value = "/students")
// @Api("操作student的接口")
public class StudentController2 {

    /**
     * 创建线程安全的Map,模拟 students 信息的存储
     */
    static Map<Long, Student> students = Collections.synchronizedMap(new HashMap<>());

    /**
     * 处理"/users/"的GET请求,用来获取用户列表
     *
     * @return
     */
    @GetMapping("/")
    // @ApiOperation("获取所有的学生信息")
    public List<Student> getStudentList() {
        // 还可以通过 @RequestParam 从页面中传递参数来进行查询条件或者翻页信息的传递
        System.out.println("====================getStudentList()=====================");
        return new ArrayList<>(students.values());
    }

    /**
     * 处理"/users/"的POST请求,用来创建User
     *
     * @param student
     * @return
     */
    @PostMapping("/")
    // @ApiOperation("增加一名学生信息")
    public String postUser(@RequestBody Student student) {
        System.out.println("====================postUser()=====================");
        // @RequestBody注解用来绑定通过http请求中application/json类型上传的数据
        students.put(student.getId(), student);
        return "success";
    }

    /**
     * 处理"/users/{id}"的GET请求,用来获取url中id值的User信息
     *
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    //@ApiOperation("获得一名学生的信息")
    public Student getUser(@PathVariable Long id) {
        System.out.println("====================getUser()=====================");
        // url中的id可通过@PathVariable绑定到函数的参数中
        return students.get(id);
    }

    /**
     * 处理"/users/{id}"的PUT请求,用来更新User信息
     *
     * @param id
     * @param student
     * @return
     */
    @PutMapping("/{id}")
    // @ApiOperation("编辑一名学生信息")
    public String putUser(@PathVariable Long id, @RequestBody Student student) {
        System.out.println("====================putUser()=====================");
        Student stu = students.get(id);
        stu.setName(student.getName());
        stu.setAge(student.getAge());
        students.put(id, stu);
        return "success";
    }

    /**
     * 处理"/users/{id}"的DELETE请求,用来删除User
     *
     * @param id
     * @return
     */
    @DeleteMapping("/{id}")
    // @ApiOperation("删除一名学生信息")
    // @ApiImplicitParam("根据学生id删除学生")
    public String deleteUser(@PathVariable Long id) {
        System.out.println("====================deleteUser()=====================");
        students.remove(id);
        return "success";
    }

}

Get 请求

@SpringBootTest
public class StudentControllerTest {
    private MockMvc mvc;

    @Resource
    private StudentController2 studentController2;

// 执行之前通过 MockMvcBuilders 获取 studentController2 模拟的 mvc 接口
    @BeforeEach
    public void setUp() {
        mvc = MockMvcBuilders.standaloneSetup(studentController2).build();
    }

    @Test
    public void testStudentController() throws Exception {
        RequestBuilder request;

        // 1、get 风格查询 student 列表 此处为空,没有添加信息
        // 1.1、get("接口请求访问的URL")
        request = get("/students/");
        // 1.2、 perform() 获取 ResultActions 对象(封装结果集)
        mvc.perform(request)
        		// 1.3、 andExpect() 执行完成后的断言:添加 ResultMatcher 验证规则,验证控制器执行完成后结果是否正确
        		// 1.3.1、对状态码断言:200 通过
                .andExpect(status().isOk())
                // 1.3.2、对查询结果断言:结果是否为 [] 通过
                // 1.3.3、如果不通过会抛出异常阻止后面的运行
                .andExpect(content().string(equalTo("[]")))
                // 1.4、 andDo() 执行处理器的方法
                // 1.4.1、输出整个响应结果信息
                .andDo(MockMvcResultHandlers.print())
                // 1.5、 andReturn() 表示执行完成后返回相应的结果
                // 1.5.1、配合使用 andReturn().getResponse().getContentAsString() 可以获取字符串类型的返回结果
                // 因为这里没有返回值,所以我没有打印,后面的 post 测试有结果
                .andReturn().getResponse().getContentAsString();

    }
}

打印结果

====================getStudentList()=====================

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /students/
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.liu.springboot_hello.controller.StudentController2
           Method = com.liu.springboot_hello.controller.StudentController2#getStudentList()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = []
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

Post 请求

@SpringBootTest
public class StudentControllerTest {
    private MockMvc mvc;

    @Resource
    private StudentController2 studentController2;

// 执行之前通过 MockMvcBuilders 获取 studentController2 模拟的 mvc 接口
    @BeforeEach
    public void setUp() {
        mvc = MockMvcBuilders.standaloneSetup(studentController2).build();
    }

    @Test
    public void testStudentController() throws Exception {
        RequestBuilder request;

                // 2、post 风格 提交一个 student (增加一名学生)
        request = post("/students/")
                // contentType表示具体请求中的媒体类型信息,MediaType.APPLICATION_JSON 表示互联网媒体类型的json数据格式
                .contentType(MediaType.APPLICATION_JSON)
                // 斜杠必须是转义字符 -- 》 \"
                // 添加一个 json 格式数据的内容
                // 注意:每个字段都必须要有,但不一定要填值(比如把 dongua 删掉也无妨)
                .content("{\"id\":1,\"name\":\"dongua\",\"phone\":\"123456\",\"age\":20}");
                // 
        String success = mvc.perform(request)
                .andExpect(content().string(equalTo("success")))
                .andDo(MockMvcResultHandlers.print())
                .andReturn().getResponse().getContentAsString();
        System.out.println("success = " + success);

    }
}

打印结果:对比 get请求的 .andDo(MockMvcResultHandlers.print()) 打印结果

====================postUser()=====================

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /students/
       Parameters = {}
          Headers = [Content-Type:"application/json", Content-Length:"50"]
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.liu.springboot_hello.controller.StudentController2
           Method = com.liu.springboot_hello.controller.StudentController2#postUser(Student)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"text/plain;charset=ISO-8859-1", Content-Length:"7"]
     Content type = text/plain;charset=ISO-8859-1
             Body = success
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
success = success

get post 对比图

restful 风格api restful风格接口案例_java


出了正常的不一样之外(比如Content-Length:“50”,Body = success),发现了我们的 Content-Type 并不是 "application/json",而是 Content-Type:"text/plain;charset=ISO-8859-1"

原因:我们没有指定客户端能够接收的内容类型

解决

// 添加  .accept(MediaType.APPLICATION_JSON)
request = post("/students/")
                // contentType表示具体请求中的媒体类型信息,MediaType.APPLICATION_JSON 表示互联网媒体类型的json数据格式
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON) // accept指定客户端能够接收的内容类型
                .content("{\"id\":1,\"name\":\"dongua\",\"phone\":\"123456\",\"age\":20}");

剩下的 Put 和 Del 的测试不再细说

剩下的接口测试

// 3、get获取 student 列表,应该有刚才插入的数据
        request = get("/students/");
        mvc.perform(request)
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("[{\"id\":1,\"name\":\"dongua\",\"phone\":\"123456\",\"age\":20}]")));

        // 4、put修改id为1的 student
        request = put("/students/1")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"name\":\"dongua\",\"phone\":\"123456\",\"age\":20}");
        mvc.perform(request)
                .andExpect(content().string(equalTo("success")));

        // 5、get一个id为1的 student
        request = get("/students/1");
        mvc.perform(request)
                .andExpect(content().string(equalTo("{\"id\":1,\"name\":\"dongua\",\"phone\":\"123456\",\"age\":20}")));

        // 6、del删除id为1的 student
        request = delete("/students/1");
        mvc.perform(request)
                .andExpect(content().string(equalTo("success")));

小问题

测试的时候犯的一个小错误:

@BeforeEach
    public void setUp() {
    // 我没有注入依赖,直接 new 的 controller ,但实际上,模拟测试时没有真实用到数据库连接,所以模拟测试是没有问题的
    // 但是我连接数据库测试的时候必须使用依赖注入
        mvc = MockMvcBuilders.standaloneSetup(new StudentController()).build();
    }

也就是:

@Resource
    private StudentController studentController;

其他的与模拟测试无异

使用 Swagger2 测试接口

1、导包

<!--swagger2的jar包-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!--引入视觉的样式的UI-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>

2、编写 swagger2 的配置类

注意要加上注解 @EnableSwagger2 :开启Swagger2

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
        		//.useDefaultResponseMessages(false) 跟下面这个效果一样(映射路径)
                .pathMapping("/")
                .select()
                //.apis(RequestHandlerSelectors.withClassAnnotation(Api.class)) 只监控标注了 @Api 注解的Controller 
                // 这里使用 basePackage("controller 包路径")
                .apis(RequestHandlerSelectors.basePackage("com.liu.springboot_hello.controller"))
                .paths(PathSelectors.any())
                .build().apiInfo(new ApiInfoBuilder()
                        .title("SpringBoot + Swagger2 ")
                        .description("详细信息")
                        .version("1.0")
                        .contact(new Contact("小冬瓜", "baidu.com", "110@qq.com"))
                        .license("The Apache License")
                        .licenseUrl("https://www.baidu.com").build()
                );
    }
}

3、swagger 的基础注解介绍

swagger 可以通过注解生成接口文档,包括接口名、请求方法、参数、返回信息等等。

@Api:修饰整个类,描述Controller的作用

@ApiOperation:描述一个类的一个方法,或者说一个接口

@ApiParam:单个参数描述

@ApiModel:用对象实体来作为入参

@ApiProperty:用对象接实体收参数时,描述对象的一个字段

@ApiResponse:HTTP响应其中1个描述

@ApiResponses:HTTP响应整体描述

@ApiIgnore:使用该注解忽略这个API

@ApiError :发生错误返回的信息

@ApiImplicitParam:一个请求参数

@ApiImplicitParams: 多个请求参数

4、Controller 例子

@RestController
@RequestMapping(value = "/students")
@Api("操作student的接口")
public class StudentController2 {

    /**
     * 创建线程安全的Map,模拟 students 信息的存储
     */
    static Map<Long, Student> students = Collections.synchronizedMap(new HashMap<>());

    /**
     * 处理"/users/"的GET请求,用来获取用户列表
     *
     * @return
     */
    @GetMapping("/")
    @ApiOperation("获取所有的学生信息")
    public List<Student> getStudentList() {
        // 还可以通过 @RequestParam 从页面中传递参数来进行查询条件或者翻页信息的传递
/*        students.put(1L, new Student());*/
        System.out.println("====================getStudentList()=====================");
        return new ArrayList<>(students.values());
    }

    /**
     * 处理"/users/"的POST请求,用来创建User
     *
     * @param student
     * @return
     */
    @PostMapping("/")
    @ApiOperation("增加一名学生信息")
    public String postUser(@RequestBody Student student) {
        System.out.println("====================postUser()=====================");
        // @RequestBody注解用来绑定通过http请求中application/json类型上传的数据
        students.put(student.getId(), student);
        return "success";
    }

    /**
     * 处理"/users/{id}"的GET请求,用来获取url中id值的User信息
     *
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @ApiOperation("获得一名学生的信息")
    public Student getUser(@PathVariable Long id) {
        System.out.println("====================getUser()=====================");
        // url中的id可通过@PathVariable绑定到函数的参数中
        return students.get(id);
    }

    /**
     * 处理"/users/{id}"的PUT请求,用来更新User信息
     *
     * @param id
     * @param student
     * @return
     */
    @PutMapping("/{id}")
    @ApiOperation("编辑一名学生信息")
    public String putUser(@PathVariable Long id, @RequestBody Student student) {
        System.out.println("====================putUser()=====================");
        Student stu = students.get(id);
        stu.setName(student.getName());
        stu.setAge(student.getAge());
        students.put(id, stu);
        return "success";
    }

    /**
     * 处理"/users/{id}"的DELETE请求,用来删除User
     *
     * @param id
     * @return
     */
    @DeleteMapping("/{id}")
    @ApiOperation("删除一名学生信息")
    @ApiImplicitParam("根据学生id删除学生")
    public String deleteUser(@PathVariable Long id) {
        System.out.println("====================deleteUser()=====================");
        students.remove(id);
        return "success";
    }

}

很好理解,就是在谁头上加上注解,谁就会在swagger面板中显示该注解的内容,其实就是注释

5、操作流程

restful 风格api restful风格接口案例_mvc_02

restful 风格api restful风格接口案例_restful_03


restful 风格api restful风格接口案例_java_04


restful 风格api restful风格接口案例_mvc_05


restful 风格api restful风格接口案例_mvc_06

6、自行测试

1.剩下的注解不再全部测试了;

@ApiModel(value = "学生实体")
public class Student{
    @ApiModelProperty(name = "studentName",notes = "学生名",dataType = "String",required = true)
    private String userName;

    @ApiModelProperty(name = "age",notes = "学生年龄",dataType = "int",required = true)
    private int age;