如何应对服务器端的不同验证错误?

How to respond to different validation errors at the server side?

提问人:Taimoor Neeshat 提问时间:10/17/2017 最后编辑:Taimoor Neeshat 更新时间:10/28/2017 访问量:5172

问:

我是第一次开发一个网站,我选择使用 mongodb、node、express 和 angular,即 MEAN Stack。我正在研究的前几个功能之一是一个简单的表单,当前手头的任务是表单验证。我已经对如何验证表单提交进行了大量研究,并了解有必要同时实现客户端和服务器端验证。客户端验证以获得更好的用户体验,服务器端验证出于各种原因,但主要是安全性。

但是,我面临的问题是,当表单数据提交到我的REST API时,我应该如何应对可能出现的不同错误。我想知道可能出现的每种情况以及如何处理每种情况,例如在哪些状态代码中进行响应以及我是否应该提供特定的错误消息?

编辑:我将添加一些更具体的问题,但请不要将您的答案限制在这些问题上。

1) 我可以假设没有客户要求字段的请求实际上不是来自我的客户,并且很可能是对手,所以我应该只发送“错误请求(400)”而不提供任何错误消息吗?
2) 对于验证错误,我是否应该使用 200 以外的任何其他状态代码,例如缺少必填字段或格式不正确的字段?我之所以问这个问题,是因为当我发送 4XX 响应时,我的浏览器控制台中会生成错误,当网站按预期运行时,这似乎是不对的。

我希望我能把自己说清楚。如果没有,请告诉我,以便我尝试更具体。

表单 验证 错误处理 服务器端 http-status-codes

评论

0赞 ricky 10/17/2017
到目前为止已经尝试了什么。不要只写长篇文章,而是提供一些你尝试过的代码
0赞 Taimoor Neeshat 10/17/2017
我的问题不是针对任何代码的,而是关于如何处理服务器端验证错误的更通用的方法。@ricky

答:

6赞 Mika Sundland 10/18/2017 #1

当您声明需要客户端和服务器端验证时,您是正确的。您希望如何呈现客户端验证错误完全取决于您。禁用提交按钮、将无效输入框的边框设置为红色等是典型的。HTML5 已经为某些东西(数字、日期、长度)提供了内置的验证方法。如果这还不够,您可以在 Google 上查找 angular 表单验证

至于服务器端验证期间的 HTTP 状态代码,对于在不同场景中应使用哪些状态代码进行响应,没有设定标准。如果你是唯一一个使用API的人,那么你可以相当自由地做任何你想做的事情。

但是,如果您想遵循 API 的 REST 原则,则有一个事实上的标准。如果我们查看 Lequoa 提供的链接,我们会发现以下类别:

  • 1xx 信息
  • 2xx 成功
  • 3xx 重定向
  • 4xx 客户端错误
  • 5xx 服务器错误

目前,我们并不对以 1 和 3 开头的代码感兴趣。事实上,我们唯一关心的代码是带星号的代码:

  • 200 正常
  • 201 创建
  • 204 无内容
  • 400 错误请求
  • 401 未经授权
  • 403 禁止访问
  • 404 未找到
  • 409冲突
  • 500 内部服务器错误

以下是通常如何使用这些的一些经验法则:

当一切顺利时

200 范围内的状态代码用于成功的请求。

当服务器在 GET 后成功返回资源时,请使用 200 OK。消息是请求的内容。

当服务器在 POST 后成功创建资源时,请使用 201 CREATED。消息通常是创建的内容(例如新的博客文章)。

每当您删除、修补或放置某些内容时,您都可以使用 204 No Content。您永远不会提供包含此状态代码的消息。

当事情不顺利时

当客户端发送错误请求时,使用 400 范围。

400 错误请求用于未通过验证的请求、缺少字段的请求等。发送的消息由您决定,但它通常包装在消息字段中,如下所示:

response.status = HttpStatus.BAD_REQUEST;
response.message = { 'message': 'Illegal schoolID' };
res.status(response.status).json(response.message);

这是我用于提供非法学校 ID 的 GET 请求的消息。当然,如果要在客户端美化错误消息,可以提供实际的验证结果对象。

401 未经授权403 禁止用于身份验证目的(登录等)。

当客户端为不存在的资源发送 GET 时,使用 404 Not Found

422 Unprocessable Entity 也可用于输入语法正确但语义错误时的验证错误。

发生实际错误时

如果抛出错误或在回调中收到错误,则可以使用 500 内部服务器错误。例如

Model
  .findById(modelId)
  .exec(function (err, models) {
      if (err) {
         res.status(500).json(err);
         return;
      }
      ...

这不用于验证错误,而是用于永远不应该发生的错误。毕竟,验证错误并不少见。内部服务器错误是服务器端代码中的错误,或者服务器着火或类似的东西。

处理客户端代码中的错误

当浏览器返回 4** 或 5** 状态代码时,它必须处理该问题。它们将被打印到控制台,这很好,你不应该使用 2** 代码让它消失。浏览器必须读取错误消息并以用户友好的方式将其显示给用户。如果服务器响应提供的消息是用户友好的,那么它可以直接显示,否则你必须编写如下内容(AngularJS):

schoolDataFactory.patchUpdateSchool(vm.school._id, newSchoolData).then(function (res) {
  if (res.status === 204) {
    vm.errorMessage = ''
    vm.message = 'School info was updated.'
  } else {
    console.log('The server should send 204 No Content on successful PATCH, so this shouldn't happen.')
  }
}).catch(function (error) {

  vm.message = ''

  if (error.status === 400 && error.data.message === 'ValidationError: schoolName') {
    vm.errorMessage = 'Fix the school name'
  } else if (error.status === 400 && error.data.message === 'ValidationError: schoolAddress') {
    vm.errorMessage = 'Fix the school address'
  } else if (error.status === 500) {
    vm.errorMessage = 'Something is wrong with the server.'
  }
})

此假设示例使用新的学校数据向服务器发送 PATCH 请求。如果 PATCH 成功,浏览器将收到状态代码 204 并在 then() 中运行回调。如果它收到 4** 或 5**,它会在 catch() 中运行回调,这将处理错误。vm.errorMessage 和 vm.message 绑定在视图中并显示给用户。

关键是,如果来自服务器的响应很丑陋,您可能必须转换客户端代码中的响应消息。

更具体地说明问题 1

我认为设计它的一个好方法是假装你只在编写服务器端代码和 API。假装别人在做前端,然后想象他们想看到什么。或者想象一下,你的 API 是完全公开的,每个人都可以使用它。如果你这样做,并且不对客户端是什么或谁做任何假设,那么后端将与前端松散地耦合,这就是你想要的。

你唯一可以假设的是,有人会试图用一个名为Postman的应用程序毁掉你的一天。