net mvc editar asp c# asp.net-mvc-4 model-binding

mvc - razor c#



Custom Model Binder hereda de DefaultModelBinder (2)

Estoy intentando crear una carpeta de modelo personalizada para MVC 4 que heredará de DefaultModelBinder . Me gustaría interceptar cualquier interfaz en cualquier nivel de enlace e intentar cargar el tipo deseado desde un campo oculto llamado AssemblyQualifiedName .

Esto es lo que tengo hasta ahora (simplificado):

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

Ejemplo de archivo Create.cshtml (simplificado):

@model Models.ScheduledJob @* Begin Form *@ @Html.Hidden("AssemblyQualifiedName", Model.Job.GetType().AssemblyQualifiedName) @Html.Partial("_JobParameters") @* End Form *@

El _JobParameters.cshtml parcial anterior _JobParameters.cshtml las Model.Job del Model.Job y construye los controles de edición, de forma similar a @Html.EditorFor() , pero con un margen adicional. La propiedad ScheduledJob.Job es del tipo IJob (interfaz).

Ejemplo ScheduledJobsController.cs (simplificado):

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

Cuando guardo el formulario, interpreta el tipo de objeto correctamente y obtiene una nueva instancia, pero las propiedades del objeto no se establecen en sus valores apropiados.

¿Qué más debo hacer para decirle al encuadernador predeterminado que se haga cargo del enlace de propiedad del tipo especificado?


Este artículo me mostró que estaba complicando demasiado la carpeta del modelo. El siguiente código funciona:

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


Con MVC 4 es fácil sobrescribir los mensajes, si eso es todo lo que necesita en una carpeta de modelo personalizado:

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

Luego crea un archivo de recursos llamado ValidationMessages con entradas como esta:

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

.

Hicimos esto por una falla de cumplimiento. A nuestro escaneo de seguridad no le gustó que una inyección de javascript volviera y apareciera en los Mensajes de Validación y se ejecutara. Al utilizar esta implementación, anulamos los mensajes predeterminados que devuelven el valor proporcionado por el usuario.