下面我为你详细解析两种方式的优劣和最佳实践。


方案一:严格遵守HTTP语义(推荐)

这种方式认为,HTTP协议本身已经为状态定义了一套丰富的、标准的语义,我们应该充分利用它。

  • 成功:返回 2xx状态码(如 200 OK, 201 Created)。

  • 客户端错误:返回 4xx状态码(如 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found)。

  • 服务端错误:返回 5xx状态码(如 500 Internal Server Error)。

响应体结构:

无论成功还是失败,响应体都使用一个统一的结构,其中包含更详细的业务信息。

  • 成功响应示例 (HTTP 200):

    {
      "code": 0, // 业务层面的成功代码,0通常表示完全成功
      "message": "操作成功", // 可读的成功信息
      "data": { ... } // 返回的实际数据
    }
  • 错误响应示例 (HTTP 400):

    {
      "code": 1001, // 具体的业务错误代码,如1001代表“邮箱已注册”
      "message": "该邮箱地址已被注册", // 给开发者的详细错误信息
      "details": { ... } // 可选的,更详细的错误信息,如验证错误列表
    }

优点:

  1. 符合标准与约定俗成:遵循RESTful API设计的最佳实践,任何开发者或工具(如网关、监控系统、浏览器)都能通过状态码立即理解请求的大致结果。

  2. 利于基础设施处理:网关、负载均衡器、API监控软件等基础设施可以直接根据HTTP状态码进行逻辑判断(如自动重试5xx错误,但不会重试4xx错误)。

  3. 客户端处理清晰:前端或客户端代码可以很容易地先根据状态码做第一层判断。例如,if (response.status == 200) { ... } else if (response.status == 400) { ... }。

缺点:

  1. 需要开发者对HTTP状态码有较好的理解,避免误用(例如,用200返回业务失败,或用500返回客户端参数错误)。


方案二:全部返回200,业务状态靠Body内的Code

这种方式下,无论业务上是成功还是失败,HTTP状态码一律返回 200。真正的结果完全由响应体中的一个字段(如 code, success)来决定。

  • 成功响应示例 (HTTP 200):

    {
      "success": true,
      "code": 0,
      "message": "操作成功",
      "data": { ... }
    }
  • 错误响应示例 (HTTP 200):

    {
      "success": false,
      "code": 1001, // 业务错误码
      "message": "该邮箱地址已被注册",
      "data": null
    }

优点:

  1. 客户端处理“统一”:对于一些经验较少的开发者,他们可以只检查HTTP状态码是否为200,如果为200,再解析body中的success或code字段,无需关心多种HTTP状态码。在某些前端框架中,非200状态码可能会触发错误捕获,这种方式可以避免这种情况。

缺点:

  1. 破坏HTTP协议语义:这是最大的缺点。一个“邮箱已注册”的错误,本质上就是客户端发送了错误请求(409 Conflict或400 Bad Request),但服务器却告诉客户端“你的请求OK了”(200),这在语义上是矛盾的。

  2. 不利于基础设施:监控系统会认为所有请求都是成功的,无法正确统计API的成功率。网关无法自动处理错误。

  3. 非标准做法:会让有经验的API消费者感到困惑,不符合行业主流实践。


总结与最佳实践建议

强烈推荐使用方案一:充分利用HTTP状态码。

这已经成为现代API设计(尤其是RESTful API)的事实标准。它更专业、更强大,并且能与整个互联网生态系统无缝集成。

一个更完善的、结合了两者优点的混合方案是这样的:

  1. 第一层:HTTP状态码

    • 用于表示HTTP请求本身的成功与否。让网络层、基础设施和客户端第一眼就能知道结果。

  2. 第二层:Body中的业务代码 (code)

    • 用于表示具体业务逻辑的成功与否。比如,HTTP状态码是200,表示请求语法正确且服务器已处理,但业务逻辑上可能失败了(如“余额不足”)。这时可以用 code来区分不同的业务失败。

    • 对于4xx/5xx错误,code可以提供更具体的错误原因,方便前端显示对应的错误提示。

示例场景:

场景

HTTP 状态码

响应体 (JSON)

登录成功

200 OK

{ "code": 0, "message": "成功", "data": { "token": "abc123" } }

用户名或密码错误

401 Unauthorized

{ "code": 1001, "message": "用户名或密码错误" }

请求参数缺失

400 Bad Request

{ "code": 1002, "message": "参数校验失败", "details": { "username": "不能为空" } }

服务器内部异常

500 Internal Server Error

{ "code": 5000, "message": "系统繁忙,请稍后再试" }

结论:

不要将所有返回都设置成200。请将HTTP状态码作为请求结果的“第一道指示”,然后在响应体中用自定义的code和message来提供更精确、更面向业务的第二道指示。这样既遵守了Web标准,又满足了业务灵活性的需求。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐