第二章:RESTful API
学习内容
使用Spring MVC编写Restful API
使用Spring MVC处理其他web应用常见的需求和场景
如何处理静态资源和异常,如何使用Spring MVC的拦截器,文件的上传下载,如何进行请求的异步开发
RESTful API开发常用辅助框架
Swagger-生成服务文档,WireMock-伪造服务
RESTful API介绍
特点:
1.用URL描述资源
2.使用HTTP方法描述行为,使用HTTP状态码来表示不同结果
增删改查对应POST、DELETE、PUT、GET,使用状态码来表示结果而不是用报文里面的内容。
3.使用json交互数据
4.RESTful只是一种风格,并不是强制标准
编写第一个RESTful API
使用注解声明RESTful API
常用注解
@RestController 标明此Controller提供RESTful API
@RequestMapping及其变体 映射http请求url到java方法
@RequestParam 映射请求参数到java方法的参数
@PageableDefault 指定分页参数的默认值
在RESTful API中传递参数
@RequestParam
required,name,defaultValue,required表示参数是否必须,name表示HTTP请求参数名,defaultValue表示未传参数时使用的默认值。
@RequestMapping(value = "/user", method = RequestMethod.GET) public List<User> query(@RequestParam(required = true, name = "username", defaultValue = "Tom") String username) { System.out.println(username); List<User> users = new ArrayList<User>(); users.add(new User()); users.add(new User()); users.add(new User()); return users; }
如果传递参数太多,Spring MVC可以自动将参数组装到对象中,可以声明一个类UserQueryCondition有多个属性。
public class UserQueryCondition { private String username; private int age; private int ageTo; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getAgeTo() { return ageTo; } public void setAgeTo(int ageTo) { this.ageTo = ageTo; } }
@RequestMapping(value = "/user", method = RequestMethod.GET) public List<User> query(UserQueryCondition condition) { System.out.println(ReflectionToStringBuilder.toString(condition, ToStringStyle.MULTI_LINE_STYLE)); List<User> users = new ArrayList<User>(); users.add(new User()); users.add(new User()); users.add(new User()); return users; }
@Test public void whenQuerySuccess() throws Exception { mockMvc.perform(get("/user") .param("username", "jojo") .param("age", "10") .param("ageTo", "20") .contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(status().isOk()) .andExpect(jsonPath("$.length()").value(3)); }
@PageableDefault
page,size,sort,表示默认查第几页,一页数据条数,排序
@RequestMapping(value = "/user", method = RequestMethod.GET) public List<User> query(UserQueryCondition condition, @PageableDefault(page = 1, size = 10, sort = "username,asc") Pageable pageable) { System.out.println(ReflectionToStringBuilder.toString(condition, ToStringStyle.MULTI_LINE_STYLE)); System.out.println(ReflectionToStringBuilder.toString(pageable, ToStringStyle.MULTI_LINE_STYLE)); List<User> users = new ArrayList<User>(); users.add(new User(1)); users.add(new User(2)); users.add(new User(3));return users; }
@Test public void whenQuerySuccess() throws Exception { mockMvc.perform(get("/user") .param("username", "jojo") .param("age", "10") .param("ageTo", "20") .param("size", "5") .param("page", "2") .param("sort", "age,desc") .contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(status().isOk()); }
编写用户详情服务
@PathVariable 映射url片段到java方法的参数
name,required,name要和url片段相同,required表示是否必须
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET) public User getInfo(@PathVariable(name = "id") String userid) { User user = new User(); user.setId(userid); user.setUsername("Tom"); return user; }
@Test public void whenGetInfoSuccess() throws Exception { String result = mockMvc.perform(get("/user/1") .contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(status().isOk()) .andExpect(jsonPath("$.username").value("Tom")) .andReturn().getResponse().getContentAsString(); System.out.println(result); }
在url片段的声明中使用正则表达式
@RequestMapping(value = "/user/{id:\\d+}", method = RequestMethod.GET) public User getInfo(@PathVariable(name = "id") String userid) { User user = new User(); user.setId(userid); user.setUsername("Tom"); return user; }
@Test public void whenGetInfoFail() throws Exception { mockMvc.perform(get("/user/a") .contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(status().is4xxClientError()); }
@JsonView控制json输出内容
假设query不想返回用户密码,getInfo时返回给前台用户密码,即在返回相同对象时控制返回哪些字段。
使用步骤:
1.使用接口声明多个视图 2.在值对象的get方法上指定视图 3.在Controller方法上指定视图
public class User { public interface UserSimpleView {}; //视图1 public interface UserDetailView extends UserSimpleView {}; //视图2,因继承视图1,所以使用视图2也会显示视图1的属性 private String id; private String username; private int age; private String password; @JsonView(UserSimpleView.class) public String getId() { return id; } public void setId(String id) { this.id = id; } @JsonView(UserSimpleView.class) public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @JsonView(UserSimpleView.class) public int getAge() { return age; } public void setAge(int age) { this.age = age; } @JsonView(UserDetailView.class) public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
@RequestMapping(value = "/user", method = RequestMethod.GET) @JsonView(User.UserSimpleView.class) public List<User> query(UserQueryCondition condition, @PageableDefault(page = 1, size = 10, sort = "username,asc") Pageable pageable) { System.out.println(ReflectionToStringBuilder.toString(condition, ToStringStyle.MULTI_LINE_STYLE)); System.out.println(ReflectionToStringBuilder.toString(pageable, ToStringStyle.MULTI_LINE_STYLE)); List<User> users = new ArrayList<User>(); users.add(new User()); users.add(new User()); users.add(new User()); return users; } @RequestMapping(value = "/user/{id:\\d+}", method = RequestMethod.GET) @JsonView(User.UserDetailView.class) public User getInfo(@PathVariable(name = "id") String userid) { User user = new User(); user.setId(userid); user.setUsername("Tom"); return user; }
代码重构
1.将url相同部分提到类上声明
2.使用RequestMapping的变体GetMapping
@RestController @RequestMapping("/user") public class UserController { @GetMapping() @JsonView(User.UserSimpleView.class) public List<User> query(UserQueryCondition condition, @PageableDefault(page = 1, size = 10, sort = "username,asc") Pageable pageable) { System.out.println(ReflectionToStringBuilder.toString(condition, ToStringStyle.MULTI_LINE_STYLE)); System.out.println(ReflectionToStringBuilder.toString(pageable, ToStringStyle.MULTI_LINE_STYLE)); List<User> users = new ArrayList<User>(); users.add(new User()); users.add(new User()); users.add(new User()); return users; } @GetMapping("/{id:\\d+}") @JsonView(User.UserDetailView.class) public User getInfo(@PathVariable(name = "id") String userid) { User user = new User(); user.setId(userid); user.setUsername("Tom"); return user; } }