3

[AllowHtml]关于属性甚至方法上的装饰器,捕获并将其附加到 ModelState 以在用户端显示为友好错误而不显示错误页面[ValidateInput(false)]的最佳方法是什么(或者在下面抛出一个新页面或使用自定义错误页面。HttpRequestValidationExceptionApplication_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但接缝到hacky......

有任何想法吗?

4

2 回答 2

1

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

于 2012-06-27T11:26:57.077 回答
1

我曾经通过自定义 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;

helper 尽最大努力为用户挖掘出相关的错误信息,但它真的很糟糕。(注意主题?)

执行:

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);
        }
    }
}

基本上,在捕获异常时,尝试手动重新绑定模型,准备捕获HttpRequestValidationException每个属性的潜在错误。如果一个被抓到,用我能得到的尽可能具体的消息相应地填充 ModelStateDictionary。

我真的希望该框架能够更容易地 1) 捕获此异常并 2) 优雅地处理它而不是弄乱整个床。

于 2013-09-05T19:37:52.570 回答