用AxumStatusCode细化Rust Web标准格式响应
构建统一响应
定义
/// 统一响应类型
pub type ApiResult<T> = Result<(StatusCode, Json<T>), (StatusCode, Json<ErrorResponse>)>;/// 错误消息响应体
#[derive(Serialize, ToSchema)]
pub struct ErrorResponse {pub message: String,
}impl ErrorResponse {pub fn error(status: StatusCode, message: impl Into<String>) -> (StatusCode, Json<Self>) {(status,Json(ErrorResponse {message: message.into(),}),)}
}
使用
成功响应:
无需返回数据的操作成功时通常响应状态码即可,响应StatusCode::OK时用Json()包裹数据
use axum::http::StatusCode;
use axum::Extension;
use axum::extract::State;
pub async fn update_example(State(state): State<AppState>,Extension(user): Extension<User>,
) -> ApiResult<()> {Ok((StatusCode::NO_CONTENT, Json(())))
}
成功响应码在响应头中,如果有数据是以下格式
{"id":1,"name":"张三"}
失败响应:
失败响应通常包含失败提示信息
let total = paginator.num_items().await.map_err(|err| {ErrorResponse::error(StatusCode::INTERNAL_SERVER_ERROR,format!("Failed to count records: {}", err),)})?;
错误响应码在响应体中,如果有数据是以下格式
{"message":"无效的用户ID"}
问题
为什么不使用以下格式?
code:200
data:[]
message:"错误"
将响应码放在响应头的优点:
-
- 符合RESTful规范:严格遵循HTTP协议标准。
-
- 浏览器友好:原生支持状态码处理,如401会自动跳转登录页。
-
- 缓存友好:CDN和浏览器能正确缓存不同状态码的响应。
-
- Json友好:前端直接判断响应码然后进行序列化;如果在响应体中定义code需要先序列化再判断响应结果,无论响应成功和失败都会在响应头中显示200状态码。
CRUD常见响应码
Create 创建
成功响应:
201 Created
- 资源创建成功
失败响应:
400 Bad Request
- 请求体无效(如字段验证失败)409 Conflict
- 资源已存在(如唯一键冲突)422 Unprocessable Entity
- 语义错误(如格式正确但业务规则不满足)
Read 读取
成功响应
200 OK
- 资源获取成功204 No Content
- 成功但无内容(如查询结果为空列表)
失败响应
401 Unauthorized
- 未认证403 Forbidden
- 无权限访问404 Not Found
- 资源不存在410 Gone
- 资源已被永久删除
Update 更新
成功响应
200 OK
- 返回更新后的完整资源204 No Content
- 成功但无需返回内容(常见于 PUT/PATCH)
失败响应
400 Bad Request
- 无效的更新数据403 Forbidden
- 无权修改该资源404 Not Found
- 要更新的资源不存在412 Precondition Failed
- 条件请求失败(如版本冲突)
Delete 删除
成功响应
204 No Content
- 删除成功(推荐不返回内容)200 OK
- 删除成功并返回被删除的资源(较少用)
失败响应
403 Forbidden
- 无权删除404 Not Found
- 要删除的资源不存在423 Locked
- 资源被锁定
AxumStatusCode所有响应码
1xx:信息响应:
状态码 | 名称 | 类型 | 含义/用途说明 |
---|---|---|---|
100 | Continue | 信息提示 | 初步请求已接受,客户端应继续发送请求主体。常用于带 Expect: 100-continue 的请求。 |
101 | Switching Protocols | 协议切换 | 服务器同意客户端更改协议(如从 HTTP 升级为 WebSocket)。 |
102 | Processing | 正在处理 | WebDAV 扩展,服务器已接受但尚未完成处理。 |
2xx:成功响应 : | |||
状态码 | 名称 | 类型 | 含义/用途说明 |
— | — | — | — |
200 | OK | 成功 | 请求成功且返回内容(最常见)。 |
201 | Created | 创建成功 | 成功创建了资源(如 POST 创建用户)。 |
202 | Accepted | 接受处理 | 请求已接受,但未完成处理(异步操作常见)。 |
203 | Non-Authoritative Information | 非授权信息 | 返回的信息来自第三方源而非原始服务器。 |
204 | No Content | 无内容 | 请求成功但无返回内容(常用于 DELETE)。 |
205 | Reset Content | 重置内容 | 请求成功,请客户端重置表单或视图。 |
206 | Partial Content | 部分内容 | 支持 HTTP 分块下载(Range 请求)。 |
207 | Multi-Status | 多状态 | WebDAV 扩展,返回多个状态(XML)。 |
208 | Already Reported | 已报告 | WebDAV 扩展,避免重复报告。 |
226 | IM Used | 实体操作成功 | 返回对资源的某种变换(如增量更新)。 |
3xx:重定向:
状态码 | 名称 | 类型 | 含义/用途说明 |
---|---|---|---|
300 | Multiple Choices | 多种选择 | 请求有多种可选响应。 |
301 | Moved Permanently | 永久重定向 | 资源已永久移动。客户端应更新地址。 |
302 | Found | 临时重定向 | 资源临时移动。旧客户端行为与规范不完全一致。 |
303 | See Other | 查看其他位置 | 应使用 GET 获取资源(常用于 POST 后跳转)。 |
304 | Not Modified | 未修改 | 缓存验证响应,无需重新下载资源。 |
305 | Use Proxy | 使用代理 | 已废弃,指定使用代理(不推荐使用)。 |
307 | Temporary Redirect | 临时重定向 | 与 302 类似,但强制使用原请求方法。 |
308 | Permanent Redirect | 永久重定向 | 与 301 类似,但强制使用原请求方法。 |
4xx:客户端错误:
状态码 | 名称 | 类型 | 含义/用途说明 |
---|---|---|---|
400 | Bad Request | 请求格式错误 | 请求无效,可能参数缺失或格式错误。 |
401 | Unauthorized | 未授权 | 缺乏身份验证信息或 Token 无效。 |
402 | Payment Required | 需付费 | 预留状态码,尚未广泛使用。 |
403 | Forbidden | 被禁止 | 有效身份但无权限。 |
404 | Not Found | 资源不存在 | 请求的资源未找到。 |
405 | Method Not Allowed | 方法不被允许 | 请求方法不支持当前资源。 |
406 | Not Acceptable | 不可接受 | 请求的内容类型不符合服务端设置。 |
407 | Proxy Authentication Required | 代理需身份验证 | 与 401 类似,但用于代理认证。 |
408 | Request Timeout | 请求超时 | 客户端未及时发送完整请求。 |
409 | Conflict | 冲突 | 请求与服务器资源状态冲突(如并发更新)。 |
410 | Gone | 已永久删除 | 资源永久不可用,服务器不再维护。 |
411 | Length Required | 长度缺失 | 需指定 Content-Length。 |
412 | Precondition Failed | 前提条件失败 | 请求头中的某些条件不满足。 |
413 | Payload Too Large | 载荷过大 | 请求体超过服务器处理能力。 |
414 | URI Too Long | URI 过长 | 请求路径超过限制。 |
415 | Unsupported Media Type | 媒体类型不支持 | Content-Type 不被接受。 |
416 | Range Not Satisfiable | 范围请求无效 | 请求的 Range 无效。 |
417 | Expectation Failed | 期望失败 | Expect 请求头指定的内容无法被满足。 |
418 | I’m a teapot | 我是茶壶 | 彩蛋状态码,来自愚人节 RFC2324。 |
421 | Misdirected Request | 请求方向错误 | 请求被发送到错误的服务器实例。 |
422 | Unprocessable Entity | 无法处理实体 | 请求格式正确但语义错误(常用于表单校验失败)。 |
423 | Locked | 资源被锁定 | WebDAV 扩展。 |
424 | Failed Dependency | 依赖失败 | 前序请求失败,导致当前操作失败。 |
425 | Too Early | 太早发出请求 | 请求过早,可能会重放。 |
426 | Upgrade Required | 需要升级协议 | 客户端需升级协议(如 TLS)。 |
428 | Precondition Required | 要求前提条件 | 需附带条件请求(如 If-Match 头)。 |
429 | Too Many Requests | 请求过多 | 触发限流策略。 |
431 | Request Header Fields Too Large | 请求头太大 | 请求头字段过多或过长。 |
451 | Unavailable For Legal Reasons | 法律原因不可用 | 被审查或屏蔽(如因 GDPR、DMCA 等法律)。 |
5xx:服务器错误:
状态码 | 名称 | 类型 | 含义/用途说明 |
---|---|---|---|
500 | Internal Server Error | 服务器内部错误 | 通用错误,服务器出错但无法具体说明。 |
501 | Not Implemented | 未实现 | 请求的方法未被支持。 |
502 | Bad Gateway | 网关错误 | 代理服务器从上游返回无效响应。 |
503 | Service Unavailable | 服务不可用 | 服务器过载或维护中。 |
504 | Gateway Timeout | 网关超时 | 代理等待上游超时。 |
505 | HTTP Version Not Supported | HTTP版本不支持 | 不支持请求使用的 HTTP 协议版本。 |
506 | Variant Also Negotiates | 协议协商失败 | 服务端配置错误,不能选择合适的响应变体。 |
507 | Insufficient Storage | 存储空间不足 | WebDAV 扩展,服务器无法存储资源。 |
508 | Loop Detected | 检测到循环 | WebDAV 扩展,检测到无限循环。 |
510 | Not Extended | 扩展未提供 | 需进一步扩展请求。 |
511 | Network Authentication Required | 需网络认证 | 例如 Wi-Fi 登录门户页拦截。 |