JSON API响应格式有什么标准吗?

是否存在从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!"}}
710055 次浏览

JSON的重点是它完全动态和灵活。将其弯曲到您想要的任何突发奇想,因为它只是一组序列化的JavaScript对象和数组,植根于单个节点。

根节点的类型由您决定,它包含什么由您决定,是否发送元数据以及响应由您决定,是否将mime类型设置为application/json或将其保留为text/plain由您决定(只要您知道如何处理边缘情况)。

构建您喜欢的轻量级架构。
就个人而言,我发现分析跟踪和mp3/ogg服务和图像库服务和文本消息和网络数据包的在线游戏,和博客文章和博客评论所有非常不同的需求在发送和接收什么以及如何消费。

因此,在执行所有这些操作时,我最不希望的是尝试使每个都符合相同的样板标准,该标准基于XML2.0或其他标准。

也就是说,使用模式有很多话要说,这些模式对有意义并且经过深思熟虑。
只要阅读一些API回复,记下你喜欢的东西,批评你不喜欢的东西,把这些批评写下来,理解为什么它们会以错误的方式摩擦你,然后思考如何将你学到的东西应用到你需要的东西上。

假设您的问题是关于REST Web服务设计,更准确地说是关于成功/错误。

我认为有三种不同类型的设计。

  1. 使用只有HTTP状态码表示是否有错误,并尝试将自己限制为标准错误(通常应该足够了)。

    • 优点:它是一个独立于API的标准。
    • 缺点:关于真正发生的事情的信息较少。
  2. 使用HTTP状态+json正文(即使是错误)。为错误定义统一的结构(例如:代码、消息、原因、类型等)并将其用于错误,如果成功,则返回预期的json响应。

    • 优点:仍然是标准的,因为您使用现有的HTTP状态代码并返回一个描述错误的json(您提供有关发生了什么的更多信息)。
    • 缺点:输出json会因错误或成功而异。
  3. 忘记超文本传输协议状态(例如:始终状态200),始终使用json并在响应的根目录添加一个布尔型响应Valid和一个错误对象(代码、消息等),如果它是错误,则将填充该对象,否则将填充其他字段(成功)。

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

    • 缺点:标准较低。

由你来选择:)

根据API的不同,我会选择2或3(对于json rest apis,我更喜欢2)。我在设计REST API时经历的另一件事是为每个资源(url)留档的重要性:参数、主体、响应、标头等+示例。

我还建议您使用jersey(jax-rs实现)+genson(java/json数据库绑定库)。你只需要在你的类路径中删除genson+jersey,就会自动支持json。

编辑:

  • 解决方案2是最难实现的,但优点是您可以很好地处理异常,而不仅仅是业务错误,最初的努力更重要,但您可以长期获胜。

  • 解决方案3在服务器端和客户端都很容易实现,但它不是那么好,因为你必须将你想要返回的对象封装在一个还包含响应值+错误的响应对象中。

我想事实上的标准还没有真正出现(可能永远不会)。但不管怎样,这是我的看法:

请求成功:

{"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"}

优势:成功和错误案例中相同的顶级元素

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

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

  1. JSONAPI-JSON API还包括创建和更新资源,而不仅仅是响应。
  2. 发送文件-简单,可能是你已经在做的事情。
  3. #30340;非常复杂。
  4. HAL-像OData一样,但目标是成为HATEOAS

也有JSON API描述格式:

  • Swagger
    • JSON架构(由swagger使用,但您可以单独使用它)
  • JSON中的WADL
  • RAML
  • 因为理论上0是自我描述的。

以下是instagram使用的json格式

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

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

我更喜欢简洁的响应(当请求 /articles我想要一个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。我不认为在J发送中有任何远程标准,尤其是其中更冗长的。XHR将对HTTP响应做出反应,如果你在AJAX中使用jQuery(像大多数人一样),你可以使用try/catchdone()/fail()回调来捕获错误。我看不出用JSON封装状态报告比这更有用。

为了它的价值,我做了不同的事情。成功的调用只有JSON对象。我不需要一个更高级别的JSON对象,其中包含一个指示true的成功字段和一个包含JSON对象的有效负载字段。我只是返回适当的JSON对象,其中包含200或200范围内适合标头中HTTP状态的任何内容。

但是,如果有错误(400家族中的某些东西),我将返回一个格式良好的JSON错误对象。例如,如果客户端正在发布具有电子邮件地址和电话号码的用户,并且其中一个格式不合法(即我无法将其插入到我的底层数据库中),我将返回如下内容:

{"description" : "Validation Failed""errors" : [ {"field" : "phoneNumber","message" : "Invalid phone number."} ],}

这里重要的一点是“field”属性必须与无法验证的JSON字段完全匹配。这允许客户端确切地知道他们的请求出了什么问题。此外,“消息”位于请求的区域设置中。如果“emailAddress”和“phoneNumber”都无效,那么“错误”数组将包含两者的条目。409(冲突)JSON响应正文可能如下所示:

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

有了HTTP状态代码和这个JSON,客户端就拥有了以确定性方式响应错误所需的一切,并且它不会创建一个新的错误标准来尝试完成替换HTTP状态代码。请注意,这些仅发生在400个错误的范围内。对于200个范围内的任何错误,我都可以返回任何合适的东西。对我来说,它通常是一个类似HAL的JSON对象,但这在这里并不重要。

我想添加的一件事是在“错误”数组条目或JSON对象本身的根目录中添加一个数字错误代码。但到目前为止,我们还不需要它。

Google JSON指南

成功响应返回data

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

错误响应返回error

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

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

JSON-RPC 2.0定义了标准的请求和响应格式,是使用REST API后的新鲜空气。

RFC 7807:HTTP API的问题详细信息是目前我们拥有的最接近官方标准的东西。

他们对大型软件巨头——谷歌、Facebook、Twitter、亚马逊和其他公司的其余api响应格式没有达成一致,尽管上面的答案中提供了许多链接,有些人试图标准化响应格式。

由于API的需求可能不同,很难让每个人都参与并同意某种格式。如果您有数百万用户使用您的API,您为什么要更改响应格式?

以下是我对受谷歌、Twitter、亚马逊和互联网上一些帖子启发的回复格式的看法:

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

Swagger文件:

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

移动开发人员可以轻松理解的Web API的最佳响应。

这是对“成功”的回应

{"code":"1","msg":"Successfull Transaction","value":"","data":{"EmployeeName":"Admin","EmployeeID":1}}

这是“错误”响应

{"code": "4","msg": "Invalid Username and Password","value": "","data": {}}

建议的基本框架看起来不错,但是定义的错误对象太有限了。人们通常不能使用单个值来表达问题,而是使用需要一系列的问题和原因

我做了一些研究,发现返回错误(异常)的最常见格式是这种形式的结构:

{"success": false,"error": {"code": "400","message": "main error message here","target": "approx what the error came from","details": [{"code": "23-098a","message": "Disk drive has frozen up again.  It needs to be replaced","target": "not sure what the target is"}],"innererror": {"trace": [ ... ],"context": [ ... ]}}}

这是OASIS数据标准OASISOData提出的格式,似乎是最标准的选择,但是目前似乎没有任何标准的高采用率。这种格式与JSON-RPC规范一致。

您可以在以下位置找到实现此功能的完整开源库:Mendocino JSON实用程序。该库支持JSON对象以及异常。

细节在我的博客文章中讨论JSON REST API中的错误处理

除了常识之外,没有任何其他违法或违法的标准。如果把这个问题抽象成两个人在说话,那么这个标准就是他们能用最少的词语,在最短的时间内准确理解对方的最好方式。在我们这里,“最小词语”是优化传输效率的带宽,而“准确理解”是提高解析器效率的结构;最终结果是数据越少,结构越通用,所以它可以通过针孔,通过一个通用的作用域被解析(至少在最初是这样)。

几乎在每个建议的case中,我都看到“成功”和“错误”场景的响应是分开的,这对我来说有点模棱两可。如果这两种情况下的响应不同,那么为什么我们真的需要在那里放一个“成功”标志呢?没有“错误”是否不明显是“成功”?是否有可能在设置了“错误”的情况下有一个“成功”为TRUE的响应?或者在没有设置“错误”的情况下“成功”为FALSE?只有一个标志是不够的?我更愿意只使用“错误”标志,因为我相信“错误”比“成功”更少。

此外,我们真的应该让“错误”成为标志吗?如果我想响应多个验证错误怎么办?所以,我发现有一个“错误”节点,每个错误都作为该节点的子节点更有效;其中一个空(计数为零)“错误”节点将表示“成功”。

对于那些稍后到来的人,除了包括HAL、J发送和JSON API的公认答案之外,我还会添加一些其他值得研究的规范:

  • JSON-LD,这是W3C建议,指定了如何在JSON中构建可互操作的Web服务
  • 离子超媒体类型对于REST,它声称自己是“一个简单直观的基于JSON的REST超媒体类型”

我曾经遵循这个标准,在客户端层上非常好,简单,干净。

通常,HTTP状态为200,这是我在顶部使用的标准检查。我通常使用以下JSON

我还使用API的模板

dynamic response;
try {// query and what not.response.payload = new {data = new {pagination = new Pagination(),customer = new Customer(),notifications = 5}}
// again something here if we get here success has to be true// I follow an exit first strategy, instead of building a pyramid// of doom.response.success = true;}catch(Exception exception){response.success = false;response.message = exception.GetStackTrace();_logger.Fatal(exception, this.GetFacadeName())}
return response;
{"success": boolean,"message": "some message","payload": {"data" : []"message": ""... // put whatever you want to here.}}

在客户端层,我将使用以下内容:

if(response.code != 200) {// woops something went wrong.return;}
if(!response.success){console.debug ( response.message );return;}
// if we are here then success has to be true.if(response.payload) {....}

请注意我是如何提前休息避免厄运金字塔的。

我将此结构用于REST API:

{"success": false,"response": {"data": [],"pagination": {}},"errors": [{"code": 500,"message": "server 500 Error"}]}

有点晚了,但这是我对HTTP错误响应的看法,我发送代码(通过状态)、通用消息和详细信息(如果我想提供特定端点的详细信息,有些是不言自明的,所以不需要详细信息,但它可以是自定义消息,甚至是完整的堆栈跟踪,这取决于用例)。为了成功,它是类似的格式、代码、消息和data属性中的任何数据。

ExpressJS响应示例:

// Error
res.status(422).json({error: {message: 'missing parameters',details: `missing ${missingParam}`,}});        
// or        
res.status(422).json({error: {message: 'missing parameters',details: 'expected: {prop1, prop2, prop3',}});
// Success
res.status(200).json({message: 'password updated',data: {member: { username }}, // [] ...});