27

我正在编写一个 REST API,但我偶然发现了一个问题。返回验证错误的最佳方法是什么。

到目前为止,我一直在返回转储到一般错误代码中的错误消息(例如,错误的请求)

{
    "status": 400,
    "error": {
        "code": 1, // General bad request code
        "message": [
                "The Key \"a\" is missing",
                "The Key \"b\" is missing",
                "The Key \"c\" is missing",
                "Incorrect Format for field \"y\""
         ]
    }

)

我对一个好的 API 响应应该是什么样子进行了更多研究,我想到了以下选项:

  1. 在第一个遇到的错误处停止并返回带有特定错误代码的响应

    {
       "status": 400, //Same as the HTTP header returned
       "error" {
            "code": 1, // Specific field validation error code
            "message": "Field \"x\" is missing from the array structure",
            "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
            "more_info" => "www.api.com/help/errors/1"
        }
    )
    
  2. 解析所有请求数据并返回多个字段验证错误。

    {
      "status": 400,
      "error": {
        "code": 1 //General bad Request code
        "message": "Bad Request",
        "developer_message": "Field validation errors."
        "more_info": "www.api.com/help/errors/1",
        "error_details": {
                0: {
                        "code": 2 // Specific field validation error code
                        "message": "Field \"x\" is missing from the array structure",
                        "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
                        "more_info": "www.api.com/help/errors/2"
                    },
    
                1: {
                        "code": 3 // Specific field validation error code
                        "message": "Incorrect Format for field \"y\"",
                        "developer_message": "The field \"y\" must be in the form of \"Y-m-d\"",
                        "more_info": "www.api.com/help/errors/3"
                   }
                       }
          }
      }
    

在我看来,选项 2 是正确的方式(它为开发人员/最终用户提供了更多有用的信息,并且服务器负载可能更低(更少的请求/无需重新验证有效数据/无需计算签名和验证用户)),但我在徘徊什么是最佳实践,以及是否有另一种方法来处理这类问题。

此外,如果我在脚本流程中遇到一个致命错误,我认为选项 1 仍然有效。(不是验证错误)

请注意,代码只是一个简单的数组,因此更容易理解。响应格式将为 JSON 或 XML。

4

6 回答 6

24

让我们看看Facebook 的 Graph API。这受到了重创,并且很可能会产生很多错误。以下是 Facebook 在 API 错误时返回的内容:

 {
   "error": {
     "message": "Message describing the error", 
     "type": "OAuthException", 
     "code": 190,
     "error_subcode": 460,
     "error_user_title": "A title",
     "error_user_msg": "A message"
   }
 }

他们试图使 Graph API 尽可能有用,但他们似乎返回带有代码和子代码 ( Ref ) 的特定错误。每个错误都有自己的代码这一事实意味着更容易搜索所述代码或消息作为调试的起点。这可能就是他们不在官方错误响应中累积错误消息的原因。如果它对 Facebook 来说足够好且方便,那么它对我们来说可能已经足够好了。

示例错误响应:

{
  "error": {
    "message": "(#200) Must have a valid access_token to access this endpoint", 
    "type": "OAuthException", 
    "code": 200
  }
}

"error": {
  "message": "(#604) Your statement is not indexable. The WHERE clause must contain 
   an indexable column. Such columns are marked with * in the tables linked from
   http://developers.facebook.com/docs/reference/fql ", 
  "type": "OAuthException", 
  "code": 604
}

然后是JSend,它“是一个规范,为来自 Web 服务器的 JSON 响应的格式设置了一些规则。” 他们的目标是:

有很多提供 JSON 数据的 Web 服务,每个服务都有自己的格式化响应的方式。此外,为 JavaScript 前端编写的开发人员不断地重新发明轮子来从他们的服务器传输数据。虽然有许多常见的模式来构建这些数据,但在命名或响应类型等方面并没有一致性。此外,这有助于促进后端开发人员和前端设计师之间的快乐和团结,因为每个人都可以期待一种共同的方式来相互交互。

这是一个示例错误消息:

{
    "status" : "fail",
    "data" : { "title" : "A title is required" }
}

看起来 Facebook 和这个试图设定行业标准的团队正在选择你的选择 #1。


赏金问题

为了响应“如果有人去了 #2 并且可能有任何改进?”的赏金请求,Pragmatic RESTful API有一个设计模式指出:

验证错误需要字段细分。这最好通过使用固定的顶级错误代码来验证失败并在附加错误字段中提供详细错误来建模,如下所示:

{
  "code" : 1024,
  "message" : "Validation Failed",
  "errors" : [
    {
      "code" : 5432,
      "field" : "first_name",
      "message" : "First name cannot have fancy characters"
    },
    {
       "code" : 5622,
       "field" : "password",
       "message" : "Password cannot be blank"
    }
  ]
}
于 2015-05-06T14:01:57.733 回答
3

我自己用过#2几次。比#1好吗?我认为这取决于您的 API 的用途。

我喜欢#2,因为它让正在通过一些测试调用测试 API 的开发人员快速了解他在请求中犯的所有错误/错误,因此他立即知道他必须修复哪些错误/错误才能使该请求有效. 如果您一一返回错误(如#1中),您必须不断重试请求并交叉手指希望这次它是有效的。

但正如我所说,#2 对开发人员非常有用,但原因并不真正适用于最终用户。最终用户通常不关心它是如何实现的。软件是执行 1 个返回 5 个错误的请求,还是执行 5 个后续请求,每个请求返回 1 个错误。
只要在客户端处理得好,最终用户就不会注意到差异。如何处理当然很大程度上取决于客户的实际情况

除了加快开发速度之外,#2(在生产中)的另一个好处是它需要更少的请求,这当然会降低服务器负载。


我想知道是否有人去了#2,也许有任何改进,所以我开了一个赏金。

当然还有改进的地方。事实上,正文中有一些数据可以省略。

{
  "status": 400,
  "error": {
    "code": 1 //General bad Request code
    "message": "Bad Request",
    "developer_message": "Field validation errors."
    "more_info": "www.api.com/help/errors/1",
    "error_details": {
            0: {
                    "code": 2 // Specific field validation error code
                    "message": "Field \"x\" is missing from the array structure",
                    "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
                    "more_info": "www.api.com/help/errors/2"
                },

            1: {
                (
                    "code": 3 // Specific field validation error code
                    "message": "Incorrect Format for field \"y\"",
                    "developer_message": "The field \"y\" must be in the form of \"Y-m-d\"",
                    "more_info": "www.api.com/help/errors/3"
                )
            }
)

对于 HTTP 响应,状态码不应该放在正文中,而应该放在标头中。这意味着"status": 400"message": "Bad Request"可以在这里省略。400 应该是响应的状态码,而 400 表示Bad Request。这是一个 HTTP 标准,不必在响应中解释。也"developer_message": "Field validation errors."有点重复,因为特定错误已经包含在每个单独的错误中,所以我们可以省略它。

那离开

{
  "error": {
    "code": 1 //General bad Request code
    "more_info": "www.api.com/help/errors/1",
    "error_details": {
            0: {
                    "code": 2 // Specific field validation error code
                    "message": "Field \"x\" is missing from the array structure",
                    "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
                    "more_info": "www.api.com/help/errors/2"
                },

            1: {
                (
                    "code": 3 // Specific field validation error code
                    "message": "Incorrect Format for field \"y\"",
                    "developer_message": "The field \"y\" must be in the form of \"Y-m-d\"",
                    "more_info": "www.api.com/help/errors/3"
                )
            }
)

"code": 1 //General bad Request code
"more_info": "www.api.com/help/errors/1",

这两条线现在不再有意义了。它们也不是必需的,因为每个错误都有自己的代码和信息链接,所以我们也可以去掉这些行,留下这个

{
  "error": {
    "error_details": {
            0: {
                    "code": 2 // Specific field validation error code
                    "message": "Field \"x\" is missing from the array structure",
                    "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
                    "more_info": "www.api.com/help/errors/2"
                },

            1: {
                (
                    "code": 3 // Specific field validation error code
                    "message": "Incorrect Format for field \"y\"",
                    "developer_message": "The field \"y\" must be in the form of \"Y-m-d\"",
                    "more_info": "www.api.com/help/errors/3"
                )
            }
)

400 状态码已经表明有错误,所以你不必再指出"error": {error details},因为我们已经知道有错误。错误列表可以简单地成为根对象:

[
    {
        "code": 2//Specificfieldvalidationerrorcode
        "message": "Field \"x\" is missing from the array structure",
        "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
        "more_info": "www.api.com/help/errors/2"
    },
    {
        "code": 3//Specificfieldvalidationerrorcode
        "message": "Incorrect Format for field \"y\"",
        "developer_message": "The field \"y\" must be in the form of \"Y-m-d\"",
        "more_info": "www.api.com/help/errors/3"
    }
]

所以现在剩下的只是一个错误列表。

状态代码在响应标头中指定。
详细信息在响应正文中指定。

于 2015-04-30T18:09:33.470 回答
2

我最近研究了一个会在结果中返回多个警告或错误的 Rest API。从您的示例 #2 开始,我将对其进行如下修改:

{
  "status": 400,
  "results" : null,
  "warnings": {
        0: {
                // Build a warning message here, sample text to show concept
                "code": 1 // Specific field validation error code
                "message": "It is no longer neccessary to put .js on the URL"
           }
  }
  "errors": {
        0: {
                "code": 2 // Specific field validation error code
                "message": "Field \"x\" is missing from the array structure"
                "developer_message": "The request structure must contain the following fields {a,b,c{x,y,z}}",
            },
        1: {
                "code": 3 // Specific field validation error code
                "message": "Incorrect Format for field \"y\"",
                "developer_message": "The field \"y\" must be in the form of \"Y-m-d\""
           }
      }
  }

这将使您能够根据需要在响应中提供多个警告或错误的结果。

是的,这在结构上确实有些臃肿,但它也为开发人员提供了一个简单的界面,让他们始终将他们的数据恢复到相同的结构中。

我还将删除以下项目,因为恕我直言,它们应该在 API 文档中(如何使用错误代码查找帮助)而不是每个错误:

"more_info": "www.api.com/help/errors/2"
"more_info": "www.api.com/help/errors/3"

同样,我不确定您是否同时需要消息和 developer_message。它们似乎是多余的,就好像您在调用者未能正确提供数据时尝试从 API 提供用户错误消息一样。

于 2015-05-06T18:00:27.833 回答
0

首先,您将为客户提供 Rest API 方法的文档。因此,客户/开发人员希望为参数提供有效数据。

现在已经说过 #1 是执行 Rest API 的最佳方式。开发人员的责任是最大限度地减少服务器的使用。因此,如果您遇到任何致命错误,请构造带有相应错误代码和错误消息的响应并返回。

此外,我们无法确定在我们遇到的错误之后还有更多错误。因此,解析其余数据没有意义。考虑到最坏的情况,它不会很好地工作。

于 2015-05-06T09:32:00.223 回答
-2

就我个人而言,我会给用户更少的细节,或者将开发人员需要的错误转储到数据库日志表或系统日志中。由于您使用的是 JSON,这在 Apache 服务器上最常见,并且您的代码看起来可能是 php(但您的带有花括号的代码示例可能是源自 PASCAL 的多种语言,例如 C、C# PERL、PHP、夏普)。如果您还不知道如何在 php http://php.net/manual/en/function.syslog.php中使用,这里是如何将自定义错误输出到系统日志。如果您使用带有 JSON 和 CSharp 的罕见配置 IIS,那么也有 .NET 库可以做类似的事情。如果您在发生错误时向您的用户提供太多信息,那么您也为未来的黑客提供了一种探测您网站的方式。

于 2015-04-30T17:29:17.480 回答
-4

API 不适用于人类。因此,您不需要返回详细的错误文本。您甚至可以返回一个错误代码,表示“缺少参数”。只是不要忘记把它记录好。

于 2015-05-07T09:30:09.633 回答