2012-10-03 4 views
7

POSTからのユーザー入力HTML文字列をモデルオブジェクトの単純文字列変数にバインドしようとしています。これは、[AllowHtml]属性を使用すると問題なく動作します。HTML文字列のカスタムモデルバインダーの使用

public class SafeHtmlModelBinder : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerCtx, ModelBindingContext bindingCtx) 
    { 
     var bound = base.BindModel(controllerCtx, bindingCtx); 
     // TODO - return a safe HTML fragment string 
     return bound; 
    } 
} 

CustomModelBinderAttribute:しかし、私はModelBinderを作成しているので、それがモデルにその方法を作る前に、HTMLのサニタイズしたい

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] 
public class SafeHtmlModelBinderAttribute : CustomModelBinderAttribute 
{ 
    public SafeHtmlModelBinderAttribute() 
    { 
     binder = new SafeHtmlModelBinder(); 
    } 

    private IModelBinder binder; 

    public override IModelBinder GetBinder() 
    { 
     return binder; 
    } 
} 

私は、モデルのプロパティに注釈を付けます私は新しい属性で消毒することにしたい:これはhttp://msdn.microsoft.com/en-us/magazine/hh781022.aspxでの例を以下れる

[Required(AllowEmptyStrings = false, ErrorMessage = "You must fill in your profile summary")] 
[AllowHtml, SafeHtmlModelBinder, WordCount(Min = 1, Max = 300)] 
public string Summary { get; set; } 

。残念ながら、それは動作していないようです! BindModelメソッドにブレークポイントを置くと、決してヒットしません。何か案は?私はSetProperty方法で時に値を傍受し、代わりにHTMLを含めることができます文字列プロパティを含むクラスにSafeHtmlModelBinderAttributeを適用するために私のIModelBinderを変更したジョエルからの情報に基づいて

UPDATE

。コードは、プロパティが文字列であり、またサニタイズしようとする前にHTMLを含むように許可されていることを確認します:私はちょうど同じことに苦しんできた

public class SafeHtmlModelBinder : DefaultModelBinder 
{ 
    protected override void SetProperty(
     ControllerContext controllerCtx, 
     ModelBindingContext bindingCtx, 
     PropertyDescriptor property, 
     object value) 
    { 
     var propertyIsString = property.PropertyType == typeof(string); 
     var propertyAllowsHtml = property.Attributes.OfType<AllowHtmlAttribute>().Count() >= 1; 

     var input = value as string; 
     if (propertyIsString && propertyAllowsHtml && input != null) 
     { 
      // TODO - sanitize HTML 
      value = input; 
     } 

     base.SetProperty(controllerCtx, bindingCtx, property, value); 
    } 
} 
+0

CustomModelBinderAttributeはプロパティで動作しますか?この属性は、内部const AttributeTargetsでのみ使用されるMVCソースコードを参照してください。ValidTargets = AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Struct; '...これは、フレームワークがプロパティでこれを見ることを期待していないかもしれないことを示唆しています。 – Schneider

答えて

1

。 GetBinder()メソッドが呼び出されないようです。掘り出した後、私はthis postを見つけました。受け入れられる答えは、プロパティのモデルバインディング属性を置くことができないということです。

それが本当であるかどうかはわかりませんが、今のところ私は別のやり方をする必要があります。 1つのアイデアは、より一般的なModelBinderを作成し、バインディングを実行するときに属性の存在を確認することです。this answerに示唆されているのと同様です。

1

私はあなたがプロパティ

public class PropertyBinderAttribute : Attribute 
     { 
      public PropertyBinderAttribute(Type binderType) 
      { 
       BinderType = binderType; 
      } 

      public Type BinderType { get; private set; } 
     } 

次モデルバインダーに適用することができ、簡単な属性を必要とする http://aboutcode.net/2011/03/12/mvc-property-binder.html 作品非常によく

まずからderrived、次の解決策を見つけた

public class DefaultModelBinder : System.Web.Mvc.DefaultModelBinder 
    { 
     protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) 
     { 
      var propertyBinderAttribute = TryFindPropertyBinderAttribute(propertyDescriptor); 
      if (propertyBinderAttribute != null) 
      { 
       var binder = CreateBinder(propertyBinderAttribute); 
       binder.BindProperty(controllerContext, bindingContext, propertyDescriptor); 
      } 
      else // revert to the default behavior. 
      { 
       base.BindProperty(controllerContext, bindingContext, propertyDescriptor); 
      } 
     } 

     IPropertyBinder CreateBinder(PropertyBinderAttribute propertyBinderAttribute) 
     { 
      return (IPropertyBinder)DependencyResolver.Current.GetService(propertyBinderAttribute.BinderType); 
     } 

     PropertyBinderAttribute TryFindPropertyBinderAttribute(PropertyDescriptor propertyDescriptor) 
     { 
      return propertyDescriptor.Attributes.OfType<PropertyBinderAttribute>().FirstOrDefault(); 
     } 
    } 

は、Global.asax.csでオーバーライドされます。

ModelBinders.Binders.DefaultBinder = new DefaultModelBinder(); 

は、あなたのモデルバインダーを作成し、今きれいにモデルプロパティ

[PropertyBinder(typeof(InvariantCultureDecimalModelBinder))] 
public decimal? value 

や使用上の標準的な方法で適用することができる

public class InvariantCultureDecimalModelBinder : IModelBinder, IPropertyBinder 
    { 
     public void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) 
     { 
      var subPropertyName = CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name); 
      if (!bindingContext.ValueProvider.ContainsPrefix(subPropertyName)) 
       return; 

      var attemptedValue = bindingContext.ValueProvider.GetValue(subPropertyName).AttemptedValue; 
      if (String.IsNullOrEmpty(attemptedValue)) 
       return; 

      object actualValue = null; 
      try 
      { 
       actualValue = Convert.ToDecimal(attemptedValue, CultureInfo.InvariantCulture); 
      } 
      catch (FormatException e) 
      { 
       bindingContext.ModelState[propertyDescriptor.Name].Errors.Add(e); 
      } 

      propertyDescriptor.SetValue(bindingContext.Model, actualValue); 
     } 

     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
     { 
      var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 
      var modelState = new ModelState { Value = valueResult }; 
      object actualValue = null; 
      try 
      { 
       if (!String.IsNullOrEmpty(valueResult.AttemptedValue)) 
        actualValue = Convert.ToDecimal(valueResult.AttemptedValue, CultureInfo.InvariantCulture); 
      } 
      catch (FormatException e) 
      { 
       modelState.Errors.Add(e); 
      } 

      bindingContext.ModelState.Add(bindingContext.ModelName, modelState); 
      return actualValue; 
     } 

     //Duplicate code exits in DefaulModelBinder but it is protected internal 
     private string CreateSubPropertyName(string prefix, string propertyName) 
     { 
      if (string.IsNullOrEmpty(prefix)) 
       return propertyName; 
      if (string.IsNullOrEmpty(propertyName)) 
       return prefix; 
      else 
       return prefix + "." + propertyName; 
     } 

    } 

内蔵のパラメータの属性

public ActionResult DoSomething([ModelBinder(typeof(InvariantCultureDecimalModelBinder))] decimal value) 
関連する問題