springboot+ResFul 风格API接口+MockMvc测试+Swagger2
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 对比图
出了正常的不一样之外(比如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、操作流程
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;
2.还有一些问题没聊到,可以参考官网深入探究,比如Swagger认证
文章说明
参考博文:
https://blog.csdn.net/zyjtoto/article/details/96480749
https://www.jianshu.com/p/7a24d202b395
https://www.iteye.com/blog/jinnianshilongnian-2004660