说说HTTP常见的状态码有哪些,适用场景
问题解析
HTTP状态码是服务器对客户端请求的响应结果标识,面试官通过这个问题考察候选人对HTTP协议的掌握程度,以及能否正确理解和使用各种状态码。
核心概念
HTTP状态码概述
HTTP状态码(Status Code)是3位数字,表示服务器对请求的处理结果。状态码分为5个类别:
| 类别 | 范围 | 含义 |
|---|---|---|
| 1xx | 100-199 | 信息性状态码(Informational) |
| 2xx | 200-299 | 成功状态码(Success) |
| 3xx | 300-399 | 重定向状态码(Redirection) |
| 4xx | 400-499 | 客户端错误状态码(Client Error) |
| 5xx | 500-599 | 服务器错误状态码(Server Error) |
状态码格式
HTTP/1.1 200 OK
Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
详细解答
1xx 信息性状态码
表示请求已被接收,需要继续处理。
100 Continue
客户端 服务器
|---- POST (Expect: 100-continue) --->|
| |
|<----------- 100 Continue -----------|
| |
|----------- 请求体 ----------------->|
| |
|<----------- 200 OK -----------------|
场景: 客户端发送大请求体前,询问服务器是否愿意接收
使用:
import requests
headers = {'Expect': '100-continue'}
response = requests.post(url, headers=headers, data=large_data)
101 Switching Protocols
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
场景: 服务器同意切换协议(如HTTP升级到WebSocket)
2xx 成功状态码
表示请求已被成功接收、理解和处理。
200 OK
HTTP/1.1 200 OK
Content-Type: application/json
{
"status": "success",
"data": { ... }
}
场景: 请求成功,返回所请求的资源
使用范围:
- GET请求:返回资源
- POST请求:返回操作结果
- PUT/PATCH请求:返回更新后的资源
- DELETE请求:返回删除确认
201 Created
HTTP/1.1 201 Created
Location: /api/users/123
Content-Type: application/json
{
"id": 123,
"name": "张三",
"created_at": "2024-01-01T00:00:00Z"
}
场景: 资源创建成功
使用:
- POST创建资源后返回
- 应包含Location头部指向新资源
202 Accepted
HTTP/1.1 202 Accepted
Location: /api/tasks/456
{
"task_id": 456,
"status": "processing",
"message": "请求已接受,正在处理"
}
场景: 请求已接受,但尚未处理完成(异步处理)
使用:
- 耗时操作(如批量处理、视频转码)
- 返回任务ID供查询进度
204 No Content
HTTP/1.1 204 No Content
场景: 请求成功,但无返回内容
使用:
- DELETE删除成功
- PUT/PATCH更新成功(无需返回资源)
- 预检请求响应
206 Partial Content
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-999/5000
Content-Length: 1000
[部分数据]
场景: 成功处理范围请求(断点续传、视频分段加载)
使用:
GET /video.mp4 HTTP/1.1
Range: bytes=0-999
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-999/5000
3xx 重定向状态码
表示需要客户端采取进一步操作才能完成请求。
301 Moved Permanently
HTTP/1.1 301 Moved Permanently
Location: https://new-domain.com/page
场景: 资源永久移动到新的URL
特点:
- 搜索引擎会更新索引
- 浏览器会缓存重定向
- 后续请求直接访问新地址
302 Found
HTTP/1.1 302 Found
Location: https://example.com/login
场景: 资源临时移动到新的URL
注意:
- 历史上实现不规范,有歧义
- 部分客户端会将POST改为GET(不符合规范)
304 Not Modified
HTTP/1.1 304 Not Modified
ETag: "33a64df5"
Cache-Control: max-age=3600
场景: 资源未修改,使用缓存版本
使用:
GET /style.css HTTP/1.1
If-None-Match: "33a64df5"
HTTP/1.1 304 Not Modified
307 Temporary Redirect
HTTP/1.1 307 Temporary Redirect
Location: https://example.com/new-path
场景: 临时重定向,保持请求方法不变
与302的区别:
- 307要求保持原请求方法(POST仍是POST)
- 302可能被实现为方法改变
308 Permanent Redirect
HTTP/1.1 308 Permanent Redirect
Location: https://example.com/new-path
场景: 永久重定向,保持请求方法不变
与301的区别:
- 308要求保持原请求方法
- 301可能被实现为方法改变
4xx 客户端错误状态码
表示客户端请求有误,服务器无法处理。
400 Bad Request
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "Bad Request",
"message": "请求参数格式错误",
"details": {
"email": "邮箱格式不正确"
}
}
场景: 请求语法错误或参数无效
使用:
- 参数缺失或格式错误
- 请求体解析失败
- 业务校验失败
401 Unauthorized
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"
{
"error": "Unauthorized",
"message": "缺少有效的身份认证"
}
场景: 需要身份认证但未提供或认证失败
注意:
- 401表示"未认证"
- 必须包含WWW-Authenticate头部
403 Forbidden
HTTP/1.1 403 Forbidden
{
"error": "Forbidden",
"message": "没有权限访问该资源"
}
场景: 服务器理解请求,但拒绝执行
401 vs 403:
401:你是谁?(未认证)
403:我知道你是谁,但你不允许访问(无权限)
404 Not Found
HTTP/1.1 404 Not Found
{
"error": "Not Found",
"message": "请求的资源不存在"
}
场景: 请求的资源不存在
使用:
- URL路径错误
- 资源已被删除
- 资源ID不存在
405 Method Not Allowed
HTTP/1.1 405 Method Not Allowed
Allow: GET, POST, HEAD
{
"error": "Method Not Allowed",
"message": "不支持DELETE方法"
}
场景: 请求方法不被允许
注意:
- 必须返回Allow头部
409 Conflict
HTTP/1.1 409 Conflict
{
"error": "Conflict",
"message": "资源状态冲突",
"details": "该用户名已被占用"
}
场景: 请求与服务器当前状态冲突
使用:
- 重复创建已存在资源
- 版本冲突(乐观锁)
- 并发修改冲突
410 Gone
HTTP/1.1 410 Gone
{
"error": "Gone",
"message": "该资源已永久删除"
}
场景: 资源曾经存在,但已永久删除
与404的区别:
- 404:资源不存在(可能是URL错误)
- 410:资源曾经存在,现在已删除
422 Unprocessable Entity
HTTP/1.1 422 Unprocessable Entity
{
"error": "Unprocessable Entity",
"message": "语义错误",
"details": "库存不足,无法创建订单"
}
场景: 请求格式正确,但语义错误(如业务规则验证失败)
400 vs 422:
400:语法错误(JSON格式错误、参数类型错误)
422:语义错误(JSON格式正确,但业务校验失败)
429 Too Many Requests
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640995200
{
"error": "Too Many Requests",
"message": "请求过于频繁,请稍后再试"
}
场景: 请求过于频繁,被限流
5xx 服务器错误状态码
表示服务器处理请求时发生错误。
500 Internal Server Error
HTTP/1.1 500 Internal Server Error
{
"error": "Internal Server Error",
"message": "服务器内部错误",
"request_id": "abc123"
}
场景: 服务器内部错误,无法完成请求
使用:
- 未捕获的异常
- 代码逻辑错误
- 不应向用户暴露详细错误信息
502 Bad Gateway
HTTP/1.1 502 Bad Gateway
{
"error": "Bad Gateway",
"message": "网关或代理收到无效响应"
}
场景: 网关或代理从上游服务器收到无效响应
常见原因:
- 后端服务未启动
- 后端服务崩溃
- 网络不通
503 Service Unavailable
HTTP/1.1 503 Service Unavailable
Retry-After: 60
{
"error": "Service Unavailable",
"message": "服务暂时不可用"
}
场景: 服务器暂时无法处理请求(过载或维护)
使用:
- 服务维护中
- 服务器过载
- 熔断器打开
504 Gateway Timeout
HTTP/1.1 504 Gateway Timeout
{
"error": "Gateway Timeout",
"message": "网关超时"
}
场景: 网关或代理在等待上游服务器响应时超时
502 vs 504:
502:收到无效响应(连接成功,但响应错误)
504:未收到响应(连接成功,但等待超时)
深入理解
状态码选择决策树
请求处理结果
|
├─ 成功?
│ ├─ 创建资源?
│ │ ├─ 是 → 201 Created
│ │ └─ 否 → 继续判断
│ │
│ ├─ 无返回内容?
│ │ ├─ 是 → 204 No Content
│ │ └─ 否 → 继续判断
│ │
│ ├─ 部分数据?
│ │ ├─ 是 → 206 Partial Content
│ │ └─ 否 → 200 OK
│ │
│ └─ 异步处理?
│ ├─ 是 → 202 Accepted
│ └─ 否 → 200 OK
│
├─ 重定向?
│ ├─ 永久移动?
│ │ ├─ 保持方法?
│ │ │ ├─ 是 → 308 Permanent Redirect
│ │ │ └─ 否 → 301 Moved Permanently
│ │ └─ 临时移动?
│ │ ├─ 保持方法?
│ │ │ ├─ 是 → 307 Temporary Redirect
│ │ │ └─ 否 → 302 Found
│ │ └─ 缓存验证?
│ │ └─ 是 → 304 Not Modified
│
├─ 客户端错误?
│ ├─ 参数错误?
│ │ ├─ 语法错误?
│ │ │ ├─ 是 → 400 Bad Request
│ │ │ └─ 否 → 422 Unprocessable Entity
│ │ └─ 未认证?
│ │ ├─ 是 → 401 Unauthorized
│ │ └─ 无权限?
│ │ ├─ 是 → 403 Forbidden
│ │ └─ 不存在?
│ │ ├─ 是 → 404 Not Found
│ │ └─ 方法不允许?
│ │ ├─ 是 → 405 Method Not Allowed
│ │ └─ 冲突?
│ │ ├─ 是 → 409 Conflict
│ │ └─ 限流?
│ │ └─ 是 → 429 Too Many Requests
│
└─ 服务器错误?
├─ 内部错误?
│ ├─ 是 → 500 Internal Server Error
│ └─ 网关错误?
│ ├─ 无效响应?
│ │ ├─ 是 → 502 Bad Gateway
│ │ └─ 超时?
│ │ ├─ 是 → 504 Gateway Timeout
│ │ └─ 服务不可用?
│ │ └─ 是 → 503 Service Unavailable
RESTful API状态码规范
成功响应:
GET /users → 200 OK + 用户列表
GET /users/123 → 200 OK + 用户详情
POST /users → 201 Created + 新用户 + Location头
PUT /users/123 → 200 OK + 更新后用户 / 204 No Content
PATCH /users/123 → 200 OK + 更新后用户 / 204 No Content
DELETE /users/123 → 204 No Content
错误响应:
400 Bad Request → 参数验证失败
401 Unauthorized → 未登录
403 Forbidden → 无权限
404 Not Found → 资源不存在
409 Conflict → 资源冲突
422 Unprocessable → 业务规则验证失败
429 Too Many Requests → 请求过于频繁
500 Internal Server Error → 服务器内部错误
状态码与SEO
对SEO友好的状态码:
200 OK 正常索引
301 Redirect 传递权重到新URL
302 Redirect 不传递权重(临时跳转)
404 Not Found 正常处理,不索引
410 Gone 告诉搜索引擎删除索引
500 Error 影响网站评分
最佳实践
API错误响应格式
{
"error": {
"code": "INVALID_PARAMETER",
"message": "请求参数错误",
"status": 400,
"details": [
{
"field": "email",
"message": "邮箱格式不正确"
},
{
"field": "age",
"message": "年龄必须在18-100之间"
}
],
"path": "/api/users",
"timestamp": "2024-01-01T00:00:00Z",
"request_id": "req_abc123"
}
}
状态码使用规范
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/api/users', methods=['POST'])
def create_user():
data = request.get_json()
# 参数校验 → 400
if not data or 'email' not in data:
return jsonify({
'error': 'Bad Request',
'message': '缺少必需参数:email'
}), 400
# 业务校验 → 422
if not validate_email(data['email']):
return jsonify({
'error': 'Unprocessable Entity',
'message': '邮箱格式不正确'
}), 422
# 冲突检查 → 409
if User.query.filter_by(email=data['email']).first():
return jsonify({
'error': 'Conflict',
'message': '该邮箱已被注册'
}), 409
# 创建成功 → 201
user = User.create(data)
return jsonify({
'id': user.id,
'email': user.email,
'created_at': user.created_at
}), 201, {'Location': f'/api/users/{user.id}'}
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
user = User.query.get(user_id)
# 资源不存在 → 404
if not user:
return jsonify({
'error': 'Not Found',
'message': f'用户 {user_id} 不存在'
}), 404
# 成功 → 200
return jsonify({
'id': user.id,
'email': user.email
}), 200
@app.errorhandler(500)
def internal_error(error):
# 服务器错误 → 500
return jsonify({
'error': 'Internal Server Error',
'message': '服务器内部错误',
'request_id': g.request_id
}), 500
Nginx状态码配置
# 自定义错误页面
error_page 400 /errors/400.html;
error_page 401 /errors/401.html;
error_page 403 /errors/403.html;
error_page 404 /errors/404.html;
error_page 500 502 503 504 /errors/50x.html;
# 精确匹配错误页面
location = /errors/400.html {
internal;
}
# 限流返回429
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location /api/ {
limit_req zone=api burst=20 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
面试要点
- 状态码分类:1xx信息、2xx成功、3xx重定向、4xx客户端错误、5xx服务器错误
- 常用状态码:200、201、204、301、302、304、400、401、403、404、500、502、503
- 401 vs 403:401未认证,403无权限
- 400 vs 422:400语法错误,422语义错误
- 301 vs 302:301永久重定向,302临时重定向
常见面试追问:
- 401和403有什么区别?
- 301和302有什么区别?
- 什么时候用201,什么时候用200?
- 400和422有什么区别?
- 502和504有什么区别?
- 如何设计API错误响应?