非rest的url写法:
查询 GET /user/query?name=tom 详情 GET /user/getinfo?id=1 创建 POST /user/create?name=tom 修改 POST /user/update?id=1&name=tom 删除 GET /user/delete?id=1
rest风格的写法
查询 GET /user?name=tom 详情 GET /user/1 创建 POST /user 修改 PUT /user 删除 DELETE /user/1
rest接口设计参考:https://github.com/huobiapi/API_Docs/wiki/REST_api_reference
https://github.com/huobiapi/API_Docs
coinflext api: https://github.com/coinflex-exchange/API
1、Rest概念,来自百度百科
REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。
目前在三种主流的Web服务实现方案中,因为REST模式的Web服务与复杂的SOAP和XML-RPC对比来讲明显的更加简洁,越来越多的web服务开始采用REST风格设计和实现。
2、RESTful API的要求
1)用URL描述资源;
2)使用HTTP方法描述行为;使用HTTP状态码来表示不同的结果;
3)使用json来交互数据;
4)RESTful只是一种风格,并不是强制的标准。
总结:使用URL定位资源,使用HTTP方法操作资源。
GET 用来获取资源;
POST 用来新建资源(也可以用于更新资源);
PUT 用来更新资源;
DELETE 用来删除资源
3、Glory of REST
REST是一种软件接口设计的模型。REST 成熟度模型:(搜索关键字:steps toward the glory of REST)
level0: 使用http作为传输方式;
level1: 引入资源概念,每个资源都有对应的URL;
level2: 使用http方法进行不同的操作,使用http状态码来表示不同的结果。
level3: 使用超媒体,在资源的表达中包含了链接信息。
4、springmvc/springboot开发restful API
IndexController
@RestController public class IndexController { @Autowired private UserService userService; @GetMapping("/user/{id}") public Object getUserById(@PathVariable Integer id) { return userService.getUserById(id); } }
测试结果:
5、使用MockMVC 编写测试用例(单元测试)
参考:https://www.cnblogs.com/xy-ouyang/p/10681965.html
选中IndexController,右键/new/Junit Test Case
测试用例代码:
package com.imooc.controller; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; /** * @author oy * @date 2019年6月22日 下午11:14:56 * @version 1.0.0 */ @RunWith(SpringRunner.class) @SpringBootTest public class IndexControllerTest { @Autowired private WebApplicationContext wac; private MockMvc mocMvc; @Before public void setUp() throws Exception { mocMvc = MockMvcBuilders.webAppContextSetup(wac).build(); } @Test public void testGetUserById() throws Exception { String mvcResult = mocMvc.perform(MockMvcRequestBuilders.get("/user/1") .contentType(MediaType.APPLICATION_FORM_URLENCODED)) .andDo(MockMvcResultHandlers.print()) // 打印信息 .andExpect(MockMvcResultMatchers.status().isOk()) // 期望返回的状态码为200 .andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(2)) // 期望返回的json数据中有两个字段 .andExpect(MockMvcResultMatchers.jsonPath("$.username").value("张三")) // 期望返回的json数据中username字段的值为"张三" .andReturn().getResponse().getContentAsString(); System.out.println("mvcResult: " + mvcResult); } }
控制台打印结果:
MockHttpServletRequest: HTTP Method = GET Request URI = /user/1 Parameters = {} Headers = {Content-Type=[application/x-www-form-urlencoded]} Handler: Type = com.imooc.controller.IndexController Method = public java.lang.Object com.imooc.controller.IndexController.getUserById(java.lang.Integer) 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;charset=UTF-8]} Content type = application/json;charset=UTF-8 Body = {"username":"张三","password":"123"} Forwarded URL = null Redirected URL = null Cookies = [] mvcResult: {"username":"张三","password":"123"}
测试用例的代码简洁写法:使用静态导入
package com.imooc.controller; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; /** * @author oy * @date 2019年6月22日 下午11:14:56 * @version 1.0.0 */ @RunWith(SpringRunner.class) @SpringBootTest public class IndexControllerTest { @Autowired private WebApplicationContext wac; private MockMvc mocMvc; @Before public void setUp() throws Exception { mocMvc = MockMvcBuilders.webAppContextSetup(wac).build(); } @Test public void testGetUserById() throws Exception { String mvcResult = mocMvc.perform(get("/user/1") .contentType(MediaType.APPLICATION_FORM_URLENCODED)) .andDo(print()) // 打印信息 .andExpect(status().isOk()) // 期望返回的状态码为200 .andExpect(jsonPath("$.length()").value(2)) // 期望返回的json数据中有两个字段 .andExpect(jsonPath("$.username").value("张三")) .andReturn().getResponse().getContentAsString(); System.out.println("mvcResult: " + mvcResult); } }
6、@PageableDefault指定默认的分页参数
测试代码:
@RunWith(SpringRunner.class) @SpringBootTest public class IndexControllerTest { @Autowired private WebApplicationContext wac; private MockMvc mocMvc; @Before public void setUp() throws Exception { mocMvc = MockMvcBuilders.webAppContextSetup(wac).build(); } @Test public void testGetUserById() throws Exception { String mvcResult = mocMvc.perform(get("/user/1") // .param("page", "2") // .param("size", "10") // .param("sort", "id,desc") .contentType(MediaType.APPLICATION_FORM_URLENCODED)) .andDo(print()) // 打印信息 .andExpect(status().isOk()) // 期望返回的状态码为200 .andExpect(jsonPath("$.length()").value(2)) // 期望返回的json数据中有两个字段 .andExpect(jsonPath("$.username").value("张三")) .andReturn().getResponse().getContentAsString(); System.out.println("mvcResult: " + mvcResult); } }
IndexController
@RestController public class IndexController { @Autowired private UserService userService; @GetMapping("/user/{id}") public Object getUserById(@PathVariable Integer id, @PageableDefault(page = 1, size = 10, sort = "id,asc") Pageable pageable) { String str = ReflectionToStringBuilder.toString(pageable, ToStringStyle.MULTI_LINE_STYLE); System.out.println(str); return userService.getUserById(id); } }
控制台打印结果:
org.springframework.data.domain.PageRequest@7d522180[ sort=id,asc: ASC page=1 size=10 ]
7、测试用例中jsonpath的用法
github上面搜索jsonpath ==> https://github.com/json-path/JsonPath
8、@PathVariable 以及url映射中使用正则表达式
@GetMapping("/user/{id:\d+}") public Object getUserById(@PathVariable Integer id) { return userService.getUserById(id); }
此时,请求url为 /user/a 时,报404。
参考:
1)详解REST架构风格:http://www.uml.org.cn/zjjs/201805142.asp
2)REST,以及RESTful的讲解:https://blog.csdn.net/qq_21383435/article/details/80032375