CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
API 设计实战入门/课时/HTTP 协议层
RawBlame
课时目录12 节

API 设计实战入门

  1. 01API 基础
  2. 02HTTP 协议层
  3. 03REST 资源设计
  4. 04错误处理
  5. 05认证
  6. 06授权模型
  7. 07API 安全
  8. 08API 风格
  9. 09架构与集成
  10. 10测试
  11. 11可观测与合规
  12. 12文档与工具

HTTP 协议层

方法、状态码、头与 CORS。

30 分钟

HTTP 协议层

这一课你将能做到

  1. 熟练运用 GET/POST/PUT/DELETE/PATCH,并说清楚各自的语义和幂等性
  2. 根据业务场景选择正确的 HTTP 状态码,不再只会 200/500
  3. 配置 CORS 解决跨域问题,理解预检请求(Preflight)机制

为什么要学

HTTP 是 API 的载体。你设计的 URI 再漂亮,如果 HTTP 方法用错、状态码乱返、Header 不会配,调用方照样崩溃。面试必考:POST 和 PUT 区别?301 和 302 区别?CORS 怎么配?

先建立图像

HTTP 请求报文:
┌─────────────────────────────────────────┐
│ POST /api/lessons HTTP/1.1              │  ← 请求行:方法 + URI + 协议版本
│ Host: localhost:8080                    │  ← 请求头
│ Content-Type: application/json          │
│ Authorization: Bearer xxx               │
│                                         │
│ {"title":"Spring Boot"}                 │  ← 请求体
└─────────────────────────────────────────┘

HTTP 响应报文:
┌─────────────────────────────────────────┐
│ HTTP/1.1 201 Created                    │  ← 状态行:协议版本 + 状态码 + 原因短语
│ Content-Type: application/json          │  ← 响应头
│ Location: /api/lessons/123              │
│                                         │
│ {"id":123,"title":"Spring Boot"}        │  ← 响应体
└─────────────────────────────────────────┘

跟着做

步骤 1:GET 查询——安全且幂等

# 查询课程列表
curl -s http://localhost:8080/api/lessons

# 查询单个课程
curl -s http://localhost:8080/api/lessons/1

# 带查询参数
curl -s "http://localhost:8080/api/lessons?trackId=java-web&page=0&size=10"

GET 的语义是「读取」,不应该有副作用。浏览器会缓存 GET 请求,刷新页面会重新发。

步骤 2:POST 创建——非幂等

curl -X POST http://localhost:8080/api/lessons \
  -H "Content-Type: application/json" \
  -d '{"title":"Spring Security","slug":"spring-security","trackId":"java-web"}'

POST 创建资源,服务器生成 id。多次执行会创建多条记录(除非服务端做去重)。

步骤 3:PUT 全量更新 vs PATCH 部分更新

# PUT:全量替换,必须传完整对象
curl -X PUT http://localhost:8080/api/lessons/1 \
  -H "Content-Type: application/json" \
  -d '{"title":"Spring Boot 进阶","slug":"spring-boot-advanced","trackId":"java-web","content":"..."}'

# PATCH:只改要变的字段
curl -X PATCH http://localhost:8080/api/lessons/1 \
  -H "Content-Type: application/json" \
  -d '{"title":"Spring Boot 进阶(第2版)"}'

PUT 是幂等的(多次替换结果一样),PATCH 取决于实现(如果 patch 是「追加」操作,多次执行会追加多次)。

步骤 4:DELETE 删除

curl -X DELETE http://localhost:8080/api/lessons/1

DELETE 是幂等的:删一次和删十次,最终结果都是「资源不存在」。但注意:第二次删除应该返回 404 还是 204?建议返回 204(幂等成功)或 404(明确告知已不存在),不要抛 500。

步骤 5:状态码实战

@PostMapping
public ResponseEntity<ApiResponse<Lesson>> create(@RequestBody @Valid LessonRequest request) {
    Lesson lesson = lessonService.create(request);
    // 201 Created:资源创建成功
    return ResponseEntity.status(HttpStatus.CREATED)
            .body(ApiResponse.success(lesson));
}

@GetMapping("/{id}")
public ResponseEntity<ApiResponse<Lesson>> getById(@PathVariable Long id) {
    Lesson lesson = lessonService.findById(id);
    if (lesson == null) {
        // 404 Not Found:资源不存在
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                .body(ApiResponse.error(404, "课程不存在"));
    }
    return ResponseEntity.ok(ApiResponse.success(lesson));
}

@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
    lessonService.delete(id);
    // 204 No Content:删除成功,无返回体
    return ResponseEntity.noContent().build();
}

步骤 6:CORS 配置

前端跑在 http://localhost:3000,后端在 http://localhost:8080,浏览器会拦截跨域请求。

@Configuration
public class CorsConfig {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**")
                        .allowedOrigins("http://localhost:3000")
                        .allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")
                        .allowedHeaders("*")
                        .allowCredentials(true)
                        .maxAge(3600);
            }
        };
    }
}

OPTIONS 是预检请求:浏览器先问服务器「我能不能发 POST?」,服务器回答允许的方法和来源,浏览器再发真实请求。

讲清楚要点

  1. GET 是安全的:安全 = 不修改服务器状态。GET 只读取,不能创建、更新、删除。爬虫、预取、缓存都依赖这个假设。
  2. POST vs PUT:POST 是「创建子资源」(服务器决定 URI),PUT 是「替换整个资源」(客户端指定 URI)。POST 非幂等,PUT 幂等。
  3. 状态码是 API 的「表情」:2xx 成功,3xx 重定向,4xx 客户端错误,5xx 服务器错误。别把所有错误都返 200 然后在 body 里写 "code": 500。
  4. Content-Type 决定解析方式:application/json 是主流,application/x-www-form-urlencoded 是表单,multipart/form-data 是文件上传。
  5. CORS 是浏览器行为:服务器之间直接调 HTTP 没有 CORS 限制。CORS 是浏览器保护用户的机制,防止恶意网站偷偷发请求。

你可能会懵的地方

  • PATCH 和 PUT 到底怎么选? PUT 是「全量替换」,传什么就是什么,没传的字段会被清空。PATCH 是「增量修改」,只改传了的字段。大多数业务用 PATCH 更友好,但实现复杂(需要处理字段级别的合并)。

  • 为什么 DELETE 有时返回 204,有时返回 200? 204 = 成功且没有返回体;200 = 成功且有返回体(比如返回被删除的资源快照)。推荐 204,简洁。

  • 401 和 403 区别? 401 Unauthorized = 未认证(没登录或 token 无效);403 Forbidden = 已认证但无权限(普通用户想删管理员数据)。

  • 302 和 307 区别? 302 是「临时重定向」,但历史上浏览器会把 POST 变成 GET;307 严格保持原方法。API 设计里推荐 307/308(永久重定向保持方法)。

  • HTTP/1.1 和 HTTP/2 对 API 设计有影响吗? HTTP/2 多路复用、头部压缩、服务器推送。对 API 设计影响最大的是「不再需要把多个请求合并成一个来减少连接数」,可以更细粒度地拆分资源。

错法对照

错法问题正解
删除成功返回 200 + 空 body浪费带宽,语义不清204 No Content
创建成功返回 200应该明确告知「创建了」201 Created + Location header
所有错误都抛 500调用方无法区分是参数错还是服务器崩参数错 400,未找到 404,权限不足 403
用 GET 做删除操作浏览器预取、爬虫会误删数据DELETE 方法
在 URL 里传大量查询条件URL 长度有限制(约 8KB),且不可读复杂查询用 POST + body,或 GraphQL

卡点急救

  1. 405 Method Not Allowed:URI 存在但方法不对,比如用 POST 访问只支持 GET 的接口。检查 @GetMapping 是否写成了 @PostMapping。
  2. 预检请求 403:CORS 配置没允许 OPTIONS 方法,或 allowedOrigins 不匹配。检查浏览器 Network 面板里 OPTIONS 请求的响应头。
  3. 状态码被改成 200:某些框架或网关会拦截错误。确保全局异常处理器正确设置了 HttpStatus。

面试常问

  • HTTP 状态码分类?各举 3 个例子。 1xx 信息(100 Continue),2xx 成功(200 OK、201 Created、204 No Content),3xx 重定向(301 Moved Permanently、302 Found、304 Not Modified),4xx 客户端错误(400 Bad Request、401 Unauthorized、403 Forbidden、404 Not Found),5xx 服务器错误(500 Internal Server Error、502 Bad Gateway、503 Service Unavailable)。

  • POST 和 PUT 的核心区别? POST 非幂等,用于创建子资源,服务器生成 URI;PUT 幂等,用于全量替换,客户端指定 URI。PUT 可以重复执行不会产生副作用。

  • CORS 预检请求什么时候触发? 非简单请求(如 PUT、DELETE、Content-Type: application/json、自定义 header)会先发送 OPTIONS 预检。服务器响应 Access-Control-Allow-* 后,浏览器才发真实请求。

自测三问

  1. 更新用户邮箱,应该用什么 HTTP 方法?URI 怎么设计?
  2. 如果调用方传了错误的 JSON 格式,应该返回什么状态码?
  3. 为什么 GET 请求不适合传敏感数据?

小结一句话

HTTP 方法定义操作语义,状态码表达执行结果,Header 传递元数据;用对这三者,API 就成功了一半。

延伸

  • MDN HTTP 方法
  • MDN HTTP 状态码
  • 下一课:REST 资源设计——URI 规范、版本策略、分页与幂等性深入。
加载本节练习…
← 上一课API 基础下一课 →REST 资源设计