有没有JSON API响应格式的标准?

916

有没有关于从API构建JSON响应的标准或最佳实践?显然,每个应用程序的数据都是不同的,所以我并不担心这一点,但是我更关心的是“响应模板”,如果你愿意这么说。我举个例子来说明:

成功请求:

{
  "success": true,
  "payload": {
    /* Application-specific data would go here. */
  }
}

请求失败:

{
  "success": false,
  "payload": {
    /* Application-specific data would go here. */
  },
  "error": {
    "code": 123,
    "message": "An error occurred!"
  }
}

26
人们可能已经从SOAP中学到了东西,不会再重复建造它。 - Denys Séguret
28
@dystroy:能解释一下你的评论吗? - FtDRbwLXw6
7
我对这个问题非常感兴趣,因为最近我设计了一个 JSON API,并想知道是否有任何标准定义响应格式。你的方案看起来相当不错,如果找不到标准,值得使用。很遗憾,提供的答案实际上并没有回答这个问题。 - Alex
18
很遗憾,无论你到哪里,都没有一个标准。这不仅适用于 JSON 本身,也包括如何将其用于 RESTful 应用程序或其他类似应用的内容。每个人都有自己的做法。你可以遵循最佳实践(HTTP 响应、有意义的包结构、考虑为系统消费而设计数据结构等),但是所有主要分发商都至少在某些方面与其他人不同... 没有标准,并且可能不会有标准,所以建立一个坚实的东西,让它适合自己的需求即可。 - Norguard
7
@Norguard,有一些标准(请参考我的回答)。实际上,“标准的好处就是你可以选择这么多。”——安德鲁·塔能鲍姆。 - Adam Gent
显示剩余12条评论
18个回答

837

是的,有几个标准(尽管一些标准定义上可能存在自由度)已经出现:

  1. JSON API - JSON API 也涵盖创建和更新资源,而不仅仅是响应。
  2. JSend - 简单明了,可能您已经在使用它。
  3. OData JSON Protocol - 非常复杂。
  4. HAL - 类似于OData,但旨在实现HATEOAS

还有针对 JSON API 的描述格式:


28
谢谢。特别是 JSend,正是我在寻找的东西。它与我之前所做的类似,但有一些我方法没有的好处。公平地说,JSend 与 @trungly 的答案非常相似。 - FtDRbwLXw6
11
对于错误响应,我还喜欢使用《HTTP API问题详细信息》RFC草案。 - Pieter Ennes
1
也许您想将 https://code.google.com/p/json-service/ 添加到描述格式列表中? - emilesilvis
1
我认为“Rails 推荐标准”标签有点言过其实-这只是一个程序员的解决方案。不确定是什么让它成为“推荐标准”(特别是如果你看看 gem 的流行程度-似乎并没有那么多人在使用它)?我个人认为,大多数 Rails 程序员不会推荐使用响应正文而不是 HTTP 标头来表示状态。 - Iwo Dziechciarow
3
谷歌 JSON 格式指南(Google JSON Style Guide)也是一个不错的参考。 - MRodrigues
显示剩余2条评论

303

Google JSON指南

成功响应返回data

{
  "data": {
    "id": 1001,
    "name": "Wing"
  }
}

错误响应返回 error

{
  "error": {
    "code": 404,
    "message": "ID not found"
  }
}

如果你的客户端是 JS,你可以使用 if ("error" in response) {} 来检查是否有错误。


2
我不确定你是否能够从服务器端的JSON API(如PlayJson)处理这个问题,但无论如何都没关系。@Steely,你的链接已经失效了。 - Rhys Bradbury
4
需要提供失败列表的错误,例如验证问题,应该如何处理? - Xeoncross
2
@Xeoncross 点击单词 error 上的链接,谷歌页面会给出一个例子。 - user4698348
1
@Xeoncross,您可以使用error.errors[]返回失败列表,其定义为:“包含有关错误的任何其他信息的容器。如果服务返回多个错误,则错误数组中的每个元素表示不同的错误。”也许顶级错误会提到“请求失败的输入验证”,而errors[]数组将针对每个发生的特定验证失败具有一个条目。 - James Daily
@Steely Wing ->>> StatusCode: 304,ReasonPhrase:'未修改',Version:1.1,Content:<null>,Headers:{} <<<< 这是一个合适的RestApi响应吗? - Arnold Brown
显示剩余2条评论

188
我猜想一种事实上的标准并没有真正出现(也许永远不会出现)。但是,无论如何,这是我的看法:
成功的请求:
{
  "status": "success",
  "data": {
    /* Application-specific data would go here. */
  },
  "message": null /* Or optional success message */
}

请求失败:

{
  "status": "error",
  "data": null, /* or optional error payload */
  "message": "Error xyz has occurred"
}

优点:在成功和错误情况下使用相同的顶级元素。

缺点:没有错误代码,但如果需要,可以将状态更改为成功或失败代码,或者添加另一个名为“code”的顶级项。


4
如果您正在使用POJO进行JSON解析,那么这是正确的方法!当我们使用POJO时,需要使用静态、非动态的JSON格式! - LOG_TAG
1
我也使用这种模式,但是使用一个名为messages的字段,它是消息数组,而不是单个字符串。 - StockBreak
5
答案几乎是 JSend 的翻版,该文档非常简单易懂且实用。其中提供了第三种状态 fail,用于典型的验证问题,而 error 仅在像数据库错误这样的致命问题时使用。 - s3m3n
3
对于成功:如果头部信息包含“200”,为什么还需要一个“状态”字段呢?直接返回数据对象即可。你应该知道这会给像TypeScript这样的强类型前端语言带来额外的麻烦。 - Deniss M.
1
@OnwukaGideon 如果你只会读取HTTP状态码中的 status === 200,那么你可能没有理解HTTP状态码的意义。浏览器原生的 fetch API 响应包括一个布尔值 ok,用于指示是否返回了任何非成功状态。您始终可以使用比较运算符检查任何在 2XX 范围之外的内容。Swagger/OpenAPI 建议使用状态码报告错误: https://swagger.io/blog/api-design/api-design-best-practices/#responses - Jon Hieb
显示剩余7条评论

112
假设您的问题是关于REST Web服务设计,更具体地涉及成功/错误方面。
我认为有三种不同类型的设计。
  1. 仅使用HTTP状态码来指示是否出现错误,并尝试限制自己使用标准代码(通常应该足够)。

    • 优点:这是一个与您的API无关的标准。
    • 缺点:提供的信息较少。
  2. 使用HTTP状态码+json body(即使是错误)。为错误定义统一的结构(例如:code、message、reason、type等),并将其用于错误,如果是成功,则返回预期的json响应。

    • 优点:仍然是标准的,因为您使用现有的HTTP状态代码,并返回描述错误的json(提供更多有关发生情况的信息)。
    • 缺点:输出的json将根据是错误还是成功而变化。
  3. 忘记HTTP状态码(例如:始终为状态200),始终使用json,并在响应的根部添加一个布尔值responseValid和一个错误对象(code、message等),如果是错误,则填充它,否则填充其他字段(成功)。

    • 优点:客户端只处理响应的主体,它是一个json字符串,并忽略状态(?)。

    • 缺点:标准性较低。

由你决定 :)

根据API的不同,我会选择2或3(我更喜欢用于json rest api的2)。 在设计REST API时,我还经历过为每个资源(URL)编写文档的重要性:参数、正文、响应、标头等+示例。

我还建议您使用jersey(jax-rs实现)+ genson(java / json数据绑定库)。 您只需将genson + jersey放入类路径中,即可自动支持json。

编辑:

  • 解决方案2最难实现,但优点是可以很好地处理异常而不仅仅是业务错误,初始工作量更重要,但长期来看会更有优势。

  • 解决方案3对于服务器端和客户端都很容易实现,但它并不像你希望的那样好,因为你必须将要返回的对象封装在包含响应有效性+错误的响应对象中。


2
你说我应该“为错误定义一个统一的结构”和其他类似的建议,但这正是我所询问的。我猜答案是“不,关于这个结构没有标准或最佳实践。” - FtDRbwLXw6
8
记录一下:HTTP状态码不是一个头部(header)信息。 - pepkin88
3
回复不会是json,而是html。错误!HTML与错误处理无关。响应可以是任何您支持的内容类型。 - oligofren
2
@アレックス HTTP状态码是HTTP响应头中状态行的3位数代码。在该行之后是标头字段,俗称为标头。 - pepkin88
1
@アレック斯,HTTP的维基百科页面很好地回答了你的问题,你可以在这里查看:https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Response_message(链接到响应消息部分)。 - pepkin88
显示剩余2条评论

37

1
三年后...似乎是前进的方向。请参见:https://youtu.be/vcjj5pT0bSQ?t=611(Visual Studio .Net核心支持7807) - edelwater

29

以下是Instagram使用的JSON格式:

{
    "meta": {
         "error_type": "OAuthException",
         "code": 400,
         "error_message": "..."
    }
    "data": {
         ...
    },
    "pagination": {
         "next_url": "...",
         "next_max_id": "13872296"
    }
}

24

我不会傲慢到声称这是一个标准,所以我将使用“我更喜欢”的形式。

在我的设计中,当请求文章列表时,我更喜欢简洁的响应(返回文章的JSON数组)。

我使用HTTP进行状态报告。 200 只返回有效载荷。

400 返回请求出错的消息:

{"message" : "Missing parameter: 'param'"}

如果模型/控制器/URI不存在,则返回404

如果在我的一侧处理时出现错误,则返回带有消息的501

{"message" : "Could not connect to data store."}

根据我所看到的,相当多的REST-ish框架往往沿这个方向。

理念:

JSON被认为是一种负载格式,它不是一个会话协议。冗长的会话样式负载的整个想法来自XML/SOAP世界和各种误导性选择,这些选择导致了那些臃肿的设计。在我们意识到所有这一切都是一个巨大的头痛之后,REST/JSON的整个重点就是要简化它,并坚持HTTP。我认为在JSend中没有任何标准可言,尤其是其中更冗长的部分。XHR将对HTTP响应做出反应,如果您使用jQuery进行AJAX(大多数人都这样做),您可以使用try/catchdone()/fail()回调来捕获错误。我无法看出将状态报告封装在JSON中是否比那更有用。


3
JSON是一种数据序列化格式,可用于传输任何您想要的内容,包括会话协议或简单负载。您的KISS评论很到位,与JSON无关。最好将JSON集中在其所代表的内容上(如您所描述的成功数据或失败原因数据),而不是混合使用它来构建需要反复组合和删除的杂乱数据。然后你可以把JSON数据存储在Couchbase中并将其原样返回给应用程序。 - Dirk Bester
1
也许我应该将其表述为“应该是有效载荷格式”,但除此之外,我仍然坚持我的评论。你可以将会话/错误数据作为HTML文档中body标签的属性,但这并不意味着这是正确或明智的做法。 - Bojan Markovic

20
就我个人而言,我会用不同的方式处理。成功的调用只返回JSON对象,我不需要一个更高级别的JSON对象来表示成功和负载,我只是在头部返回适当的HTTP状态码所对应的JSON对象。
但是,如果出现错误(例如400错误),我会返回格式良好的JSON错误对象。例如,如果客户端使用POST方法提交用户信息,其中电子邮件或电话号码格式不正确(即无法插入到数据库中),我将返回以下内容:
{
  "description" : "Validation Failed"
  "errors" : [ {
    "field" : "phoneNumber",
    "message" : "Invalid phone number."
  } ],
}

重要的部分是"field"属性必须精确匹配无法验证的JSON字段。这允许客户端准确地了解其请求出了什么问题。此外,"message"是请求的区域设置语言。如果“emailAddress”和“phoneNumber”都无效,则“errors”数组将包含两者的条目。 409(冲突)JSON响应体可能如下所示:

{
  "description" : "Already Exists"
  "errors" : [ {
    "field" : "phoneNumber",
    "message" : "Phone number already exists for another user."
  } ],
}

通过HTTP状态码和这个JSON,客户端具备了响应错误的确定性方式,而不会创建一个试图完全替代HTTP状态码的新错误标准。需要注意的是,这些仅适用于400错误范围内的任何内容。对于200范围内的任何内容,我只需返回适当的内容即可。对于我来说,通常是一种类似HAL的JSON对象,但在这里并不重要。

我考虑增加的一件事是在“errors”数组条目或JSON对象本身的根中添加数字错误代码。但到目前为止我们还没有需要它。


19

谷歌、Facebook、Twitter和亚马逊等大型软件公司的REST API响应格式没有达成一致,虽然在上面的回答中提供了许多链接,有些人试图标准化响应格式。

由于API的需求可能不同,很难让每个人都接受某种格式。如果你有数百万用户使用你的API,你为什么要改变你的响应格式呢?

以下是我基于谷歌、Twitter、亚马逊和一些互联网帖子的响应格式思路:

https://github.com/adnan-kamili/rest-api-response-format

Swagger文件:

https://github.com/adnan-kamili/swagger-sample-template


1
赞同无信封的REST API响应格式。 - Kerem Baydoğan
@adnan kamilli ->>> StatusCode: 304,ReasonPhrase: '未修改',Version: 1.1,Content:<null>,Headers:{} <<<< 这是RestApi的正确响应吗? - Arnold Brown
@ArnoldBrown 你是为哪个API端点 - 操作返回这段代码的? - adnan kamili
这是一个API的响应返回,用于上传图像(表单数据)-客户端编写的API。 - Arnold Brown

9
JSON的优点在于它完全是动态和灵活的。您可以将其弯曲成任何您想要的形式,因为它只是一组序列化的JavaScript对象和数组,根据单个节点的根节点进行排列。
根节点的类型由您决定,它包含的内容也由您决定,是否在响应中发送元数据由您决定,无论您将mime-type设置为“application/json”还是将其保留为“text/plain”,都由您决定(只要您知道如何处理边缘情况)。
构建一个您喜欢的轻量级模式。就我个人而言,我发现分析跟踪、mp3/ogg服务、图像库服务、短信和在线游戏的网络数据包、博客文章和博客评论在所发送和接收的内容方面以及在使用方式上都有非常不同的要求。
因此,当我处理所有这些事情时,最不希望的是尝试使每个项目符合基于XML2.0或类似技术的相同样板标准。
话虽如此,使用对您有意义且经过深思熟虑的模式确实有很多好处。只需阅读一些API响应,注意您喜欢的内容,批评您不喜欢的内容,将这些批评记录下来并了解为什么它们让您感到不舒服,然后考虑如何将所学应用于您的需求。

2
谢谢您的回复,但是我仍然不担心有效载荷本身。尽管您的示例在_有效载荷_中发送/接收内容及如何使用这些_有效载荷_方面要求非常不同,但它们都必须解决有关_响应本身_的相同问题。也就是说,它们都需要确定请求是否成功。如果成功,则继续处理。如果没有成功,则出了什么问题。我在我的问题中指的是所有API响应都通用的样板文件。 - FtDRbwLXw6
1
要么对所有内容返回状态200,并定义特定的错误负载,要么根据错误返回相应的状态,可以在负载主体中包含更多细节(如果支持)。就像我说的,模式由您决定 - 包括任何元/状态信息。这是一个100%的空白板,根据您喜欢的架构风格自由发挥。 - Norguard
2
我意识到这是一个空白的板子,可以按照我的意愿进行操作。我的问题是想问一下在结构方面是否有任何新兴标准。我不是在问“JSON是什么以及如何使用它”,而是在问“我知道如何使用JSON来返回/构造任何我想要的东西,但我想知道是否有任何正在被使用或变得流行的标准结构。”如果我的问题表述不清楚,我很抱歉。无论如何,感谢您的回答。 - FtDRbwLXw6

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接