web-dev-qa-db-ja.com

DefaultModelBinderから継承するカスタムモデルバインダー

DefaultModelBinderから継承するMVC 4のカスタムモデルバインダーを構築しようとしています。 anyバインディングレベルで任意のインターフェイスをインターセプトし、AssemblyQualifiedNameという非表示フィールドから目的のタイプをロードしようと思います。

ここに私がこれまでに持っているものがあります(簡略化されています):

_public class MyWebApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        ModelBinders.Binders.DefaultBinder = new InterfaceModelBinder();
    }
}

public class InterfaceModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, 
        ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType.IsInterface 
            && controllerContext.RequestContext.HttpContext.Request.Form.AllKeys.Contains("AssemblyQualifiedName"))
        {
            ModelBindingContext context = new ModelBindingContext(bindingContext);

            var item = Activator.CreateInstance(
                Type.GetType(controllerContext.RequestContext.HttpContext.Request.Form["AssemblyQualifiedName"]));

            Func<object> modelAccessor = () => item;
            context.ModelMetadata = new ModelMetadata(new DataAnnotationsModelMetadataProvider(),
                bindingContext.ModelMetadata.ContainerType, modelAccessor, item.GetType(), bindingContext.ModelName);

            return base.BindModel(controllerContext, context);
        }

        return base.BindModel(controllerContext, bindingContext);
    }
}
_

Create.cshtmlファイルの例(簡略化):

_@model Models.ScheduledJob

@* Begin Form *@
@Html.Hidden("AssemblyQualifiedName", Model.Job.GetType().AssemblyQualifiedName)

@Html.Partial("_JobParameters")
@* End Form *@
_

上記の部分的な__JobParameters.cshtml_は、_Model.Job_のプロパティを調べ、@Html.EditorFor()に似ていますが、追加のマークアップを使用して編集コントロールを構築します。 _ScheduledJob.Job_プロパティのタイプはIJob(インターフェース)です。

ScheduledJobsController.csの例(簡略化):

_[HttpPost]
public ActionResult Create(ScheduledJob scheduledJob)
{
    //scheduledJob.Job here is not null, but has only default values
}
_

フォームを保存すると、オブジェクトタイプが正しく解釈され、新しいインスタンスが取得されますが、オブジェクトのプロパティが適切な値に設定されていません。

指定されたタイプのプロパティバインディングを引き継ぐようにデフォルトのバインダーに指示するために、これ以外に何をする必要がありますか?

17
Albert Bori

この記事 は、モデルバインダーが複雑すぎることを示しました。次のコードは機能します。

public class InterfaceModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType.IsInterface)
        {
            Type desiredType = Type.GetType(
                EncryptionService.Decrypt(
                    (string)bindingContext.ValueProvider.GetValue("AssemblyQualifiedName").ConvertTo(typeof(string))));
            bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, desiredType);
        }

        return base.BindModel(controllerContext, bindingContext);
    }
}
24
Albert Bori

MVC 4を使用すると、メッセージを上書きするのが簡単です。それがカスタムモデルバインダーで必要なすべての場合です。

    protected void Application_Start(object sender, EventArgs e)
    {
        //set mvc default messages, or language specifc
        ClientDataTypeModelValidatorProvider.ResourceClassKey = "ValidationMessages";
        DefaultModelBinder.ResourceClassKey = "ValidationMessages";
    }

次に、ValidationMessagesという名前のリソースファイルを次のようなエントリで作成します。

NAME: FieldMustBeDate 
VALUE: The field {0} must be a date. 
NAME: FieldMustBeNumeric 
VALUE: The field {0} must be a number

これはコンプライアンス違反のために行いました。私たちのセキュリティスキャンは、javascriptインジェクションが戻って検証メッセージに表示されて実行されることを好みませんでした。この実装を使用することで、ユーザーが指定した値を返すデフォルトのメッセージを上書きしています。

1
Joe Kahl