Spring Boot: REST API 快速入门

最后更新:2026-08-26

REST API 是微服务的通用语言——用 HTTP 动词操作资源,用 JSON 传递数据,简单、标准、高效。

1. 你将学到


2. 一个 API 开发者的真实故事

(1) 痛点:接口规范混乱

Alice 加入了一个老项目团队,发现 REST API 设计混乱:有的用 GET 修改数据,有的 URL 命名不一致(/getOrder/order/list/deleteOrderById),返回格式也不统一——有的返回 JSON,有的返回 XML,错误时返回纯文本。前端同事 Bob 每次对接新接口都要问后端的人,浪费了大量沟通时间。

(2) RESTful 风格的解法

REST 用统一的约定规范 API 设计:

JAVA
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    @GetMapping("/{id}")      // GET    /api/orders/123
    @PostMapping              // POST   /api/orders
    @PutMapping("/{id}")      // PUT    /api/orders/123
    @DeleteMapping("/{id}")   // DELETE /api/orders/123
}

(3) 收益

Alice 用 RESTful 风格重构 OrderFlow 的 API 后,URL 命名统一、HTTP 动词语义明确,前端同事 Bob 说"看 URL 就知道怎么用",接口对接时间从平均 2 小时缩短到 15 分钟。


3. REST 核心概念

(1) REST 六大约束

REST(Representational State Transfer)定义了架构风格的核心约束:

100%
graph TD
    A[REST Constraints] --> B[Client-Server<br/>Separation of Concerns]
    A --> C[Stateless<br/>Each Request Contains<br/>All Needed Info]
    A --> D[Cacheable<br/>Responses Define<br/>Cacheability]
    A --> E[Uniform Interface<br/>Consistent API Design]
    A --> F[Layered System<br/>Client Cannot Tell<br/>If Connected Directly]
    A --> G[Code on Demand<br/>Optional: Server Can<br/>Send Executable Code]

(2) HTTP 动词与 CRUD 映射

HTTP 动词 操作 幂等性 安全性 典型 URL
GET 查询 /api/orders
POST 创建 /api/orders
PUT 全量更新 /api/orders/123
PATCH 部分更新 /api/orders/123
DELETE 删除 /api/orders/123
📌 重点: 幂等性指多次执行结果相同。PUT 是幂等的(替换整个资源),POST 不是幂等的(每次可能创建新资源)。


4. @RestController 与请求映射

(1) @RestController 原理

@RestController = @Controller + @ResponseBody,表示该类所有方法返回值直接写入 HTTP 响应体(默认 JSON)。

▶ 示例: 基础 OrderController

JAVA
@RestController
@RequestMapping("/api/orders")
public class OrderController {

    private final List<Map<String, Object>> orders = new ArrayList<>();

    @GetMapping
    public List<Map<String, Object>> listOrders() {
        return orders;
    }

    @PostMapping
    public Map<String, Object> createOrder(
            @RequestBody Map<String, Object> order) {
        order.put("id", (long) (orders.size() + 1));
        order.put("status", "PENDING");
        orders.add(order);
        return order;
    }
}

输出:

TEXT 📖 仅展示
// 执行成功

(2) 请求参数绑定

▶ 示例: @PathVariable 和 @RequestParam

JAVA
@RestController
@RequestMapping("/api/orders")
public class OrderController {

    @GetMapping("/{id}")
    public Map<String, Object> getOrder(@PathVariable Long id) {
        return Map.of("id", id, "status", "PENDING");
    }

    @GetMapping
    public List<Map<String, Object>> searchOrders(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size,
            @RequestParam(required = false) String status) {
        return List.of(Map.of(
            "page", page, "size", size, "status", status
        ));
    }
}

输出:

TEXT 📖 仅展示
// 执行成功
注解 用途 示例 URL
@PathVariable 提取路径中的变量 @PathVariable Long id /api/orders/123
@RequestParam 提取查询参数 @RequestParam String status /api/orders?status=PENDING
@RequestBody 提取请求体 JSON @RequestBody OrderRequest req POST body
@RequestHeader 提取请求头 @RequestHeader String auth Header: Authorization

▶ 示例: 使用 DTO 接收请求体

JAVA
public record CreateOrderRequest(
    Long productId,
    Integer quantity,
    String shippingAddress
) {}

@RestController
@RequestMapping("/api/orders")
public class OrderController {

    @PostMapping
    public ResponseEntity<Map<String, Object>> createOrder(
            @RequestBody CreateOrderRequest request) {
        Map<String, Object> order = Map.of(
            "productId", request.productId(),
            "quantity", request.quantity(),
            "shippingAddress", request.shippingAddress(),
            "status", "PENDING"
        );
        return ResponseEntity
            .status(HttpStatus.CREATED)
            .body(order);
    }
}

输出:

TEXT 📖 仅展示
// 执行成功

5. ResponseEntity 与 HTTP 状态码

(1) ResponseEntity 的使用

ResponseEntity 允许你完全控制 HTTP 响应:状态码、响应头、响应体。

方式 适用场景 灵活度
直接返回对象 简单成功响应 低(固定 200)
ResponseEntity.ok(body) 需要指定 200
ResponseEntity.status(CREATED).body(body) 创建资源返回 201
ResponseEntity.notFound().build() 资源不存在返回 404

▶ 示例: 完整 CRUD 操作

JAVA
@RestController
@RequestMapping("/api/orders")
public class OrderController {

    private final Map<Long, Map<String, Object>> orderStore = new ConcurrentHashMap<>();
    private final AtomicLong idGenerator = new AtomicLong(1);

    @PostMapping
    public ResponseEntity<Map<String, Object>> create(
            @RequestBody CreateOrderRequest request) {
        Long id = idGenerator.getAndIncrement();
        Map<String, Object> order = new HashMap<>();
        order.put("id", id);
        order.put("productId", request.productId());
        order.put("quantity", request.quantity());
        order.put("status", "PENDING");
        orderStore.put(id, order);
        return ResponseEntity.status(HttpStatus.CREATED).body(order);
    }

    @GetMapping("/{id}")
    public ResponseEntity<Map<String, Object>> getOne(@PathVariable Long id) {
        Map<String, Object> order = orderStore.get(id);
        if (order == null) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(order);
    }

    @PutMapping("/{id}")
    public ResponseEntity<Map<String, Object>> update(
            @PathVariable Long id,
            @RequestBody CreateOrderRequest request) {
        if (!orderStore.containsKey(id)) {
            return ResponseEntity.notFound().build();
        }
        Map<String, Object> order = new HashMap<>();
        order.put("id", id);
        order.put("productId", request.productId());
        order.put("quantity", request.quantity());
        order.put("status", "CONFIRMED");
        orderStore.put(id, order);
        return ResponseEntity.ok(order);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        if (orderStore.remove(id) == null) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.noContent().build();
    }
}

输出:

TEXT 📖 仅展示
// 执行成功

6. API 测试方法

(1) curl 测试命令

▶ 示例: curl 测试 CRUD 接口

BASH
# Create order
curl -X POST http://localhost:8080/api/orders \
  -H "Content-Type: application/json" \
  -d '{"productId":1, "quantity":3, "shippingAddress":"123 Main St"}'

# Get order
curl http://localhost:8080/api/orders/1

# List orders with pagination
curl "http://localhost:8080/api/orders?page=0&size=10"

# Update order
curl -X PUT http://localhost:8080/api/orders/1 \
  -H "Content-Type: application/json" \
  -d '{"productId":2, "quantity":5, "shippingAddress":"456 Oak Ave"}'

# Delete order
curl -X DELETE http://localhost:8080/api/orders/1

输出:

TEXT 📖 仅展示
{"status":"ok","data":{}}
HTTP 状态码 含义 何时返回
200 OK 成功 GET / PUT 成功
201 Created 已创建 POST 创建资源成功
204 No Content 无内容 DELETE 成功
400 Bad Request 请求错误 参数校验失败
404 Not Found 未找到 资源不存在

7. RESTful API 设计规范

(1) URL 命名规范

规则 错误写法 正确写法
使用名词复数 /getOrder /api/orders
嵌套资源关系 /orderItems?orderId=1 /api/orders/1/items
用路径而非查询 /api/orders?id=1 /api/orders/1
版本控制 /api/v1/orders
过滤用查询参数 /api/pendingOrders /api/orders?status=PENDING

8. 综合示例:OrderFlow 订单 CRUD API

JAVA
package com.orderflow.controller;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

public record CreateOrderRequest(
    Long productId, Integer quantity, String shippingAddress
) {}

@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {

    private final Map<Long, Map<String, Object>> store = new ConcurrentHashMap<>();
    private final AtomicLong seq = new AtomicLong(1);

    @PostMapping
    public ResponseEntity<Map<String, Object>> create(
            @RequestBody CreateOrderRequest req) {
        Long id = seq.getAndIncrement();
        Map<String, Object> order = new LinkedHashMap<>();
        order.put("id", id);
        order.put("productId", req.productId());
        order.put("quantity", req.quantity());
        order.put("shippingAddress", req.shippingAddress());
        order.put("status", "PENDING");
        order.put("createdAt", java.time.Instant.now());
        store.put(id, order);
        return ResponseEntity.status(HttpStatus.CREATED).body(order);
    }

    @GetMapping
    public List<Map<String, Object>> list(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size) {
        return store.values().stream()
            .skip((long) page * size).limit(size).toList();
    }

    @GetMapping("/{id}")
    public ResponseEntity<Map<String, Object>> get(@PathVariable Long id) {
        Map<String, Object> order = store.get(id);
        return order != null ? ResponseEntity.ok(order)
                             : ResponseEntity.notFound().build();
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        return store.remove(id) != null
            ? ResponseEntity.noContent().build()
            : ResponseEntity.notFound().build();
    }
}

❓ 常见问题

Q @Controller 和 @RestController 有什么区别?
A @Controller 返回视图名(配合模板引擎),@RestController = @Controller + @ResponseBody,返回值直接序列化为 JSON。写 REST API 一律用 @RestController。
Q PUT 和 PATCH 有什么区别?
A PUT 是全量替换,必须传完整对象;PATCH 是部分更新,只传需要修改的字段。Spring Boot 对两者都支持,但 PATCH 需要自定义合并逻辑。
Q @PathVariable 和 @RequestParam 什么时候用?
A @PathVariable 用于标识资源的唯一标识(如订单 ID),@RequestParam 用于过滤和分页参数(如 status、page)。简单规则:路径用 PathVariable,查询用 RequestParam。
Q 返回 ResponseEntity 还是直接返回对象?
A 简单查询直接返回对象即可(自动 200)。需要自定义状态码(如 201、404)或响应头时使用 ResponseEntity。
Q 如何处理日期时间格式?
A 默认序列化为时间戳。可以在 application.yml 中配置 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss,或在字段上用 @JsonFormat(pattern = "yyyy-MM-dd")
Q REST API 需要版本控制吗?
A 推荐使用 URL 版本控制(/api/v1/orders),简单直观。也可以用 Header 版本控制(Accept: application/vnd.orderflow.v1+json),更 RESTful 但实现复杂。

📖 小节


📝 作业

  1. 基础题(难度⭐):为 OrderFlow 实现商品(Product)资源的 CRUD API,包括 GET /api/v1/productsPOST /api/v1/productsGET /api/v1/products/{id}DELETE /api/v1/products/{id}

  2. 进阶题(难度⭐⭐):在订单 API 中添加 PATCH /api/v1/orders/{id}/status 接口,只更新订单状态字段,思考 PATCH 与 PUT 的区别。

  3. 挑战题(难度⭐⭐⭐):实现 GET /api/v1/orders/{id}/items 嵌套资源接口,返回指定订单下的所有订单项,并支持分页参数。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏