SpringBoot系列 - 实现RESTful接口
REST,即Representational State Transfer的缩写,对这个词组的翻译是表现层状态转化
。
RESTful是一种软件设计风格,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。
SpringMVC对RESTful风格的接口有着天然的支持,本篇将讲述如何在SpringBoot中怎样写。
几个注解
在讲述使用之前,想要理解SpringMVC的几个常用注解:
- @Controller:修饰class,用来创建处理http请求的对象
- @RestController:Spring4之后加入的注解,原来在@Controller中返回json需要@ResponseBody来配合,如果直接用@RestController替代@Controller就不需要再配置@ResponseBody,默认返回json格式。
- @RequestMapping:配置url映射
- @PostMapping: 这个是@RequestMapping+POST方法的简写
- @RequestHeader: 请求Header参数
- @PathVariable: URL路径参数,比如/user/{id}中的id参数
- @RequestParam: URL请求参数,比如/user?id=1中的id参数
- @RequestBody: 请求Body参数
下面我们尝试使用Spring MVC来实现一组对User对象操作的RESTful API,配合注释详细说明在Spring MVC中如何映射HTTP请求、如何传参、如何编写单元测试。
API设计
RESTful API具体设计如下:
请求类型 |
URL |
功能说明 |
GET |
/users |
查询用户列表 |
POST |
/users |
创建一个用户 |
GET |
/users/{id} |
根据id查询用户 |
PUT |
/users/{id} |
根据id更新用户 |
DELTE |
/users/{id} |
更加id删除用户 |
RESTful架构有一些典型的设计误区,就是URI包含动词。因为”资源”表示一种实体,所以应该是名词,URI不应该有动词,动词应该放在HTTP协议中。 上面设计的API的URI中都是名词。
实体定义
1 2 3 4 5 6
| public class User { private Long id; private String name; private Integer age; }
|
Controller实现
接下来就可以编写RestController了,这里为了演示,会将数据保存到内存Map中,实际使用肯定是保存到数据库中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
|
@RestController @RequestMapping(value = "/users") public class UserController {
private static final Logger _logger = LoggerFactory.getLogger(UserController.class);
private static Map<Long, User> users = Collections.synchronizedMap(new HashMap<Long, User>());
@RequestMapping(value = "/", method = RequestMethod.GET) public BaseResponse<List<User>> getUserList() { List<User> r = new ArrayList<>(users.values()); return new BaseResponse<>(true, "查询列表成功", r); }
@RequestMapping(value = "/", method = RequestMethod.POST) public BaseResponse<String> postUser(@ModelAttribute User user) { users.put(user.getId(), user); return new BaseResponse<>(true, "新增成功", ""); }
@RequestMapping(value = "/{id}", method = RequestMethod.GET) public BaseResponse<User> getUser(@PathVariable Long id) { return new BaseResponse<>(true, "查询成功", users.get(id)); }
@RequestMapping(value = "/{id}", method = RequestMethod.PUT) public BaseResponse<String> putUser(@PathVariable Long id, @ModelAttribute User user) { User u = users.get(id); u.setName(user.getName()); u.setAge(user.getAge()); users.put(id, u); return new BaseResponse<>(true, "更新成功", ""); }
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE) public BaseResponse<String> deleteUser(@PathVariable Long id) { users.remove(id); return new BaseResponse<>(true, "删除成功", ""); }
}
|
测试
接下来就是写一个测试来测一下,SpringMVC的内置测试支持非常方便。
先写一个json和对象之间相互转化的工具类,使用Jackson库:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class JacksonUtil { private static ObjectMapper mapper = new ObjectMapper();
public static String bean2Json(Object obj) { try { return mapper.writeValueAsString(obj); } catch (JsonProcessingException e) { e.printStackTrace(); return null; } }
public static <T> T json2Bean(String jsonStr, TypeReference<T> typeReference) { try { return mapper.readValue(jsonStr, typeReference); } catch (IOException e) { e.printStackTrace(); return null; } } }
|
然后再编写单元测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| @AutoConfigureMockMvc @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class ApplicationTests { @Autowired private MockMvc mvc;
@Test public void testUserController() throws Exception { RequestBuilder request;
request = get("/users/"); MvcResult result = mvc.perform(request) .andExpect(status().isOk()) .andReturn(); String content = result.getResponse().getContentAsString(); BaseResponse<List<User>> response = JacksonUtil.json2Bean(content, new TypeReference<BaseResponse<List<User>>>() {}); assertThat(response.isSuccess(), is(true)); assertThat(response.getMsg(), is("查询列表成功")); assertThat(((List) response.getData()).size(), is(0));
request = post("/users/") .param("id", "1") .param("name", "测试大师") .param("age", "20"); result = mvc.perform(request) .andExpect(status().isOk()) .andReturn(); content = result.getResponse().getContentAsString(); BaseResponse<String> response1 = JacksonUtil.json2Bean(content, new TypeReference<BaseResponse<String>>() {}); assertThat(response1.isSuccess(), is(true)); assertThat(response1.getMsg(), is("新增成功"));
request = get("/users/"); result = mvc.perform(request) .andExpect(status().isOk()) .andReturn(); content = result.getResponse().getContentAsString(); BaseResponse<List<User>> response2 = JacksonUtil.json2Bean(content, new TypeReference<BaseResponse<List<User>>>() {}); assertThat(response2.isSuccess(), is(true)); assertThat(response2.getMsg(), is("查询列表成功")); assertThat((response2.getData()).size(), is(1));
request = put("/users/1") .param("name", "测试终极大师") .param("age", "30"); result = mvc.perform(request) .andExpect(status().isOk()) .andReturn(); content = result.getResponse().getContentAsString(); BaseResponse<String> response3 = JacksonUtil.json2Bean(content, new TypeReference<BaseResponse<String>>() {}); assertThat(response3.isSuccess(), is(true)); assertThat(response3.getMsg(), is("更新成功"));
request = get("/users/1"); result = mvc.perform(request) .andExpect(status().isOk()) .andReturn(); content = result.getResponse().getContentAsString(); BaseResponse<User> response4 = JacksonUtil.json2Bean(content, new TypeReference<BaseResponse<User>>() {}); assertThat(response4.isSuccess(), is(true)); assertThat(response4.getMsg(), is("查询成功")); User user = response4.getData(); assertThat(user.getId(), is(1L)); assertThat(user.getName(), is("测试终极大师"));
request = delete("/users/1"); result = mvc.perform(request) .andExpect(status().isOk()) .andReturn(); content = result.getResponse().getContentAsString(); BaseResponse<String> response5 = JacksonUtil.json2Bean(content, new TypeReference<BaseResponse<String>>() {}); assertThat(response5.isSuccess(), is(true)); assertThat(response5.getMsg(), is("删除成功"));
request = get("/users/"); result = mvc.perform(request) .andExpect(status().isOk()) .andReturn(); content = result.getResponse().getContentAsString(); BaseResponse<List<User>> response6 = JacksonUtil.json2Bean(content, new TypeReference<BaseResponse<List<User>>>() {}); assertThat(response6.isSuccess(), is(true)); assertThat(response6.getMsg(), is("查询列表成功")); assertThat((response6.getData()).size(), is(0)); }
}
|
运行测试,结果为green bar,测试通过。
GitHub源码
springboot-restful —已测试
来源
https://www.xncoding.com/