2010-12-21 10 views
11

En ASP.NET MVC 3 RC2, el ModelBinder predeterminado analizará automáticamente el cuerpo de la solicitud si el Content-Type está configurado en application/json. El problema es que esto deja el Request.InputStream al final de la transmisión. Esto significa que si se intenta leer el flujo de entrada utilizando su propio código, primero tiene que restablecer de nuevo al principio:¿Hay alguna manera de deshabilitar el JSON ModelBinder en ASP.NET MVC 3 RC2?

// client sends HTTP request with Content-Type: application/json and a JSON 
// string in the body 

// requestBody is null because the stream is already at the end 
var requestBody = new StreamReader(Request.InputStream).ReadToEnd(); 

// resets the position back to the beginning of the input stream 
var reader = new StreamReader(Request.InputStream); 
reader.BaseStream.Position = 0; 
var requestBody = reader.ReadToEnd(); 

Desde que estoy usando para hacer mi Json.NET serialización/deserialización, me gustaría para deshabilitar el ModelBinder predeterminado de hacer este análisis adicional. ¿Hay alguna forma de hacer eso?

Respuesta

15

Usted puede poner lo siguiente en Application_Start en su Global.asax:

ValueProviderFactories.Factories.Remove(
      ValueProviderFactories.Factories.OfType<JsonValueProviderFactory>().First()); 

Esto supone que sólo hay uno de este tipo (que por defecto no lo es), pero se puede cambiar fácilmente a trabajar si Hay mas de uno. No creo que haya una manera más limpia si eso es lo que estás buscando.

+0

¡Gracias, eso funcionó! ¿Es esta una característica no documentada, o simplemente me perdí algo? –

+0

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. –

+0

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. –

0

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() { ... } 
Cuestiones relacionadas