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

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

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

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

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

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

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

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

上查找 angular 表单验证

至于服务器端验证时的HTTP状态码,在不同的场景下应该响应什么样的状态码并没有固定的标准。如果您是唯一使用 API 的人,那么您可以随心所欲地做任何想做的事。

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

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

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

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

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

当一切顺利时

200范围内的状态码表示请求成功。

当服务器成功returns你使用GET后的资源200 OK。消息是请求的内容。

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

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

当事情不顺利时

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

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

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

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

401 Unauthorized403 Forbidden 用于身份验证目的(登录等)。

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

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 完全是 public 并且每个人都可以使用它。如果你这样做并且不假设客户端是什么或谁,那么后端将与前端松散耦合,这就是你想要的。

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