我曾經通過自定義模型綁定器和投擲周圍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);
}
}
}
基本上,在捕獲異常,請嘗試手動重新綁定模型,準備抓潛在HttpRequestValidationException
誤差爲每個屬性。如果有人被抓到,請根據我的具體信息填充ModelStateDictionary。
我真的希望這個框架能夠更容易地抓住這個異常,並且2)優雅地處理它,而不是cra住整個牀。