Obviamente, llegué bastante tarde respondiendo esto, pero he desarrollado una forma de cambiar el IValueProvider
para una acción específica en MVC5. No he hecho el esfuerzo de ver si esto es posible en MVC3 ya que esta pregunta es antigua, pero supongo que es algo similar.
Descargo de responsabilidad: No es bonito.
En primer lugar, se crea una nueva interfaz que podemos poner en práctica en un atributo para hacer configuraciones específicas de acción:
internal interface IActionConfigurator
{
void Configure(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
}
Luego, creamos una costumbre ControllerActionInvoker
(o AsyncControllerActionInvoker
si utiliza async
) para conectar nuestra nueva interfaz:
internal sealed class CustomControllerActionInvoker : AsyncControllerActionInvoker
{
protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
{
var actionDescriptor = base.FindAction(controllerContext, controllerDescriptor, actionName);
var configurators = actionDescriptor.GetCustomAttributes(typeof(IActionConfigurator), true).Cast<IActionConfigurator>();
foreach (var configurator in configurators)
configurator.Configure(controllerContext, actionDescriptor);
return actionDescriptor;
}
}
Ahora, tenemos que poner en práctica una costumbre DefaultControllerFactory
para establecer Controller.ActionInvoker
:
internal sealed class CustomControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
var instance = base.GetControllerInstance(requestContext, controllerType);
var controller = instance as Controller;
if (controller != null)
controller.ActionInvoker = new CustomControllerActionInvoker();
return instance;
}
}
Por último, fijamos nuestra fábrica controlador personalizado como opción predeterminada en el código de inicio:
ControllerBuilder.Current.SetControllerFactory(typeof(CustomControllerFactory));
y poner en práctica nuestra interfaz IActionConfigurator
en un atributo personalizado:
internal sealed class IgnoreJsonActionConfiguratorAttribute : Attribute, IActionConfigurator
{
public void Configure(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
// Here we can configure action-specific stuff on the controller
var factories = ValueProviderFactories.Factories.Where(f => !(f is JsonValueProviderFactory)).ToList();
controllerContext.Controller.ValueProvider = new ValueProviderFactoryCollection(factories).GetValueProvider(controllerContext);
}
}
Desde una nueva instancia del controlador se crea en cada solicitud, podemos establecer valores específicos de acción en el controlador para modificar cómo MVC procesa la acción.
[AcceptVerbs(HttpVerbs.Post)]
[IgnoreJsonActionConfigurator]
public async Task<ActionResult> Foo() { ... }
¡Gracias, eso funcionó! ¿Es esta una característica no documentada, o simplemente me perdí algo? –
MVC incluye un conjunto de fábricas de proveedores de valores por defecto. Con MVC3, incluyeron JsonValueProviderFactory como uno de los valores predeterminados. Todo lo que hace el código anterior es encontrarlo y eliminarlo cuando se inicia la aplicación. Otra forma de evitarlo podría ser que tus solicitudes ajax utilicen el tipo de contenido application/json, pero creo que eliminar la fábrica del proveedor de valor es probablemente más correcto. –
Gracias por la explicación. Ni siquiera sabía que había fábricas de proveedores para cosas como cadenas de búsqueda y rutas. Supuse que el ModelBinder manejaba todo eso. –