跳到内容

HTTP API 的错误响应标准

发布于:2024年1月2日

1. 背景

HTTP API 成为了构建现代 Web 应用程序和移动应用程序的常用选择,因为它简单、灵活且易于理解。

对于正常响应,网络上有无数的文章讲过,不赘述。但是对于返回错误信息,并没有形成共识。以下是一些常见的做法:

通过适当选择合适的状态码,可以清楚地表示请求的结果和错误的类型。

{
    "error":{
        "code":400,
        "message":"Invalid request",
        "details":"The request body is missing a required parameter."
    }
}

举几个常用的状态码及其意义的例子:

状态码状态名建议场景
200OK成功
201Created资源被创建
400Bad Request请求参数不合法
401Unauthorized未登录或登录失效
403Forbidden没有访问权限
422Unprocessable Entity请求体验证错误
500Internal Server Error服务器发生错误

今天讨论的重点是响应主体的格式,它可是百花齐放的,举几个业界例子:

1.1. Amazon SP-API

来源: https://developer-docs.amazon.com/sp-api/docs/response-format#error-response

{
    "errors":[
        {
            "message":"Access to requested resource is denied.",
            "code":"Unauthorized",
            "details":"Access token is missing in the request header."
        }
    ]
}

Amazon 这个 API 返回的是错误对象数组,可以支持返回多个错误。

1.2. Azure REST API

来源: https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md

{
    "error":{
        "code":"InvalidPasswordFormat",
        "message":"Human-readable description",
        "target":"target of error",
        "innererror":{
            "code":"PasswordTooShort",
            "minLength":6
        }
    }
}

Azure 这个 API 在发生错误的时候,返回一个 ErrorDetail 对象,这是很常见的一种做法。

1.3. Google Cloud API

来源: https://cloud.google.com/apis/design/errors

{
    "error":{
        "code":400,
        "message":"API key not valid. Please pass a valid API key.",
        "status":"INVALID_ARGUMENT",
        "details":[
            {
                "@type":"type.googleapis.com/google.rpc.ErrorInfo",
                "reason":"API_KEY_INVALID",
                "domain":"googleapis.com",
                "metadata":{
                    "service":"translate.googleapis.com"
                }
            }
        ]
    }
}

Google API 设计指南建议的格式,是一个 Error.Status 对象。

1.4. Twitter/X API

来源: https://developer.twitter.com/en/support/x-api/error-troubleshooting

{
    "client_id":"101010101",
    "required_enrollment":"Standard Basic",
    "registration_url":"[https://developer.twitter.com/en/account](https://developer.twitter.com/en/account)",
    "title":"Client Forbidden",
    "detail":"This request must be made using an approved developer account that is enrolled in the requested endpoint. Learn more by visiting our documentation.",
    "reason":"client-not-enrolled",
    "type":"[https://api.twitter.com/2/problems/client-forbidden](https://api.twitter.com/2/problems/client-forbidden)"
}

Twitter 的错误格式,虽然没明说,其实就是下面要介绍的 RFC 7807/9457。

由此可见,各家都有自己定义的标准。但是其实标准化组织 IANA 已经定义了标准。

2. RFC 9457:Problem Details for HTTP APIs

2.1. 先说 RFC 7807

RFC 7807 是最初的版本。

RFC 7807 的目标是提供一种一致的方式来描述和传递 API 中的错误,以提高互操作性和开发人员的开发体验。它定义了一个标准的 JSON 格式,用于表示问题详情(Problem Details),包括错误代码、错误消息、错误描述等。

以下是 RFC 7807 中定义的 Problem Details 的基本结构:

HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Content-Language: en

{
    "type":"[https://example.com/probs/out-of-credit](https://example.com/probs/out-of-credit)",
    "title":"You do not have enough credit.",
    "detail":"Your current balance is 30, but that costs 50.",
    "instance":"/account/12345/msgs/abc",
    "balance":30,
    "accounts":[
        "/account/12345",
        "/account/67890"
    ]
}

字段解释

使用 RFC 7807 标准,API 可以以统一的方式返回错误和问题信息,使得客户端能够更好地理解和处理这些问题。该标准还提供了一些推荐的扩展字段,用于支持更多的自定义属性,以满足特定应用或领域的需求。

通过采用 RFC 7807 标准,API 开发人员可以提供一致和规范的错误处理机制,提高 API 的可用性、可维护性和互操作性。同时,客户端开发人员也可以更轻松地处理和解析 API 返回的问题信息。

建议还是去看看 RFC 原文: https://www.rfc-editor.org/rfc/rfc7807

2.2. RFC 9457

RFC 9457 是 RFC 7807 的更新版,是会替换 RFC 7807 的。

相对于 RFC 7807,它在继承所有字段的基础上,增加了 errors 错误信息数组,结构如下:

HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
Content-Language: en

{
    "type":"[https://example.net/validation-error](https://example.net/validation-error)",
    "title":"Your request is not valid.",
    "errors":[
        {
            "detail":"must be a positive integer",
            "pointer":"#/age"
        },
        {
            "detail":"must be 'green', 'red' or 'blue'",
            "pointer":"#/profile/color"
        }
    ]
}

这个 errors 字段主要用来描述验证错误,所以需要是一个数组。

errors 字段解释

另外,因为 type 是一个 URL 字段,实际上这个字段可以涵盖多家厂商的错误类型,所以,IANA 建立了一个共享注册表,用来记录多家厂商的错误类型。但是目前为止,这个错误注册表还没有太多的信息。

错误注册表网址: https://www.iana.org/assignments/http-problem-types/http-problem-types.xhtml

更多信息请参考 RFC 原文: https://www.rfc-editor.org/rfc/rfc9457

3. 总结

这个标准还是挺话痨的。但既然有了标准,在设计新的 API 的时候,就尽量使用吧,在 IT 界,有时候的问题就是选择太多了,造成了混乱。与其手搓一个,还不如遵从一个标准。

想起了巴别塔的故事:

巴别塔的传说源自圣经《创世纪》第 11 章。根据传说,当时地球上的人类都使用同一种语言,并聚集在一起建造一座高耸入云的塔,被称为巴别塔(Tower of Babel)。人类的目的是建造一座塔,以达到天堂,并使他们的名字在整个地球上得到认可。然而,上帝看到人类的行为,并决定干涉他们的计划。为了阻止人类完成巴别塔,上帝使人类的语言变得混乱,使他们无法相互理解。人们开始说不同的语言,导致他们无法有效沟通和合作。由于语言的混乱,工程停顿,人们分散到世界各地,形成了不同的民族和语言群体。因此,巴别塔的传说被视为上帝对人类过于自负和傲慢的行为的惩罚,同时也解释了为什么世界上存在着不同的语言和文化。

欢迎关注同名微信公众号,文章自动推送:

nomadic-blood