源本科技 | 码上会

RESTful API

2026/03/31
24
0

什么是 REST API

核心定义

REST 全称为 Representational State Transfer表征状态转移),是 Roy Fielding 博士在 2000 年博士论文中提出的软件架构设计风格(不是协议、不是框架、不是库)。 遵循 REST 设计规范的 API 称为 RESTful API,是目前前后端分离、微服务架构中最主流的接口设计标准

简单理解:

  • 资源:互联网中一切实体都是资源(用户、订单、商品)

  • 表征:资源的表现形式(JSON/XML,主流用 JSON)

  • 状态转移:通过 HTTP 方法对资源进行增删改查,实现资源状态的变化

REST 核心约束

只有满足以下约束,才能称为标准 RESTful API:

  1. 客户端 - 服务器分离:前后端解耦,独立开发、独立部署、独立扩展

  2. 无状态(核心):服务器不存储客户端会话信息,每个请求必须携带完整参数

  3. 可缓存:响应支持 HTTP 缓存,提升性能

  4. 统一接口:使用标准 HTTP 方法操作资源,接口语义统一

  5. 分层系统:网关、负载均衡、服务器分层部署,提高扩展性

  6. 按需代码(可选):服务器可向客户端传输可执行代码(极少用)


为什么使用

相比传统接口(如 getUser.phpaddOrder),RESTful 具备绝对优势:

优势

说明

解耦性

前后端完全独立,前端专注展示,后端专注数据

可扩展性

无状态设计,支持水平扩容(加机器即可提升并发)

标准化

基于 HTTP 协议,通用、易懂、跨语言(Java/Python/JS/Go 通用)

可读性

见名知意,通过 URL + HTTP 方法即可知道接口功能

可缓存

天然支持浏览器 /CDN 缓存,降低服务器压力


核心规范

HTTP 方法 + CRUD + 幂等性 + 状态码

RESTful 核心:用标准 HTTP 方法操作资源,不使用动词,只用名词

HTTP 方法

对应 CRUD

核心用途

幂等性

标准响应码

GET

读取(Read)

查询单个 / 列表资源

✅ 幂等

200 OK

POST

创建(Create)

新增资源

❌ 非幂等

201 Created

PUT

更新(Update)

全量覆盖更新资源

✅ 幂等

200 OK

DELETE

删除(Delete)

删除资源

✅ 幂等

204 No Content

关键概念:幂等性

  • 幂等:同一请求执行 1 次和执行 N 次,结果完全一致(无脏数据)

  • 非幂等:重复执行会创建多条数据(POST 必须做防重复提交)


企业级分层架构

RESTful 项目必须采用分层架构,解耦代码、便于维护:

├── EmployeeApiApplication.java  # 启动类(项目入口)
├── controller       # 控制层:接收请求、参数校验、返回响应
├── service          # 业务层:核心业务逻辑、事务控制
├── mapper/dao       # 持久层:数据库交互(MyBatis/JPA)
├── entity/model     # 实体层:数据库映射对象
├── common           # 公共层:统一响应、全局异常、常量
└── vo               # 视图对象:前端返回的数据封装(可选)

每层职责

  1. Controller:只处理 HTTP 相关,不写业务逻辑

  2. Service:核心业务处理、事务、数据校验

  3. Mapper:只做数据库增删改查

  4. Entity:数据库表映射

  5. Common:全局工具、统一响应、异常处理


完整示例

环境依赖

<dependencies>
    <!-- Spring Web 核心依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Lombok 简化代码 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!-- 参数校验 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
</dependencies>

公共模块

统一响应结果(企业必备)

解决原生返回格式不统一问题,前端更容易处理:

package com.example.employeeapi.common;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 全局统一响应结果
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
    private int code;    // 响应码
    private String msg;  // 响应信息
    private T data;      // 响应数据

    // 成功响应
    public static <T> Result<T> success(T data) {
        return new Result<>(200, "操作成功", data);
    }
    // 成功响应(无数据)
    public static <T> Result<T> success() {
        return new Result<>(200, "操作成功", null);
    }
    // 失败响应
    public static <T> Result<T> fail(String msg) {
        return new Result<>(500, msg, null);
    }
}

实体层

package com.example.employeeapi.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
    private Integer id;

    @NotBlank(message = "名不能为空") // 参数校验
    private String firstName;

    @NotBlank(message = "姓不能为空")
    private String lastName;

    @Email(message = "邮箱格式不正确") // 邮箱校验
    private String email;
}

持久层

package com.example.employeeapi.mapper;

import com.example.employeeapi.entity.Employee;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Repository
public class EmployeeMapper {
    // 内存集合模拟数据库
    private final List<Employee> db = new ArrayList<>();

    // 初始化数据
    {
        db.add(new Employee(1, "Prem", "Tiwari", "prem@gmail.com"));
        db.add(new Employee(2, "Vikash", "Kumar", "vikash@gmail.com"));
    }

    // 查询所有
    public List<Employee> findAll() {
        return db;
    }

    // 根据ID查询
    public Optional<Employee> findById(Integer id) {
        return db.stream().filter(e -> e.getId().equals(id)).findFirst();
    }

    // 新增
    public Employee save(Employee employee) {
        employee.setId(db.size() + 1);
        db.add(employee);
        return employee;
    }

    // 全量更新
    public Employee update(Employee employee) {
        db.removeIf(e -> e.getId().equals(employee.getId()));
        db.add(employee);
        return employee;
    }

    // 删除
    public boolean deleteById(Integer id) {
        return db.removeIf(e -> e.getId().equals(id));
    }
}

业务层

package com.example.employeeapi.service;

import com.example.employeeapi.entity.Employee;
import com.example.employeeapi.mapper.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;

@Service
public class EmployeeService {
    @Autowired
    private EmployeeMapper employeeMapper;

    // 查询所有员工
    public List<Employee> getAll() {
        return employeeMapper.findAll();
    }

    // 根据ID查询
    public Optional<Employee> getById(Integer id) {
        return employeeMapper.findById(id);
    }

    // 新增员工
    public Employee create(Employee employee) {
        return employeeMapper.save(employee);
    }

    // 全量更新员工
    public Employee update(Employee employee) {
        return employeeMapper.update(employee);
    }

    // 删除员工
    public boolean delete(Integer id) {
        return employeeMapper.deleteById(id);
    }
}

控制层

package com.example.employeeapi.controller;

import com.example.employeeapi.common.Result;
import com.example.employeeapi.entity.Employee;
import com.example.employeeapi.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.net.URI;
import java.util.List;

@RestController
@RequestMapping("/api/v1/employees")
@Validated // 开启参数校验
public class EmployeeController {
    @Autowired
    private EmployeeService employeeService;

    // 1. 查询所有员工 GET /api/v1/employees
    @GetMapping
    public Result<List<Employee>> getAll() {
        return Result.success(employeeService.getAll());
    }

    // 2. 根据ID查询员工 GET /api/v1/employees/{id}
    @GetMapping("/{id}")
    public Result<Employee> getById(@PathVariable Integer id) {
        return employeeService.getById(id)
                .map(Result::success)
                .orElse(Result.fail("员工不存在"));
    }

    // 3. 新增员工 POST /api/v1/employees
    @PostMapping
    public ResponseEntity<Result<Employee>> create(@Valid @RequestBody Employee employee) {
        Employee emp = employeeService.create(employee);
        URI uri = URI.create("/api/v1/employees/" + emp.getId());
        // 返回201状态码(REST标准)
        return ResponseEntity.created(uri).body(Result.success(emp));
    }

    // 4. 全量更新员工 PUT /api/v1/employees/{id}
    @PutMapping("/{id}")
    public Result<Employee> update(
            @PathVariable Integer id,
            @Valid @RequestBody Employee employee
    ) {
        employee.setId(id);
        return Result.success(employeeService.update(employee));
    }

    // 5. 删除员工 DELETE /api/v1/employees/{id}
    @DeleteMapping("/{id}")
    public Result<Void> delete(@PathVariable Integer id) {
        if (employeeService.delete(id)) {
            return Result.success();
        }
        return Result.fail("删除失败,员工不存在");
    }
}

启动类

package com.example.employeeapi;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class EmployeeApiApplication {
    public static void main(String[] args) {
        SpringApplication.run(EmployeeApiApplication.class, args);
    }
}

接口测试

接口功能

请求方法

URL

请求体

预期状态码

查询所有

GET

http://localhost:8080/api/v1/employees

200

根据 ID 查询

GET

/employees/1

200

新增员工

POST

/employees

JSON

201

全量更新

PUT

/employees/1

JSON

200

删除员工

DELETE

/employees/1

200

新增 / 更新请求体示例

{
  "firstName": "Elon",
  "lastName": "Musk",
  "email": "elon@tesla.com"
}

最佳实践

  1. 接口必须版本化/api/v1/xxx,避免升级影响旧业务

  2. 统一响应格式:所有接口返回统一结构,前端无需适配多种格式

  3. 参数必校验:使用 @Valid 校验前端参数,防止非法数据

  4. 全局异常处理:统一捕获异常,避免前端收到杂乱错误

  5. 幂等性保障:POST 接口增加防重机制,PUT/DELETE 天然幂等

  6. 日志记录:接口请求、异常、业务操作全量日志

  7. 接口文档:集成 Swagger/Knife4j 自动生成接口文档


总结

  1. REST 本质:基于 HTTP 协议,用标准方法操作资源的架构风格

  2. 核心规范:GET 查、POST 增、PUT 改、DELETE 删,URL 用名词复数

  3. 分层架构:Controller→Service→Mapper,职责分离,代码易维护

  4. 企业标准:统一响应、参数校验、全局异常、版本控制是必备能力

  5. 核心优势:解耦、无状态、可扩展、跨平台,是微服务 / 前后端分离的首选