C# 什么';捕获HttpRequestValidationException并将其添加到ModelState的最佳方法是什么?

C# 什么';捕获HttpRequestValidationException并将其添加到ModelState的最佳方法是什么?,c#,asp.net-mvc-3,exception-handling,C#,Asp.net Mvc 3,Exception Handling,关于属性上的decorator[allowtml],甚至方法上的[ValidateInput(false)],捕获HttpRequestValidationException并将其附加到ModelState以在用户端显示为友好错误,而不显示错误页面的最佳方法是什么(在Application\u Error下抛出新页面或使用自定义错误页面 在global.asax中,我有一个陷阱: protected void Application_Error() { // http://romstea

关于属性上的decorator
[allowtml]
,甚至方法上的
[ValidateInput(false)]
,捕获
HttpRequestValidationException
并将其附加到ModelState以在用户端显示为友好错误,而不显示错误页面的最佳方法是什么(在
Application\u Error
下抛出新页面或使用自定义错误页面

global.asax中,我有一个陷阱:

protected void Application_Error()
{
    // http://romsteady.blogspot.dk/2007/06/how-to-catch-httprequestvalidationexcep.html
    // Code that runs when an unhandled error occurs

    System.Exception ex = Server.GetLastError();

    if (ex is System.Web.HttpRequestValidationException)
    { 
        // I got the exception here, I can do plenty now!

        Server.ClearError(); // no need to continue, I know the error
    }
}
如何在不使用任何会话/应用程序变量的情况下(考虑此处的云以及承载用户请求的所有不同服务器),从这里获得模型状态

我在考虑添加到路由,或者
TempData
,但是这里没有这样的功能…可能是
Cookie
,但是seams to hacky

有什么想法吗?


ASP.NET MVC中的错误处理是一个有争议的话题。处理错误有不同的选择。请阅读:


我曾经通过自定义ModelBinder处理过这种情况,并在base.BindModel调用中抛出了一个try/catch。这很难看,但它完成了任务

我重复一遍,这很难看

下面是一个例子:

public class FooModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        Foo model;
        try
        {
            model = (Foo)base.BindModel(controllerContext, bindingContext);
        }
        catch (HttpRequestValidationException)
        {
            // handle here
        }
    }
}
现在,在用错误填充ModelState的勇敢努力中,我有了一个帮助器类,它尽最大努力维护状态。它的使用(和实现)还有很多需要改进的地方(很多管道、魔法字符串的使用、特定类型、异常消息文本的正则表达式等),所以欢迎任何建议。这是最丑陋的部分,imo

用法:

// from above code snippet
catch (HttpRequestValidationException)
{
    // handle any potentially dangerous form values here.  Don't want an exception bubbling up to the user
    // so handle the HttpRequestValidationException by hand here
    // manually populate the model here so that the original values are presented back to the user
    model = new Foo()
    {
        Bar = HandleHttpRequestValidationExceptionHelper.TryAssignment(bindingContext.ModelState, () => bindingContext.ValueProvider.GetValue("Bar").AttemptedValue),
        Baz = HandleHttpRequestValidationExceptionHelper.TryAssignment(bindingContext.ModelState, () => bindingContext.ValueProvider.GetValue("Baz").AttemptedValue)
    };
}

return model;
助手尽最大努力为用户挖掘相关的错误信息,但这真的很糟糕。(注意主题?)

实施:

public static class HandleHttpRequestValidationExceptionHelper
{
    /// <summary>
    /// Use TryAssignment in anticipation of a HttpRequestValidationException; it's used to help return error information to the user
    /// </summary>
    /// <param name="modelStateDictionary">The ModelStateDictionary to add the errors to</param>
    /// <param name="action">The attempted value to assign</param>
    /// <returns>Either the proper value or the errored value read from the HttpRequestValidationException Message property</returns>
    public static string TryAssignment(ModelStateDictionary modelStateDictionary, Func<string> action)
    {
        try
        {
            return action();
        }
        catch (HttpRequestValidationException ex)
        {
            // in effort to better inform the user, try to fish out the offending form field
            var parenthesesMatch = Regex.Match(ex.Message, @"\(([^)]*)\)");
            if (parenthesesMatch.Success)
            {
                var badFormInput = parenthesesMatch.Groups[1].Value.Split('=');
                modelStateDictionary.AddModelError(badFormInput[0], badFormInput[1] + " is not valid.");
                return badFormInput[1].TrimStart('"').TrimEnd('"');
            }
            else
            {
                // if attempt to find the offending field fails, just give a general error
                modelStateDictionary.AddModelError("", "Please enter valid information.");
                return string.Empty;
            }
        }
    }

    /// <summary>
    /// Use TryAssignment in anticipation of a HttpRequestValidationException; it's used to help return error information to the user
    /// </summary>
    /// <typeparam name="T">Type of the value</typeparam>
    /// <param name="modelStateDictionary">The ModelStateDictionary to add the errors to</param>
    /// <param name="action">The attempted value to assign</param>
    /// <returns>Either the proper value or default(T)</returns>
    public static T TryAssignment<T>(ModelStateDictionary modelState, Func<T> action)
    {
        try
        {
            return action();
        }
        catch (HttpRequestValidationException ex)
        {
            // in effort to better inform the user, try to fish out the offending form field
            var parenthesesMatch = Regex.Match(ex.Message, @"\(([^)]*)\)");
            if (parenthesesMatch.Success)
            {
                var badFormInput = parenthesesMatch.Groups[1].Value.Split('=');
                modelState.AddModelError(badFormInput[0], badFormInput[1] + " is not valid.");
                // can't really cast a string to an unknown type T.  safer to just return default(T)
            }
            else
            {
                // if attempt to find the offending field fails, just give a general error
                modelState.AddModelError("", "Please enter valid information.");
            }
            return default(T);
        }
    }
}
公共静态类HandleHttpRequestValidationExceptionHelper
{
/// 
///使用TryAssignment预测HttpRequestValidationException;它用于帮助向用户返回错误信息
/// 
///要向其中添加错误的ModelStateDictionary
///试图分配的值
///从HttpRequestValidationException消息属性读取的正确值或错误值
公共静态字符串TryAssignment(ModelStateDictionary ModelStateDictionary,Func操作)
{
尝试
{
返回动作();
}
捕获(HttpRequestValidationException-ex)
{
//为了更好地通知用户,请尝试找出有问题的表单字段
var括号匹配=Regex.Match(例如,Message,@“\([^)]*)\”;
if(括号匹配成功)
{
var badFormInput=括号匹配.Groups[1].Value.Split('=');
modelStateDictionary.AddModelError(badFormInput[0],badFormInput[1]+“无效”);
返回badFormInput[1].TrimStart(“”).TrimEnd(“”);
}
其他的
{
//如果试图查找有问题的字段失败,只需给出一个常规错误
modelStateDictionary.AddModelError(“,”请输入有效信息“);
返回字符串。空;
}
}
}
/// 
///使用TryAssignment预测HttpRequestValidationException;它用于帮助向用户返回错误信息
/// 
///值的类型
///要向其中添加错误的ModelStateDictionary
///试图分配的值
///正确值或默认值(T)
公共静态T TryAssignment(ModelStateDictionary modelState,Func action)
{
尝试
{
返回动作();
}
捕获(HttpRequestValidationException-ex)
{
//为了更好地通知用户,请尝试找出有问题的表单字段
var括号匹配=Regex.Match(例如,Message,@“\([^)]*)\”;
if(括号匹配成功)
{
var badFormInput=括号匹配.Groups[1].Value.Split('=');
AddModelError(badFormInput[0],badFormInput[1]+“无效”);
//无法将字符串强制转换为未知类型t。只返回默认值(t)更安全
}
其他的
{
//如果试图查找有问题的字段失败,只需给出一个常规错误
AddModelError(“,”请输入有效信息“);
}
返回默认值(T);
}
}
}
基本上,捕获异常后,尝试手动重新绑定模型,准备捕获每个属性的潜在
HttpRequestValidationException
错误。如果捕获到一条消息,请在ModelStateDictionary中填充尽可能具体的消息

我真的希望这个框架能够更容易地1)捕捉到这个异常,2)优雅地处理它,而不是拖累整个床