MVC가 모델 바인딩을 어떻게하는지 보았습니다. 솔루션 자체를 생각해 냈습니다. 요청 유효성 검사 내가 추가 한 발생시하여 web.config
에 다음을 제어하는 나를 위해
public abstract class ExtendedController : Controller
{
protected override void Execute(RequestContext requestContext)
{
ActionInvoker = new ExtendedActionInvoker(ModelState);
ValidateRequest = false;
base.Execute(requestContext);
}
}
:
<httpRuntime requestValidationMode="2.0"/>
의 I는 Execute
방법과 같이 재정의하는 사용자 정의 구현과 Controller
클래스를 확장
public class ExtendedActionInvoker : ControllerActionInvoker
{
private ModelStateDictionary _modelState;
private const string _requestValidationErrorKey = "RequestValidationError";
public ExtendedActionInvoker(ModelStateDictionary modelState)
{
_modelState = modelState;
}
protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
{
var action = base.FindAction(controllerContext, controllerDescriptor, actionName);
controllerContext.RequestContext.HttpContext.Request.ValidateInput();
return action;
}
protected override object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
{
try
{
return base.GetParameterValue(controllerContext, parameterDescriptor);
}
catch (HttpRequestValidationException)
{
var fieldName = parameterDescriptor.ParameterName;
_modelState.AddModelError(fieldName, ModelRes.Shared.ValidationRequestErrorMessage);
_modelState.AddModelError(_requestValidationErrorKey, ModelRes.Shared.ValidationRequestErrorMessage);
var parameterType = parameterDescriptor.ParameterType;
if (parameterType.IsPrimitive || parameterType == typeof(string))
{
return GetValueFromInput(parameterDescriptor.ParameterName, parameterType, controllerContext);
}
var complexActionParameter = Activator.CreateInstance(parameterType);
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(complexActionParameter))
{
object propertyValue = GetValueFromInput(descriptor.Name, descriptor.PropertyType, controllerContext);
if (propertyValue != null)
{
descriptor.SetValue(complexActionParameter, propertyValue);
}
}
return complexActionParameter;
}
}
private object GetValueFromInput(string parameterName, Type parameterType, ControllerContext controllerContext)
{
object propertyValue;
controllerContext.RouteData.Values.TryGetValue(parameterName, out propertyValue);
if (propertyValue == null)
{
propertyValue = controllerContext.HttpContext.Request.Params[parameterName];
}
if (propertyValue == null)
return null;
else
return TypeDescriptor.GetConverter(parameterType).ConvertFrom(propertyValue);
}
}
어떤이 : 액션의 고기는 ControllerActionInvoker
클래스의 사용자 정의 구현에서 발생 조치가 발견 된 후에 요청 유효성 검증을 수행하는 것입니다. 요청이 유효하지 않은 경우 즉시 오류가 발생하지 않지만 GetParameterValue
이 호출되면 예외가 발생합니다. 이 문제를 피하기 위해이 메서드를 재정의하고 try-catch에서 기본 호출을 래핑합니다. 예외가 잡히면 기본적으로 모델 바인딩을 다시 구현합니다 (이 코드의 품질에 대한 약속은하지 않습니다). ModelStateDictionary
객체에 값을 추가합니다.
보너스로, 내 ajax 메소드의 표준 형식으로 오류를 반환하고 싶었으므로 InvokeActionMethod
의 사용자 정의 구현도 추가했습니다.
protected override ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
{
if (_modelState.ContainsKey(_requestValidationErrorKey))
{
var errorResult = new ErrorResult(_modelState[_requestValidationErrorKey].Errors[0].ErrorMessage, _modelState);
var type = controllerContext.Controller.GetType();
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
if (methods.Where(m => m.Name == actionDescriptor.ActionName).First().ReturnType == typeof(JsonResult))
return (controllerContext.Controller as ExtendedControllerBase).GetJson(errorResult);
}
return base.InvokeActionMethod(controllerContext, actionDescriptor, parameters);
}