18

から継承する MVC 4 用のカスタム モデル バインダーを構築しようとしていDefaultModelBinderます。任意のバインディング レベルで任意のインターフェイスをインターセプトし、 という非表示フィールドから目的の型をロードしようとします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
}

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

指定された型のプロパティ バインディングを引き継ぐように既定のバインダーに指示するには、これに対して他に何をする必要がありますか?

4

2 に答える 2

26

この記事では、私がモデル バインダーを過度に複雑にしていることがわかりました。次のコードが機能します。

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);
    }
}
于 2013-05-25T15:53:47.243 に答える
1

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インジェクションが戻ってきて検証メッセージに表示され、実行されることが気に入りませんでした。この実装を使用することで、ユーザーが提供した値を返すデフォルト メッセージをオーバーライドしています。

于 2014-11-21T18:45:06.413 に答える