2010-05-19 2 views
3

ASP.NET MVC 2로 구축하고있는 시스템의 보안을 조사해 보면 ASP.NET의 요청 유효성 검사 기능을 발견하게되었습니다. 실제로이 기능은 매우 정교합니다. 하지만 HTML로 데이터를 입력 할 때 죽음의 옐로우 스크린을 사용자에게 보여주고 싶지 않으므로 더 나은 솔루션을 찾으려고합니다.ASP.NET MVC에서 ModelStateDictionary에 요청 유효성 검사 오류를 추가하는 방법은 무엇입니까?

내 아이디어는 잘못된 데이터가있는 모든 필드를 찾아서 UI를 오류 메시지로 자동 표시되도록 조치를 호출하기 전에 ModelStateDictionary에 추가하는 것입니다. 인터넷 검색을 한 후 아무도이 프로그램을 구현하지 않은 것으로 보입니다. 여기에 누구든지이 작업을 수행하는 방법에 대한 제안이 있습니까? 내 자신의 생각은 컨트롤러에 ControllerActionInvoker 사용자 지정을 제공하는 것입니다 (here). 어떻게 든이를 확인하고 ModelStateDictionary을 수정하지만이 마지막 비트를 수행하는 방법에 대해 고민하고 있습니다.

예외를 잡는 것만으로는 실제로 필요한 모든 정보가 포함되어 있지 않으므로 유용한 접근 방식으로 보이지 않습니다.

나는 스스로에게 대답했지만, 나는 여전히 더 우아하고 견고한 해결책을 듣고 자 매우 흥미를 느꼈다.

답변

1

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