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
}
_
フォームを保存すると、オブジェクトタイプが正しく解釈され、新しいインスタンスが取得されますが、オブジェクトのプロパティが適切な値に設定されていません。
指定されたタイプのプロパティバインディングを引き継ぐようにデフォルトのバインダーに指示するために、これ以外に何をする必要がありますか?
この記事 は、モデルバインダーが複雑すぎることを示しました。次のコードは機能します。
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);
}
}
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
インジェクションが戻って検証メッセージに表示されて実行されることを好みませんでした。この実装を使用することで、ユーザーが指定した値を返すデフォルトのメッセージを上書きしています。