RESTful API,你真理解了吗?

  |   评论   |   浏览

file 如遇图片加载失败,可尝试使用手机流量访问

什么是RESTful API?

RESTful API并不是什么框架,他也并不是某段啥代码,他单纯的就是一种规范,一个标准。一旦涉及带规范、标准,就是一个很空泛概念,一开始很难理解真正的特点,然后就很难将其与传统的API区分开来;

RESTful API与传统API的区别

file 如遇图片加载失败,可尝试使用手机流量访问

  • 传统API的url代表的是一种行为;如上图的查询/user/query,通过url就可以知道当前的接口适用于查询操作的;
  • RESTful API的url表示的是资源;如上图的接口地址,多次出现/user/1;/user/1表示着用户ID为1的这个用户资源,1000个用户,就有1000个请求地址,也就对应着1000个资源;根据url地址,我们没有办法知道当前接口的操作是什么;具体接口的功能是通过这个接口的请求方式(method)来进行标识;如同为/user/1的资源:
    • Method是GET的时候,标识的就是查询id为1的用户;
    • Method是PUT的时候,就是修改;
    • Method是DELETE时就是删除这个资源了;

SpringBoot中用于定义RESTful API的常用注解

  • @RestController 标明对应的Controller用于提供RESTful API
  • @RequestMapping 用于映射http的url到对应的java方法上;其还有如下变种的方法
    • @GetMapping 等价于 @RequestMapping(method = RequestMethod.GET)
    • @PostMapping 等价于 @RequestMapping(method = RequestMethod.POST)
    • @PutMapping 等价于 @RequestMapping(method = RequestMethod.PUT)
    • @DeleteMapping 等价于 @RequestMapping(method = RequestMethod.DELETE)
  • @RequestParam 映射请求参数到java方法的参数
  • @PageableDefaule 指定分页参数默认值
  • @PathVariable 将path中的变量映射到java方法的参数;如GetMapping("/user/{id}"),当请求/user/1的时候id会映射为1,当请求/user/100时id会映射为100
  • @RequestBody 映射请求体到java方法的参数

示例

  • pom.xml

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
    • 以下是使用RESTful API 基于以上的示例及以上的注解,编写的测试代码
    // 用户对象 @Data @AllArgsConstructor public class User { private Integer id; private String username; private String nickName; private Integer age; private String password; }
    import com.fasterxml.jackson.annotation.JsonView; import com.lupf.springbootrestfulapi.dto.User; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; /** * @author lupf * @date 2020/7/13 9:16 * @desc */ @RestController @RequestMapping ("/user") @Slf4j public class UserController { /** * 等价于 @RequestMapping (value = "user", method = RequestMethod.GET) * * @param name * @param pageable * @return * @RequestParam 用于映射请求参数 * @PageableDefault 用于配置默认的分页数据 */ @GetMapping public List<User> getUserByName(@RequestParam ("username") String name, @PageableDefault (page = 1, size = 10, sort = {"age"}, direction = Sort.Direction.DESC) Pageable pageable) { log.info("username:{}", name); log.info("pageable.getPageSize():{}", pageable.getPageSize()); log.info("pageable.getPageNumber():{}", pageable.getPageNumber()); log.info("pageable.getSort():{}", pageable.getSort()); User user = new User(1, name, "xiaoxx", 10, "123456"); List<User> users = new ArrayList<>(); users.add(user); return users; } /** * 根据ID获取用户的详细信息 * * @param id * @return */ @GetMapping ("/{id:\\d+}") public User getUserInfoById(@PathVariable Integer id) { log.info("username:{}", id); User user = new User(1, "zhangsan", "xiaoxx", 10, "123456"); return user; } /** * 添加用户信息 * Spring会将请求中content中的json对象转换为一个User对象 * * @param user */ @PostMapping public void addUser(@RequestBody User user) { log.info("user:{}", user); } /** * 根据用户ID修改用户数据 * * @param id 修改的用户对应的ID * @param user 待修改的用户信息 */ @PutMapping ("/{id:\\d+}") public void updaste(@PathVariable Integer id, @RequestBody User user) { log.info("update user id:{}", user.getId()); log.info("update user:{}", user); } /** * 根据用户id删除 * * @param id */ @DeleteMapping ("/{id}") public void delete(@PathVariable Integer id) { log.info("delete user id:{}", id); } }

接口测试用例

import lombok.extern.slf4j.Slf4j; 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.MockMvcResultMatchers; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; @RunWith (SpringRunner.class) @SpringBootTest @Slf4j public class SpringbootRestfulApiApplicationTests { @Autowired WebApplicationContext wac; MockMvc mockMvc; /** * 每个测试用例执行之前都会执行这一段方法 */ @Before public void setup() { mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); } @Test public void queryUserSuccessByUserName() throws Exception { String responseStr = mockMvc.perform( // 请求构建对象 MockMvcRequestBuilders // 指定请求的restful api的地址 // .get 就是表示发送get方法 .get("/user") // 指定请求内容的格式 .contentType(MediaType.APPLICATION_JSON_UTF8) // 参数 .param("username", "zhangsan") // 页面 .param("page", "1") // 分页的大小 .param("size", "10") // 排序 .param("sort", "age,desc")) // 指定响应的预期状态码 .andExpect(MockMvcResultMatchers.status().isOk()) // 指定响应预期的内容 .andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(1)) // 获取到响应数据 .andReturn().getResponse().getContentAsString(); log.info("return string:{}", responseStr); // jsonPath : https://github.com/json-path/JsonPath } @Test public void queryUserSuccessByIdSuccess() throws Exception { String responseStr = mockMvc.perform( // 请求构建对象 MockMvcRequestBuilders // 指定请求的restful api的地址 // .get 就是表示发送get方法 .get("/user/1") // 指定请求内容的格式 .contentType(MediaType.APPLICATION_JSON_UTF8)) // 指定响应的预期状态码 .andExpect(MockMvcResultMatchers.status().isOk()) // 指定响应预期的内容 // 要求返回的对象的用户名为:zhangsan .andExpect(MockMvcResultMatchers.jsonPath("$.username").value("zhangsan")) // 获取到响应数据 .andReturn().getResponse().getContentAsString(); log.info("return string:{}", responseStr); } @Test public void queryUserSuccessByIdFail() throws Exception { String responseStr = mockMvc.perform( // 请求构建对象 MockMvcRequestBuilders // 指定请求的restful api的地址 // .get 就是表示发送get方法 .get("/user/mm") // 指定请求内容的格式 .contentType(MediaType.APPLICATION_JSON_UTF8)) // 指定响应的预期状态码为4xx .andExpect(MockMvcResultMatchers.status().is4xxClientError()) // 获取到响应数据 .andReturn().getResponse().getContentAsString(); log.info("return string:{}", responseStr); } @Test public void addUserSuccess() throws Exception { String content = "{\"username\":\"wangwu\",\"age\":25,\"nickName\":\"wuwu\",\"password\":\"123321\"}"; mockMvc .perform(MockMvcRequestBuilders.post("/user").contentType(MediaType.APPLICATION_JSON_UTF8).content(content)) .andExpect(MockMvcResultMatchers.status().isOk()); } @Test public void updateUserSuccess() throws Exception { String content = "{\"username\":\"wangwu\",\"age\":30,\"nickName\":\"wwuwu\",\"password\":\"789456\"}"; mockMvc.perform( // MockMvcRequestBuilders // 请求对象 .put("/user/1") // 请求内容的个数 .contentType(MediaType.APPLICATION_JSON_UTF8) // 请求数据 .content(content)) // 响应状态要求 .andExpect(MockMvcResultMatchers.status().isOk()); } @Test public void deleteUserSuccess() throws Exception { mockMvc.perform(MockMvcRequestBuilders.delete("/user/1").contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(MockMvcResultMatchers.status().isOk()); } }

file 如遇图片加载失败,可尝试使用手机流量访问

只要理解了对数据的操作转换为资源的操作,RESTful API就比较好理解了。



标题:RESTful API,你真理解了吗?
作者:码霸霸
地址:https://blog.lupf.cn/articles/2020/07/13/1594622668926.html