关于哪些信息可以包含在错误响应中以及为什么它可能有用的一些建议
互联网上有很多关于REST错误响应的博客和讨论以及可能是“最佳响应结构”的内容,但仅在几篇文章中我才看到这些神奇的词汇:
“这取决于你在做什么。”
这句话应该是主要的答案,应该产生一些关于设计的想法,并提出REST服务的使用案例的问题。您可以调查来自Facebook,Google,Twitter等公司的回复,但我完全可以确保您仍然可以根据您在特定时间和您的体系结构的实际需求创建自己的响应结构。以下是有关如何有效创建或至少对高级用法提出一些想法的一些提示。
我们假设在以下请求中:
curl -X POST \
https://localhost/rest/api/v1/register \
-d '{
"username": "admin",
"password": "password123",
"confirmPassword": "password123",
"email": "[4302aaf2-135b-47c2-b521-8b567ba1b23f@example.com](mailto:4302aaf2-135b-47c2-b521-8b567ba1b23f@example.com)",
}'
收到以下错误响应:
{
"error": {
"code": "alreadyExists",
"detail": "account already exists"
}
}
这是一个简单的错误响应,非常适合处理业务案例错误。为什么?因为商业错误往往是单身人士。它允许API客户端中继错误代码并处理客户端的任何逻辑。这种响应结构的实现也很简单。
缺点是该结构不适合请求验证,因为它不能包含有关多个错误的信息。我们可以通过向数组添加错误来快速解决此问题。
在进行其他更改后,我们假设在以下请求中:
curl -X POST \
[https://localhost/rest/api/v1/register](https://localhost/rest/api/v1/register) \
-d '{
"x-username": "user-43a46ff3-784f-440f-b0e5-8291a4a8403c",
"x-password": "password123",
"confirmPassword": "password123",
"email": "[4302aaf2-135b-47c2-b521-8b567ba1b23f@example.com](mailto:4302aaf2-135b-47c2-b521-8b567ba1b23f@example.com)",
}'
收到以下错误响应:
{
"errors": [
{
"code": "missingUsername",
"detail": "username field is missing"
},
{
"code": "missingPassword",
"detail": "password field is missing"
}
]
}
现在我们在响应中有多个错误,这些错误适用于请求验证方案。对于小型服务,这将是一个完美的选择。但是,如果在更大规模的应用程序中使用这种方法,那么您可能会遇到麻烦。
根据提供的示例,每个验证错误现在应该有自己的错误代码。想象一下你必须检查的情况required
,min
max,
等等。每个检查都有自己的错误代码,以了解请求的错误。你能想象会是什么样的地狱?
我们可以通过在错误结构中包含一些额外的字段来避免这种情况
现在是最后一次,我们假设在以下请求中:
curl -X POST \
[https://localhost/rest/api/v1/register](https://localhost/rest/api/v1/register) \
-d '{
"username": "a",
"password": "password123",
"confirmPassword": "123456789",
"email": "1b3ee58f-0092-4569-893a-8a648f697b77",
}'
收到以下错误响应:
{
"errors": [
{
"placement": "field",
"title": "value too short",
"detail": "field username must be at least 4 symbols",
"location": "username",
"field": "username",
"code": "validation.min",
"expression": "min",
"argument": "4",
"traceid": "74681b27-b1ea-454d-9847-d27059e19119",
"stacktraces": [
{
"file": "model/response.go",
"function": "model.(*ErrorMessage).LogStacktraceWithErr",
"linenumber": 22,
"realerror": null
},
{
"file": "helpers/validator.go",
"function": "helpers.Validator",
"linenumber": 58,
"realerror": null
},
{
"file": "handlers/registration.go",
"function": "handlers.RegistrationHandler",
"linenumber": 61,
"realerror": null
}
]
},
{
"placement": "field",
"title": "must be equal",
"detail": "field password is not equal to field confirmpassword",
"location": "password",
"field": "password",
"code": "validation.equal",
"expression": "equal",
"argument": "confirmpassword",
"traceid": "e017ecb2-d72f-4f79-889f-6c42126970a8",
"stacktraces": [
{
"file": "model/response.go",
"function": "model.(*ErrorMessage).LogStacktraceWithErr",
"linenumber": 22,
"realerror": null
},
{
"file": "helpers/validator.go",
"function": "helpers.Validator",
"linenumber": 58,
"realerror": null
},
{
"file": "handlers/registration.go",
"function": "handlers.RegistrationHandler",
"linenumber": 61,
"realerror": null
}
]
},
{
"placement": "field",
"title": "incorrect email format",
"detail": "field email has incorrect value",
"location": "email",
"field": "email",
"code": "validation.email",
"expression": null,
"argument": null,
"traceid": "cecda6b8-7ce8-4054-8c06-9382320afd78",
"stacktraces": [
{
"file": "model/response.go",
"function": "model.(*ErrorMessage).LogStacktraceWithErr",
"linenumber": 22,
"realerror": null
},
{
"file": "helpers/validator.go",
"function": "helpers.Validator",
"linenumber": 58,
"realerror": null
},
{
"file": "handlers/registration.go",
"function": "handlers.RegistrationHandler",
"linenumber": 61,
"realerror": null
}
]
}
]
}
是的,它变得非常大!但是,让我们检查那里发生了什么,提供了哪些信息以及它可以提供哪些帮助。
注意:我故意添加*null*
以便在视觉上理解这一点,但如果不包含任何信息,最佳做法是省略字段。
Placement
如果API客户端应用程序倾向于向最终用户显示这些错误,那么建议错误放置是明智的。几个例子:
- general - 显示以上形式的一般错误;
- field - 字段右侧的字段/显示错误 ;
- snackbar(alert) - 在特定前端组件中显示错误。
标题 - 简短的错误摘要。
详细信息 - 错误的详细说明,在验证错误的情况下将动态生成。现场还可以包含可用的附加信息argument
或expression
领域。
Location
如果请求很长并且包含多个子结构,其中相同的字段名称多次出现,那么获得失败字段的位置将是明智的。但是,恕我直言,只有当您在一个视图中包含大量字段并且您不想为每个类别进行多个不同的调用或具有小步进时,才需要这样做。您知道,您的典型企业业务应用程序具有糟糕的用户体验,显示50个字段,另有666个通过填充这50个字段来召唤。
Field
故障Field的关键。这可以很方便,因为您的客户端可以在代码中使用与请求中相同的字段名称,并自动将响应绑定到该特定字段。
Code
在这里,我们要么看到业务错误代码,要么是表示验证组的验证错误代码。API客户端可以为其前端使用类似的验证代码,从而更容易处理错误。
Expression
该字段可以是各种验证规则(一个列表greater
,not equals
,equals
,min
,max
,required
,等等)。该字段应具有一组预定义的表达式。所有未定义的表达式都应归入内部错误,以便以后安全地实现它们。该字段可以与错误代码字段组合,将解析逻辑留在API客户端(不可取)。
参数
该字段应为表达式参数,即数字字符串或其他字段键。
Traceid
此字段应包含UUID或其他类型唯一标识值以跟踪错误。API客户端可以根据此字段的值请求信息,如果出现内部错误,您可以进一步调查。
Stacktraces
这是一个可选字段,可以在开发环境中使用看,,和调用堆栈。这允许在开发API客户端时进行快速调试。这完全是可选的 - 您只需记录此信息,然后在出现错误时转到日志并在那里进行调查。然而,我发现这比查看日志快得多。此外,这是内部信息,如果在生产模式下使用,应该隐藏!*file*``*function*``*line*``*real error*
在这里,我们有一个错误响应,可以通过调整选项满足最低需求。我并不是说这是一个能够满足您需求的完美解决方案,但它可以帮助您在设计错误响应时思考正确的方向。其他想法可以在RFC7807中找到。
顶部的cherry可能是以下响应标题:
- Response-ID - 每个响应应该是唯一的UUID。它可以帮助API客户端识别针对具有相同内容的相同请求收到的不同响应。
- Correlation-ID - 这是一个内部ID,用于跟踪流程中的整个流程。但是,如果您的服务是庞大生态系统的一部分,那么强烈建议始终拥有此值并从流程的开始到结束传递其值。
-
Content-Language -如果您已经实现了多种语言支持,那么您应该返回一种内容语言来指示
title
和detail
字段中使用的语言。 - Content-Type: application/problem+json - 如RFC7807中所建议的,如果出现错误,则具有以下标头是合乎逻辑的。
翻译自:https://medium.com/better-programming/tips-on-rest-api-error-response-structure-aebe726e7f94
网友评论