RESTful接口设计

一、概述

  1. 创建Springboot项目

  2. 创建实体类

  3. 设计接口实例

  4. RUSTful设计

    4.1 资源设计

    4.2 动作设计

    4.3 返回结果

二、Springboot开发RESTful服务

  1. 什么是REST

  2. REST和RESTful

  3. 为什么用RESTful接口

一、概述

1. 什么是REST

前提:理解REST首先要理解”资源“,所谓“资源”,就是网络上的一个实体,或者说是网络上的一个具体信息,可以是一段文本,一张图片,一首歌曲,一种服务,总之就是一个具体的实体,可以用一个URI(统一资源标识符)指向它,每种资源对应一个特定的URI,要获取这个资源,访问它的URI就可以了,因此URI就成了每个资源的地址或独一无二的标识符。

REST全称是Representational State Transfer(表述性状态转移),将其拆分出来进行理解:

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

简言之就是讲资源的状态以适合客户端或服务端的形式从服务器转移到客户端或从客户端转移到服务端

资源通过URL进行识别和定位,然后通过HTTTP方法来定义REST来完成所要实现的功能。

2. REST和RESTful

REST就是上面第一点所讲述的这些理论,而RESTful是一种设计风格,如果开发应用过程中是使用REST这些理论来实现的,则这些应用称为RESTful应用,就相当于beauty和beautiful。

3. 为什么用RESTful接口

在开发后台服务接口的时候,只需要专注于API接口的开发,数据怎么返回,目前基本使用json格式作为返回格式。而不用关心客户端请求的来源,请求可能来源于android,ios,pc,服务只认客户端请求的URI,然后经过路由找到对应的服务,返回请求资源。RESTful是一种设计风格,并非一个标准。因此,即使完全不用这种风格,功能上也能够满足需求。但是它结构清晰、符合标准、易于理解、扩展方便,越来越多的接口设计都采用这种风格。

4. RUSTful设计

4.1 资源设计

资源设计也就是路径设计,表示API的具体网址,在设计的时候有以下规范:

  • 网址中不能有动词,只能有名词
  • 所用的名词一般与数据库的表名对应
  • 由于数据库大多记录的都是同种记录的集合,网址中的名词应该使用复数

4.2 动作设计

数据的元操作,即CRUD操作,分别对应于HTTP方法:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源,这样就统一了数据操作的接口,仅通过HTTP方法,就可以完成对数据的所有增删查改工作。 即

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

还有两个不常用的:

  • HEAD:获得一个资源的元数据,比如一个资源的hash值或者最后修改的日期
  • OPTIONS:获得客户端针对一个资源能够实施的操作(获取该资源的api(能够对资源做什么操作的描述))

示例:

在Restful之前的操作:
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 删除用户信息

4.3 返回结果

1)返回值类型(作参考)

  • GET:返回资源对象的列表或单个资源对象
  • POST:返回生成的资源对象
  • PUT:返回完整的资源对象
  • PATCH:返回完整的资源对象
  • DELETE:返回一个空文档

2)常见状态码

RESTful框架服务器常见返回状态码和提示信息,常见的有以下(方括号中是该状态码对应的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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

3)响应类型(Content Type)

这里区分一下Accept和Content Type

  • Accept属于请求头,Content Type属于请求实体
    • HTTP报头:通用报头、请求报头、响应报头、实体报头
    • 请求方的http报头结构:通用报头|请求报头|实体报头
    • 响应方的http报头结构:通用报头|响A应报头|实体报头
  • Accept代表发送端(客户端)希望接受的数据类型
    • eg:Accept:application/json; :表示客户端希望接收的数据类型是json类型,后台需要返回json数据
  • Content Type代表发送端(客户端或服务器)发送的实体数据的数据类型
    • eg:Content Type:application/json; :表示发送端发送的数据格式是json,后台就需要以这种格式类接收前端发过来的数据
  • 在发送资源请求的时候,请求头Accept表示客户端希望接收什么样的数据类型,只能出现在Request请求里面,不能出现在Response响应里面,而Content Type请求和响应都能有

二、Springboot开发RESTful服务

在Java中常见的RESTful开发框架有:Jersey、play、SpringMVC,这里就以SpringMVC为例,使用Springboot开发RESTful服务,确定一个开发场景,就以员工薪资管理为例进行讲述,使用RESTful开发以下接口:

  1. 获取所有员工
  2. 获取某个员工信息
  3. 删除一个员工
  4. 获取某个员工某个月的薪资
  5. 给某个员工添加一条薪资记录

1. 创建Springboot项目

使用idea快速创建Springboot项目,这里不细讲,不明白的可以看我之前的博客:https://onestar.newstar.net.cn/blog/27

2. 创建实体类

这里创建两个实体类,一个员工实体类,一个薪资实体类,如下:

  • 员工实体类
@Data
public class Employee {
    private Long id;
    // 员工姓名
    private String name;
}
  • 薪资实体类
@Data
public class Salary {
    private Long id;
    // 员工ID
    private Long employeeId;
    // 薪资
    private BigDecimal money;
    // 所属时间
    private Date date;
}

3. 设计接口实例

设计RESTful接口步骤:

  1. 确定资源
  2. 确定请求方式
  3. 确定返回结果(类型、头信息、状态码)

代码如下:

package com.star.controller;

import com.star.entity.Employee;
import com.star.entity.Salary;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @ClassName: EmployeeController
 * @Description: 员工资源控制器
 * @Author ONESTAR
 * @Date: 2020/10/26 14:11
 * @QQ群:530311074
 * @URL:https://onestar.newstar.net.cn/
 * @Version 1.0
 */

@RestController
@RequestMapping("employees")
public class EmployeeController {

    /**
     * 获取所有员工
     * 1. 确定资源 : /employees
     * 2. 确定请求方式  :  GET
     * 3. 确定返回结果(类型、头信息、状态码) :  员工集合,content-type=application/json,200
     */
    @GetMapping
    public List<Employee> list(){
        ArrayList<Employee> list = new ArrayList<>();
        list.add(new Employee(1L,"onestar"));
        list.add(new Employee(2L,"twostar"));
        list.add(new Employee(3L,"threestar"));
        return list;
    }
    

    /**
     * 获取某个员工信息
     * 1. 确定资源 : /employees
     * 2. 确定请求方式  :  GET
     * 3. 确定返回结果(类型、头信息、状态码) :  员工对象,content-type=application/json,201
     */
    @GetMapping("{id}")
    public Employee getById(@PathVariable("id") Long id){
        return new Employee(id,"ONESTAR");
    }
    

    /**
     * 获取某个员工某个月的薪资记录
     * 1. 确定资源 : /employees/{employeeId}/salaries/{month}
     * 2. 确定请求方式  :  GET
     * 3. 确定返回结果(类型、头信息、状态码) :  薪资对象,content-type=application/json,200
     *
     * @DateTimeFormat:前台传递日期参数到后台接收时使用的注解
     * @JsonFormat:后台返回json数据给前台时使用的注解
     */
    @GetMapping("{employeeId}/salaries/{month}")
    public Salary getSalaryByEmployee(@PathVariable("employeeId") Long employeeId,
                                      @PathVariable("month") @DateTimeFormat(pattern = "yyyy-MM-dd") Date month){
        return new Salary(1L,employeeId, BigDecimal.TEN,month);
    }
    

    /**
     * 删除一个员工
     * 1. 确定资源 : /employees
     * 2. 确定请求方式  :  DELETE
     * 3. 确定返回结果(类型、头信息、状态码) :  空文档,204
     */
    @DeleteMapping ("{id}")
    public void deleteById(@PathVariable("id") Long id, HttpServletResponse response){
        System.out.println("删除ID为" + id + "的员工");
        // 设置状态码
        response.setStatus(HttpServletResponse.SC_NO_CONTENT);
    }
    

    /**
     * 给某个员工添加一条薪资记录
     * 1. 确定资源 : /employees/{employeeId}/salaries
     * 2. 确定请求方式  :  POST
     * 3. 确定返回结果(类型、头信息、状态码) :  薪资对象,content-type=application/json,201
     *
     * 路径占位符上的参数可以封装到自定义的对象中的同名属性上
     */
    @PostMapping("{employeeId}/salaries")
    public Salary save(Salary salary){
        return salary;
    }
}

以上五个功能都是使用RESTful风格编写的api,这里可以使用postman进行接口测试

end
  • 作者:ONESTAR(联系作者)
  • 更新时间:2020-10-26 20:10
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 转载声明:如果是转载栈主转载的文章,请附上原文链接
  • 公众号转载:请在文末添加作者公众号二维码(公众号二维码见右边,欢迎关注)
  • 评论