RESTful介绍和使用

RESTful发展背景及简介

网络应用程序,分为前端和后端两个部分。当前的发展趋势,就是前端设备层出不穷(手机、平板、桌面电脑、其他专用设备…)。因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信。这导致API构架的流行,甚至出现"APIFirst"的设计思想。RESTful API是目前比较成熟的一套互联网应用程序的API设计理论。

REST(Representational State Transfer)表述性状态转换,REST指的是一组架构约束条件和原则。 如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。REST本身并没有创造新的技术、组件或服务,而隐藏在RESTful背后的理念就是使用Web的现有特征和能力, 更好地使用现有Web标准中的一些准则和约束。虽然REST本身受Web技术的影响很深, 但是理论上REST架构风格并不是绑定在HTTP上,只不过目前HTTP是唯一与REST相关的实例。

RESTful架构

(1)每一个URI代表一种资源;

(2)客户端和服务器之间,传递这种资源的某种表现层;

(3)客户端通过HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。

符合上述REST原则的架构方式称为RESTful

URI命名规则

1. 全部小写,用中划线连接

命名规则永远是一个具有争议的话题,在RESTful接口中,对于URI一个普遍接受的规则是:全部小写,用中划线连接。

之所以不混用大小写字母,是因为早期的URI一般都是表示服务器上的文件路径,而不同服务器对大小写的敏感性是不同的,为了兼容不同服务器所以才规定不能混用大小写字母,然后用中划线或下划线连接多个单词。

我们如今开发的api,URI一般不再表示服务器上的文件了,而是一种程序使用的路由,而各类开发程序对字符串的判断也都是区分大小写的,所以就算URI混用了大小写一般也不会有问题。但是为了与那些仍旧表示服务器文件路径的URI保持一致,更多的人仍旧愿意沿用全部使用小写字母的规则。至于连接多个单词使用中划线还是下划线,我只能说,中划线使用得更加广泛。

2.单数复数

英文中,名词是有单复数之分的,所以在对资源命名时究竟是用单数还是复数,这也成为了一个有争议的问题。

我的使用习惯是,用单数来表示单个资源,用复数来表示集合资源。比如分页查询时用的是复数,单条数据查询时用的时单数

参考:https://blog.csdn.net/haochen_net/article/details/78159949

URL和URI的区别

URI包括URL和URN两个类别,URL是URI的子集,所以URL一定是URI,而URI不一定是URL

URI = Universal Resource Identifier 统一资源标志符,用来标识抽象或物理资源的一个紧凑字符串。
URL = Universal Resource Locator 统一资源定位符,一种定位资源的主要访问机制的字符串,一个标准的URL必须包括:protocol、host、port、path、parameter、anchor。
URN = Universal Resource Name 统一资源名称,通过特定命名空间中的唯一名称或ID来标识资源。

在这里插入图片描述

参考:https://blog.csdn.net/koflance/article/details/79635240

接口示例

传统URL请求格式:

http://127.0.0.1/user/query/1 GET 根据用户id查询用户数据

http://127.0.0.1/user/save POST 新增用户

http://127.0.0.1/user/update POST 修改用户信息

http://127.0.0.1/user/delete GET/POST 删除用户信息

RESTful请求格式:

http://127.0.0.1/user/1 GET 根据用户id查询用户数据

http://127.0.0.1/user POST 新增用户

http://127.0.0.1/user PUT 修改用户信息

http://127.0.0.1/user DELETE 删除用户信息

原文链接:https://blog.csdn.net/x541211190/article/details/81141459

HTTP动词

常用的方法有GET, POST, PUT, DELETE,此外有时还会用到HEAD、PATCH和OPTION。前面说我们系统的设计是以资源为核心的,URI的命名要使用名词,那我们要如何来表示一个操作动作呢?就是依靠HTTP方法,也叫HTTP动词。也就是通过这种动名词的组合来表示一次操作,在REST中会被叫做状态转移。

简单的讲GET是查询操作,不会产生状态转移, PUT和PATCH是更新操作,会将资源状态转移为客户端期望的新的状态,也有可能是一个从无到有的转移, DELETE表示删除,将资源状态转移为删除,POST表示添加一个资源,即资源状态从无转移到有,另外POST也被认为是一个万能的方法,可以用于所有的操作,包括写操作和复杂的查询操作。

GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。 DELETE(DELETE):从服务器删除资源。

还有两个不常用的HTTP动词。

HEAD:获取资源的元数据。
OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

HTTP动词 安全性 幂等性 功能性
GET 查询
POST 创建
PUT 更新
DELETE 删除
HEAD 查询
PATCH 更新

幂等性:对同一REST接口的多次访问,得到的资源状态是相同的。

安全性:对该REST接口访问,不会使服务器端资源的状态发生改变。

PATCH方法是新引入的,是对PUT方法的补充,用来对已知资源进行局部更新

patch只传部分字段到指定资源去,表示该请求是一个局部更新,后端仅更新接收到的字段。

put虽然也是更新资源,但要求前端提供的一定是一个完整的资源对象,理论上说,如果你用了put,但却没有提供完整的对象,那么缺了的那些字段应该被清空

http响应状态码

200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [* ]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized - [* ]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [* ] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND -[* ]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable -[GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone-[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [* ]:服务器发生错误,用户将无法判断发出的请求是否成功。

状态码的完全列表参见这里

RESTful的使用

在这里插入图片描述

UserEntity

package com.honger1234.restfulapi.entity;

import lombok.Data;

/**
 * @Description: 用户实体类
 * @author: zt
 * @date: 2020年3月26日
 */
@Data
public class UserEntity {
    private Long id;
    private String name;
    private Integer age;
}

UserController

package com.honger1234.restfulapi.controller;

import com.honger1234.restfulapi.entity.UserEntity;
import org.springframework.web.bind.annotation.*;

import java.util.*;

/**
 * @Description: RESTful API
 * @author: zt
 * @date: 2020年3月26日
 */

@RestController
@RequestMapping(value="/users")
public class UserController {

    static Map<Long, UserEntity> users = Collections.synchronizedMap(new HashMap<Long, UserEntity>());
    static {
        //初始化map模拟数据库存储数据
        UserEntity user = new UserEntity();
        user.setId(1L);
        user.setName("RESTful API测试");
        user.setAge(20);
        users.put(user.getId(),user);
    }

    @RequestMapping(value="/", method= RequestMethod.GET)
    public List<UserEntity> getUserList() {
        // 处理"/users/"的GET请求,用来获取用户列表
        // 还可以通过@RequestParam从页面中传递参数来进行查询条件或者翻页信息的传递
        List<UserEntity> r = new ArrayList<UserEntity>(users.values());
        return r;
    }

    @RequestMapping(value="/", method=RequestMethod.POST)
    public UserEntity postUser(@ModelAttribute UserEntity user) {
        // 处理"/users/"的POST请求,用来创建User
        // 除了@ModelAttribute绑定参数之外,还可以通过@RequestParam从页面中传递参数
        users.put(user.getId(), user);
        return user;
    }

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

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

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

REST接口测试类 UserControllerTest

package com.honger1234.restfulapi.controller;

import com.honger1234.restfulapi.entity.UserEntity;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

class UserControllerTest {

    private MockMvc mvc;

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

    @Test
    void getUserList() throws Exception {
        // 1、get查一下user列表,应该有一条数据
        RequestBuilder request = get("/users/");
        ResultActions resultActions = mvc.perform(request)
                .andExpect(status().isOk());
//                .andExpect(content().string(equalTo("[]")));
        //将返回的信息做utf-8处理防止中文乱码
        resultActions.andReturn().getResponse().setCharacterEncoding("UTF-8");
        //添加输出
        resultActions.andDo(print()).andExpect(status().isOk());
    }

    @Test
    void postUser() throws Exception {
        // 2、post提交一个user
        RequestBuilder request = post("/users")
                .param("id", "2")
                .param("name", "测试大师")
                .param("age", "20");
        ResultActions resultActions = mvc.perform(request)
                //.andDo(print()) 没做utf-8处理在这里打印可能出现中文乱码
//                .andDo(print())
//                .andExpect(content().string(equalTo("success")))
               ;
        //将返回的信息做utf-8处理防止中文乱码
        resultActions.andReturn().getResponse().setCharacterEncoding("UTF-8");
        //添加输出
        resultActions.andDo(print()).andExpect(status().isOk());
    }

    @Test
    void getUser() throws Exception {

        // 3、获取一个id为1的user
        RequestBuilder request = get("/users/1");
        ResultActions resultActions = mvc.perform(request)
                .andExpect(status().isOk());
//                .andExpect(content().string(equalTo("[{\"id\":1,\"name\":\"测试大师\",\"age\":20}]")));

        //将返回的信息做utf-8处理防止中文乱码
        resultActions.andReturn().getResponse().setCharacterEncoding("UTF-8");
        //添加输出
        resultActions.andDo(print()).andExpect(status().isOk());
    }

    @Test
    void putUser() throws Exception {
        // 4、put修改id为1的user
        UserEntity user = new UserEntity();
        user.setId(3L);
        user.setName("更新API");
        user.setAge(30);
        RequestBuilder request = put("/users/1")
                .param("user", user.toString());
        ResultActions resultActions = mvc.perform(request)
                .andExpect(status().isOk());
        //将返回的信息做utf-8处理防止中文乱码
        resultActions.andReturn().getResponse().setCharacterEncoding("UTF-8");
        //添加输出
        resultActions.andDo(print()).andExpect(status().isOk());

    }

    @Test
    void deleteUser() throws Exception {
        // 5、del删除id为1的user
        RequestBuilder request = delete("/users/1");
        ResultActions resultActions = mvc.perform(request)
                .andExpect(content().string(equalTo("success")));
        //将返回的信息做utf-8处理防止中文乱码
        resultActions.andReturn().getResponse().setCharacterEncoding("UTF-8");
        //添加输出
        resultActions.andDo(print()).andExpect(status().isOk());
    }
}

结语

至此,RESTful的介绍和使用已经完成,RESTful已在各互联网公司接口定义中,成为主流的范式,RESTful减少了传统请求的拆装箱操作,结构清晰,在接口定义中受到前后端开发者的青睐。

猜你喜欢

转载自blog.csdn.net/qq_38738510/article/details/105192802