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

JSON的关键在于它是完全动态和灵活的。你可以随心所欲地使用它,因为它只是一组序列化的JavaScript对象和数组,植根于一个节点。

rootnode的类型取决于您,它包含的内容取决于您、是否随响应一起发送元数据取决于您以及是否将mime类型设置为application/json或将其保留为text/plain取决于您(只要您知道如何处理边缘情况)。

构建您喜欢的轻量级模式。就我个人而言,我发现分析跟踪、mp3/ogg服务、图片库服务、在线游戏的短信和网络包、博客帖子和博客评论在发送内容、接收内容以及如何消费方面都有着非常不同的要求。

所以,在完成所有这些工作时,我最不想做的就是让每一个都符合相同的样板标准,该标准基于XML2.0或类似的标准。

也就是说,使用对你有意义且经过深思熟虑的模式有很多值得说的地方。只需阅读一些API响应,注意你喜欢的,批评你不喜欢的,写下这些批评,并理解它们为什么会让你感到不舒服,然后思考如何将你学到的东西应用到你需要的东西上。


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

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

仅使用HTTP状态代码来指示是否存在错误,并尝试将自己限制在标准状态(通常这就足够了)。优点:它是一个独立于api的标准。缺点:关于真实发生的事情的信息较少。使用HTTP Status+json主体(即使是错误)。为错误定义一个统一的结构(例如:代码、消息、原因、类型等),并将其用于错误,如果成功,则返回预期的json响应。优点:仍然是标准的,因为您使用现有的HTTP状态代码,并返回一个描述错误的json(您提供了有关发生的更多信息)。缺点:输出json会因错误或成功而有所不同。忘记http状态(例如:always status 200),始终使用json,并在响应的根位置添加一个布尔responseValid和一个错误对象(代码、消息等),如果是错误,将填充该对象,否则将填充其他字段(成功)。优点:客户端只处理作为json字符串的响应主体,而忽略状态(?)。缺点:标准越低。

这取决于您的选择:)

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

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

编辑:

解决方案2是最难实现的,但它的优点是,您可以很好地处理异常,而不仅仅是业务错误,最初的努力更重要,但从长远来看,您会赢得胜利。解决方案3在服务器端和客户端都很容易实现,但它并不是很好,因为您必须将要返回的对象封装在包含responseValid+错误的响应对象中。


我想事实上的标准还没有真正出现(可能永远不会)。但无论如何,我的看法是:

成功的请求:

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

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

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


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

JSON API-JSON API还包括创建和更新资源,而不仅仅是响应。JSend-简单,可能是您已经在做的事情。OData JSON协议-非常复杂。哈尔-像OData,但目标是像HATEOAS。

还有JSON API描述格式:

大摇大摆JSON模式(swagger使用,但可以单独使用)JSON格式的WADL冲压,冲压HAL,因为理论上HATEOAS是自我描述的。


以下是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框架都是这样的。

理论基础:

JSON应该是一种有效载荷格式,而不是会话协议。冗长的会话式有效负载的整个概念来自XML/SOAP世界,以及导致这些臃肿设计的各种错误选择。在我们意识到所有这些都是一个令人头疼的问题之后,REST/JSON的全部目的就是KISS,并遵守HTTP。我不认为JSend中有任何远程标准,尤其是其中更冗长的部分。XHR将对HTTP响应做出反应,如果您对AJAX使用jQuery(像大多数人一样),您可以使用try/catch和done()/fail()回调来捕获错误。我看不出用JSON封装状态报告有多有用。


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

但是,如果有错误(400系列中的某些错误),我会返回一个格式良好的JSON错误对象。例如,如果客户端向用户发送电子邮件地址和电话号码,其中一个错误(即我无法将其插入基础数据库),我将返回如下内容:

{
  "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对象本身的根中添加一个数字错误代码。但到目前为止,我们还不需要它。


Google JSON指南

成功响应返回数据

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

错误响应返回错误

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

如果您的客户端是JS,则可以使用if(响应中的“error”){}来检查是否存在错误。


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


RFC 7807:HTTP API的问题细节是目前最接近官方标准的内容。


他们对大型软件巨头(谷歌、脸书、推特、亚马逊等)的rest api响应格式没有达成一致,尽管上面的答案中提供了许多链接,一些人已经尝试将响应格式标准化。

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

以下是我对谷歌、推特、亚马逊和互联网上一些帖子的回应格式的看法:

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

交换文件:

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数据标准OASIS OData提出的格式,似乎是最标准的选项,但目前任何标准的采用率似乎都不高。此格式与JSON-RPC规范一致。

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

详细信息在我关于JSON REST API中的错误处理的博客文章中讨论


除了常识之外,没有违法或非法的标准。如果我们像两个人说话一样抽象这一点,那么标准就是他们在最短时间内用最少的语言准确理解对方的最佳方式。在我们的例子中,“最小单词”是优化带宽以提高传输效率,而“准确理解”是提高解析器效率的结构;这最终导致数据越少,结构越常见;以便它可以穿过针孔并且可以通过公共范围(至少最初)解析。

几乎在所建议的每一种情况下,我都会看到“成功”和“错误”场景的单独响应,这对我来说有点模糊。如果这两种情况下的响应不同,那么为什么我们真的需要在那里设置“成功”标志呢?不存在“错误”是否意味着“成功”?“成功”为TRUE且设置了“错误”时,是否可能有响应?或者,如果没有设置“错误”,则“成功”为FALSE?仅仅一个旗帜是不够的?我更希望只有“错误”标志,因为我相信“错误”比“成功”少。

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


对于后面的内容,除了包括HAL、JSend和JSON API的公认答案之外,我还将添加一些其他值得研究的规范:

JSON-LD,它是W3C推荐标准,规定了如何以JSON构建可互操作的Web服务Ion Hypermedia Type for 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错误响应的看法,我发送代码(通过状态)、通用消息和详细信息(如果我想提供特定端点的详细信息,有些是不言自明的,因此不需要详细信息,但它可以是自定义消息,甚至可以是完整的堆栈跟踪,具体取决于用例)。为了成功,数据属性中的格式、代码、消息和任何数据都是类似的。

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 }}, // [] ...
      });