代码改变世界

ASP.NET MVC 中如何用自定义 Handler 来处理来自 AJAX 请求的 HttpRequestValidationException 错误

2013-09-02 14:50  音乐让我说  阅读(1809)  评论(0编辑  收藏  举报

今天我们的项目遇到问题

   为了避免跨站点脚本攻击, 默认我们项目是启用了 validateRequest,这也是 ASP.NET 的默认验证规则。项目发布后,如果 customError 启用了,则会显示我们自己定义的错误页面,如果没有,就会显示具体的错误页面,比如:

如果想忽略这个 ASP.NET 默认的验证规则,则可以在 web.config 中禁用

<system.web>
 <pages validateRequest="false" />
 </system.web>

如果是基于 ASP.NET 4.0 的项目,则还需要配置

<httpRuntime requestValidationMode="2.0"/>

 

自定义请求验证处理规则

由于 ASP.NET 在验证提交的数据时,默认会调用 System.Web.Util.RequestValidator 类的 IsValidRequestString 方法,所以我们可以自定义一个类,继承它,并重写 IsValidRequestString 方法,然后在 web.config 中注册。

<httpRuntime requestValidationType="TestMvc.MvcUI.Extensions.RequestValidatorDisabled"/>

如果 RequestValidatorDisabled 类没有继承 System.Web.Util.RequestValidator 类,则应用程序启动时就会报错:

我们自定义的代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TestMvc.MvcUI.Extensions
{
    public class RequestValidatorDisabled : System.Web.Util.RequestValidator
    {
        protected override bool IsValidRequestString(HttpContext context,
            string value,
            System.Web.Util.RequestValidationSource requestValidationSource,
            string collectionKey,
            out int validationFailureIndex)
        {
            validationFailureIndex = -1;
            return true;
        }
    }
}

 

ASP.NET MVC 默认

这样也符合验证逻辑,想要在自定义错误页面中显示自定义的消息,需要在 MVC 项目中的 Shared/Error.cshtml 中写代码,比如:

@model System.Web.Mvc.HandleErrorInfo

@{
    ViewBag.Title = "错误";
}

<div class="errorDiv clearfix">
    <div class="errorImg"></div>
    <div class="errorMsg">
        <p>抱歉,处理您的请求时出错!</p>
        <p>
        @if (Model.Exception is HttpRequestValidationException)
        {
            <span>抱歉!此次请求不允许提交包含 Html 代码的内容(即:< 或 >)!</span>
        }
        else if (Model.Exception is ArgumentException)
        {
            <span>请检查您输入或提交的的 Url 是否正确,或者提交的数据是否正确!</span>
        }
       else 
       { 
           <span>详细:</span>  @Model.Exception.Message;
       }
        </p>
    </div>
</div>


这样用户就会清楚地知道,具体的消息是什么。但有一个问题,就是来自 AJAX 的请求,页面会无法响应。于是就开始思考解决方案。

在 Global.asax 中解决

 想到 Global.asax 中的 Application_Error 中可以定义异常发生后的处理逻辑,于是就有了如下处理方式:

protected void Application_Error(object sender, EventArgs e)
{
    if (HandleHttpRequestValidationException())
    {
        return;
    }
}

/// <summary>
/// 处理 HttpRequestValidationException
/// </summary>
/// <returns>是否错误已经被处理</returns>
protected bool HandleHttpRequestValidationException()
{
    if (!(Context.Handler is MvcHandler))
    {
        return false;
    }
    Exception ex = Server.GetLastError();
    if (ex is HttpRequestValidationException)
    {
        HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(Request);
        if (httpRequestWrapper.IsAjaxRequest())
        {
            Response.Write(WebCommonHelper.Serialize(new ResultMessage(false, "抱歉!此次请求不允许提交包含 Html 代码的内容(即:< 或 >)!")));
            Response.ContentType = "text/json";
            Server.ClearError();
            return true;
        }
    }
    return false;
}


其中 WebCommonHelper.Seriallize 方法是调用了 .NET 内部的 JavaScriptSerializer 来 JSON 序列化。ResultMessage 是我们大部分用来响应 AJAX 请求的模型。

 

Javascript 验证

 <script type="text/javascript">
     function validate()
     {
         var text = document.getElementById("TextBox1").value;
         var testArray = text.split('');

         var flag = 0;
         for (var a in testArray)
         {
             if (testArray[a] == '<' && flag == 0)
             {
                 flag = 1;
             }
             if (testArray[a] == '>' && flag == 1)
             {
                 flag = 2;
                 break;
             }
         }
         if (flag == 2)
         {
             alert("不能包含 <html> 标签!");
             return false;
         }
         return true;
     }
  </script>

 

 

谢谢浏览!