RESTful风格介绍及应用

本文深入解析RESTful API的设计理念,通过实例演示如何在SpringBoot中实现RESTful风格的API,涵盖GET、POST、PUT、DELETE等HTTP方法的应用,并提供详细的测试案例。

一、概念

RESTful风格:REST是REpresentational State Transfer的缩写(表述性状态转移),REST是一种体系结构,而HTTP是一种包含了REST架构属性的协议,为了便于理解,我们把它的首字母拆分成不同的几个部分:

  • 表述性(REpresentational):REST资源实际上可以用各种形式来进行表述,包括XML、JSON甚至HTML——最适合资源使用者的任何形式;
  • 状态(State):当使用REST的时候,我们更关注资源的状态而不是资源采取的行为。
  • 转义(Transfer):REST涉及到转移资源数据,它以某种表述性形式从一个应用转移到另一个应用。

简单地说,REST就是将资源的状态以适合客户端或服务端的形式从服务端转移到客户端(或者从客户端转移到服务端)。在REST中,资源通过URL进行识别和定位,然后通过行为(即HTTP方法)来定义REST来完成怎样的功能。

二、实例说明

       在平时的WEB开发中,method常用的值是GET和POST,但实际上,HTTP方法还有PATCH、DELETE、PUT等其他值,这些方法又通常会匹配为如下的CRUD动作:

CRUD 动作HTTP 方法
CreatePOST
ReadGET
UpdatePUT 或 PATCH
DeleteDELETE

尽管通常来讲,HTTP方法会映射为CRUD动作,但这并不是严格的限制,有时候PUT也可以用来创建新的资源,POST也可以用来更新资源。实际上,POST请求非幂等的特性(即同一个URL可以得到不同的结果)使其成为一个非常灵活的方法,对于无法适应其他HTTP方法语义的操作,它都能够胜任。

在使用RESTful风格之前,我们如果想要增加一条用户信息,通常是这样的:

/addUser?name=xxx

 但是,使用了RESTful风格之后:

/user

      RESTful就是使用同一个URL,通过约定不同的HTTP方法来实施不同的业务,传统风格与RESTful风格来进行对比:、

 

三、在Spring Boot中应用

RESTful API具体涉及如下:

请求类型URL功能说明
GET/user查询用户列表
POST/user创建一个用户
GET/user/id根据id查询一个用户
PUT/user/id根据id更新一个用户
DELETE/user/id根据id删除一个用户
  • UserBean实体类:
package com.test.bean;

public class UserBean {
    private Long id;
    private String name;
    private String age;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

}
  • 操作接口:UserController
package com.test.controller;

import com.test.bean.UserBean;
import org.springframework.web.bind.annotation.*;

import java.util.*;

@RestController
@RequestMapping("/user")
public class UserController {
    static Map<Long, UserBean> users = Collections.synchronizedMap(new HashMap<Long, UserBean>());

    //处理"/user/"的GET请求,用来获取用户列表
    @RequestMapping(value="/",method = RequestMethod.GET)
    public List<UserBean> getUserList() {
        ArrayList<UserBean> list = new ArrayList<UserBean>(users.values());

        return list;
    }

    //处理"/user/"的POST请求,用来创建UserBean
    @RequestMapping(value="/",method = RequestMethod.POST)
    public String postUser(@ModelAttribute UserBean user){
        users.put(user.getId(),user);
        return "success";
    }

    // 处理"/users/{id}"的GET请求,用来获取url中id值的User信息
    @RequestMapping(value="/{id}", method=RequestMethod.GET)
    public UserBean getUser(@PathVariable Long id) {
        // url中的id可通过@PathVariable绑定到函数的参数中
        return users.get(id);
    }


    //处理"/user/{id}"的PUT请求,用来更新UserBean的信息
    @RequestMapping(value="/{id}", method=RequestMethod.PUT)
    public String putUser(@PathVariable Long id,@ModelAttribute UserBean user){
        UserBean u = users.get(id);
        u.setName(user.getName());
        u.setAge(user.getAge());
        users.put(id,u);
        return "success";
    }

    //处理"/user/{id}"的DELETE请求,用来删除UserBean
    @RequestMapping(value="/{id}", method=RequestMethod.DELETE)
    public String deleteUser(@PathVariable Long id){
        users.remove(id);
        return "success";
    }
}
  • 测试用例

       使用MockMvc实现了对HTTP请求的模拟,它能够直接使用玩咯的形式,转换到Controller进行调用,这样使得测试速度更快、不依赖网络环境,还提供了一套验证工具,这样可以使得请求的验证统一而且很方便。(在MockMvc使用之前需要先用MockMvcBuilder构建MockMvc对象)。

package com.example.demo;

import com.test.controller.UserController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.hamcrest.core.IsEqual.equalTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = MockServletContext.class)
@WebAppConfiguration
public class ApplicaionTests {
    private MockMvc mvc;

    @Before
    public void setUp(){
        mvc = MockMvcBuilders.standaloneSetup(new UserController()).build();
    }

    RequestBuilder requestBuilder = null;

    //测试GET:查看user列表
    @Test
    public void testUserController_GET(){
        RequestBuilder requestBuilder = null;

        //1、get查看user列表,应该为空
        requestBuilder = get("/user/");
        try {
            mvc.perform(requestBuilder).andExpect(status().isOk()).andExpect(content().string(equalTo("[]")));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //测试POST:提交一个UserBean
    @Test
    public void testUserController_POST(){
        //1、post提交一个UserBean
        requestBuilder = post("/user/")
                .param("id", "1")
                .param("name", "hdn")
                .param("age", "20");
        try {
            mvc.perform(requestBuilder).andExpect(content().string(equalTo("success")));
        } catch (Exception e) {
            e.printStackTrace();
        }

        //2、get查看user列表,此时应该有刚刚插入的一条数据
        requestBuilder = get("/user/");
        try {
            mvc.perform(requestBuilder).andExpect(status().isOk()).andExpect(content().string(equalTo("[{\"id\":1,\"name\":\"hdn\",\"age\":\"20\"}]")));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //测试PUT:根据id来修改一条记录
    @Test
    public void testUserController_PUT(){
        //1、post提交一个UserBean
        requestBuilder = post("/user/")
                .param("id", "1")
                .param("name", "hdn")
                .param("age", "20");
        try {
            mvc.perform(requestBuilder).andExpect(content().string(equalTo("success")));
        } catch (Exception e) {
            e.printStackTrace();
        }

        //2、将id为1的userBean的name修改为何大宁
        requestBuilder = put("/user/1")
                .param("id", "1")
                .param("name", "何大宁")
                .param("age", "20");
        try {
            mvc.perform(requestBuilder).andExpect(content().string(equalTo("success")));
        } catch (Exception e) {
            e.printStackTrace();
        }

        //3、get查看user列表,此时一条数据的name应该是何大宁
        requestBuilder = get("/user/");
        try {
            mvc.perform(requestBuilder).andExpect(status().isOk()).andExpect(content().string(equalTo("[{\"id\":1,\"name\":\"何大宁\",\"age\":\"20\"}]")));
        } catch (Exception e) {
            e.printStackTrace();
        }

        //4、根据id来查询一个UserBean
        requestBuilder = get("/user/1");
        try {
            mvc.perform(requestBuilder).andExpect(status().isOk()).andExpect(content().string(equalTo("{\"id\":1,\"name\":\"何大宁\",\"age\":\"20\"}")));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //测试DELETE:提交一个UserBean
    @Test
    public void testUserController_DELETE(){
        //1、post提交一个UserBean
        requestBuilder = post("/user/")
                .param("id", "1")
                .param("name", "hdn")
                .param("age", "20");
        try {
            mvc.perform(requestBuilder).andExpect(content().string(equalTo("success")));
        } catch (Exception e) {
            e.printStackTrace();
        }

        //2、get查看user列表,此时应该有刚刚插入的一条数据
        requestBuilder = get("/user/");
        try {
            mvc.perform(requestBuilder).andExpect(status().isOk()).andExpect(content().string(equalTo("[{\"id\":1,\"name\":\"hdn\",\"age\":\"20\"}]")));
        } catch (Exception e) {
            e.printStackTrace();
        }

        //3、delete删除id为1的记录
        requestBuilder = delete("/user/1");
        try {
            mvc.perform(requestBuilder).andExpect(status().isOk()).andExpect(content().string(equalTo("success")));
        } catch (Exception e) {
            e.printStackTrace();
        }

        //4、get再查看user列表,此时应该为空
        requestBuilder = get("/user/");
        try {
            mvc.perform(requestBuilder).andExpect(status().isOk()).andExpect(content().string(equalTo("[]")));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

       如果你想要看到更多的细节信息,可以在每次调用 perform() 方法后再跟上一句 .andDo(MockMvcResultHandlers.print()) ,例如:

         //1、get查看user列表,应该为空
        requestBuilder = get("/user/");
        try {
            mvc.perform(requestBuilder).andExpect(status().isOk()).andExpect(content().string(equalTo("[]"))).andDo(MockMvcResultHandlers.print());
        } catch (Exception e) {
            e.printStackTrace();
        }

详细信息显示如下:

四、小结

        在RESTful中,我们仍然使用@RequestMapping注解,但不同的是,我们指定method属性来处理不同的HTTP方法,并且通过@PathVariable注解来将HTTP请求中的属性绑定到我们指定的形参上。@ModelAttribute注解来将parm参数封装成javaBean对象。

        实际上,在Spring4.3之后,为了更好的支持RESTful风格,增加了几个注解:@PutMapping、@GetMapping、@DeleteMapping、@PostMapping,从名字也能大概的看出,其实就是将method属性的值与@RequestMapping进行了绑定。

    ----------------改造前------------------
    //处理"/user/"的GET请求,用来获取用户列表
    @RequestMapping(value="/",method = RequestMethod.GET)
    public List<UserBean> getUserList() {
        ArrayList<UserBean> list = new ArrayList<UserBean>(users.values());

        return list;
    }

    ----------------改造后------------------
    @GetMapping(value="/")
    public List<UserBean> getUserList() {
        ArrayList<UserBean> list = new ArrayList<UserBean>(users.values());

        return list;
    }

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值