【问题标题】:How to display server side validation error message?如何显示服务器端验证错误消息?
【发布时间】:2017-03-03 04:33:59
【问题描述】:

我们正在构建一个 Web 应用程序,但在前端显示服务器端验证错误消息时遇到了一些问题。

为了提供一些上下文,我们正在运行一个单页应用程序,其中前端在 react.js 中实现,后端 api 在 asp.net 中实现。我们的应用程序中有许多表单要求用户填写,我们计划在客户端和服务器端都实现表单验证。

我们现在的工作流程是先进行客户端验证,然后将数据传递给后端 api。然后后端将首先执行与前端相同的验证,以确保请求不是来自绕过客户端验证规则的恶意用户。对于来自正常通道的请求,它不应该在这一步失败,但是,如果它失败了,它只是意味着我们正在与黑客打交道。

但是,服务器端验证的内容远不止这些。有客户端不知道的信息,例如刚刚输入的银行账户用户是否存在于我们的数据库中? (我们没有专门的 api 来验证这一点,所以我们必须在用户将整个表单提交到服务器端后在表单级别执行此操作)。

因此,总而言之,服务器端验证规则有两个部分:与前端共享的基本验证规则,以及需要数据库访问的高级验证规则,该规则仅在服务器端可用。

现在的问题是,在用户未通过这两种类型的验证后,我们如何通知用户?

以下是我们目前提出的两个建议

解决方案 1:使用 HTTP 400 - Bad Request 表示业务失败

我们以相同的方式处理这两种服务器端验证,当任何请求未通过任何验证时,我们会发回 HTTP 400,其中包含一个包含详细错误消息的 json 对象,说明失败的原因。示例响应将是

HTTP 400 Bad Request
{
      "bsb_name" :"bsb_name has special characters",          
      "bsb_number":"bsb number doesn't exist",
      "amount":"amount is required"
 }

现在的问题是,我们不是唯一可以返回 HTTP 400 的一方。由于我们使用的是 asp.net 框架加上 OWIN 框架,框架本身在某些情况下也可以返回 HTTP 400,而 HTTP 400他们发送的与我们完全不同。这为前端创建了额外的工作来区分 2 种类型的 HTTP 400 并以不同的方式处理它们。

提出了重新考虑我们的响应的建议,我们不发回 JSON 对象,而是发回纯字符串 --- 基于 asp.net 框架/owin 将以纯字符串发回其 HTTP 400 的假设(1)。所以在前端我们没有区分它们并且可以以相同的方式处理它们。此外,为了避免引入魔术分隔符,我们只返回第一个错误消息文本,而不是全部返回。所以一个示例响应可能是

HTTP 400 Bad Request
"Bank account name has special characters" 

我们将把它当作 asp.net 框架返回的 HTTP 400 来处理,比如说,

HTTP 400 Bad Request
Request header too long

因此,在这两种情况下,网站用户在提交表单后都会看到一条错误消息,提示“银行帐户名称包含特殊字符”或“请求标头太长”

解决方案 2:使用业务协议而不是 HTTP 协议

如果请求未通过基本的服务器端验证,这只会发生在黑客身上,服务器将立即抛出异常,最终以 HTTP 500 结束。一旦收到 HTTP 500,客户端将简单地将用户引导至一般错误在没有任何详细信息的情况下出现意外事件的页面——我们尽量避免对黑客友好。

如果请求未通过高级服务器端验证,这可能发生在普通应用程序用户身上,服务器仍将返回 HTTP 200 - Success,但带有不同的 status_code 表示这是一个错误响应。例如

  HTTP 200 Success
  {
       status_code: Err_123
       error_message: "bsb doesn't exist"
  }

我个人想使用解决方案 2,但被告知我们不应该使用 HTTP 200 来处理验证错误。由于 HTTP 200 代表 OK,因此在服务器端验证失败的情况下使用 HTTP 200 没有意义。

我的理解是,HTTP 状态码是为 HTTP 协议设计的,应该和你的业务逻辑无关。 HTTP 200 仅表示客户端已将请求发送到服务器,并且服务器已成功处理并返回响应。响应是否意味着交易已被批准,或交易已被拒绝,或者您提交的 BSB_Number 不存在,它根本不在乎。它不应该关心。

所以在交易确实被拒绝的情况下,或者表单有一些只能在服务器端检测到的无效输入,我们仍然应该响应 HTTP 200,但分配一个不同的 status_code,相当于 HTTP 400, 但是,它在我们的业务协议中。如此有效,我们应该将我们的业务协议与 http 协议区分开来,我们不能使用 HTTP FAILURE 来代表 BUSINESS FAILURE(2)。

以上是我对此的所有想法,我欢迎所有的cmets,关于这个话题的进一步讨论。

干杯, 博

  • (1):假设是错误的,因为我最近发现 IIS 在某些情况下可以返回 HTTP 400。发生这种情况时,它会返回一个 HTML 页面。

  • (2):类比可以是

**IP -- TCP -- HTTP -- BUSINESS** If TCP layer fails, IP shouldn't be concerned; If HTTP layer fails, TCP shouldn't be concerned; If BUSINESS layer fails, HTTP shouldn't be concerned;

“关注点分离”不是编程中的常识吗?看到有人说“如果业务失败,可以返回 HTTP 400”,我有点惊讶

【问题讨论】:

标签: c# asp.net asp.net-mvc validation


【解决方案1】:

在我看来,关注点分离应该是首要的。

解决方案一虽然声明错误源于业务层,但暗示表示存在错误。现在,对于错误的实际来源,现在是模棱两可的。

另一方面,解决方案 2 详细说明 HTTP 事务成功并且业务逻辑以错误响应。

理想情况下(在我看来)服务器端的所有错误都应该被捕获并报告为业务逻辑中的错误,这样只有在用户请求错误或敏感页面时才会引发 HTTP 错误。

这会使系统更优雅地失败,并使其更具可扩展性。 (例如,如果您在其他地方实现您的 API,则不需要在其中包含 HTTP 错误处理,只需显示所述错误)

顺便说一句,这可能更适合 P.SE 而不是 SO。

【讨论】:

  • 这是一个很好的答案,因为它使您的解决方案可移植到不同的协议和设备上。虽然您现在可能总是使用 HTTP,但将来可能不是这样。即使您从不打算从 HTTP 迁移,这也是一个很好的答案,纯粹因为它在 HTTP 异常和业务逻辑异常之间做出了明显的区分。
【解决方案2】:

我的解决方案类似于解决方案 2。我将实体定义为所有 RESTful 请求的响应。

class Entity<T>{
    public int code {get;set;}
    public int message {get;set;}
    public T content {get;set;}
}

无论请求成功还是失败,前端都会获取一个实体并检查代码。如果代码为 200(或其他定义),则继续处理内容,否则显示消息并返回。

【讨论】:

    【解决方案3】:

    对于来自正常通道的请求,它不应该在这一步失败,但是,如果它失败了,它只是意味着我们正在与黑客打交道。

    如果您的 API 是公开的,为什么不使用状态码 500,...这是一种欺骗试图访问您的 API 的人的方法。但是,您现在如果这不是带有读取状态消息的内部服务器错误。或you use Status Code 202(请求已被接受处理,但处理尚未完成。请求最终可能会或可能不会被执行,因为在实际进行处理时可能会被拒绝。没有状态返回的设施来自诸如此类的异步操作。)

    如果您使用状态代码 200 或 400,并且每个人都知道是否可以注入...但是,这取决于您,这只是我的建议,就像您需要的意见一样。

    【讨论】:

      【解决方案4】:

      在我看来,您的 400 响应代码是可以的。您实际上需要对应该如何响应验证场景进行建模。例如,BaseResponse.cs 看起来像这样。

      public class BaseResponse<T> where T: object {
        public T Data {get;set;}
        public bool Success {get; set;}
        public IList<Error> Errors {get;set;}
      }
      

      另一方面,您可以如下定义错误类

      public class Error {
         string Type {get;set;} // "validation", "business"
         string Code {get;set;} // "VAL_1234", "BIZ_4321"
         string Message {get;set;} // "Value should be greater than 10"
      }
      

      现在在 react 应用程序的 Javascript 端,您的代码并不那么复杂

      fetch(url)
      .then((response)=> {
        if(response.status === 400) {
           let json = response.json();
           if(json) { // you have validation errors, do something with errors property in json
           }else {  // 400 was returned because of some other reason
           }
        }
      });
      

      结合我上面的内容,一个更复杂的解决方案应该可以工作。示例here

      【讨论】:

      • 你让 json = response.json() 在iis.net/learn/troubleshoot/diagnosing-http-errors/…时会抛出异常
      • 我的代码仅用于说明目的。对于异常的情况,你可以不同的处理。基本上需要检查响应是否有正文。
      • HTTP 400 可能来自您的虚拟主机(即 IIS、apache 或 Nginx),可能来自 Web 框架(asp.net、flusk、rails..),也可能来自您的代码。没有办法知道它来自哪里,因为你不知道 iis、apache、nginx、asp.net、flusk、rails 格式化它们的 HTTP 400。我们需要区分它们的原因是因为我们可以处理返回的错误优雅地通过我们的后端代码,因为我们知道它是什么;但是我不知道如何处理 IIS 返回的 HTT 400,比如说,如果它发生,我们只能让它进入一般错误页面。
      • @BoChen IIS 400 响应正文与您的应用程序设置的不同。在 javascript 中,您可以检查 json 响应以确定其服务器是否已创建或应用程序已创建。
      猜你喜欢
      • 1970-01-01
      • 2020-12-26
      • 1970-01-01
      • 2011-02-26
      • 1970-01-01
      • 1970-01-01
      • 2011-10-21
      • 2015-05-05
      • 1970-01-01
      相关资源
      最近更新 更多