In ASP.NET MVC 3 RC2, the default ModelBinder will automatically parse the request body if the Content-Type
is set to application/json
. Problem is, this leaves the Request.InputStream
at the end of the stream. This means that if you try to read the input stream using your own code, you first have reset it back to the beginning:
// 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;开发者_JS百科
var requestBody = reader.ReadToEnd();
Since I'm using Json.NET
to do my serialization/deserialization, I'd like to disable the default ModelBinder from doing this extra parsing. Is there any way to do that?
You can put the following in Application_Start in your Global.asax:
ValueProviderFactories.Factories.Remove(
ValueProviderFactories.Factories.OfType<JsonValueProviderFactory>().First());
This assumes there is only one of that type (which by default there is), but it can easily be changed to work if there is more than one. I don't believe there is a cleaner way if that is what you are looking for.
I'm obviously pretty late in answering this, but I've developed a way to change the IValueProvider
for a specific action in MVC5. I haven't gone through the effort of seeing if this is possible in MVC3 since this question is old, but I assume it is somewhat similar.
Disclaimer: It's not pretty.
First, we create a new interface we can implement in an attribute to make action-specific configurations:
internal interface IActionConfigurator
{
void Configure(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
}
Then, we create a custom ControllerActionInvoker
(or AsyncControllerActionInvoker
if you use async
) to hook up our new interface:
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;
}
}
Now, we have to implement a custom DefaultControllerFactory
to set 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;
}
}
Finally, we set our custom controller factory as the default in the startup code:
ControllerBuilder.Current.SetControllerFactory(typeof(CustomControllerFactory));
and implement our IActionConfigurator
interface in a custom attribute:
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);
}
}
Since a new Controller instance is created on each request, we are able to set action-specific values on the controller to modify how MVC processes the action.
[AcceptVerbs(HttpVerbs.Post)]
[IgnoreJsonActionConfigurator]
public async Task<ActionResult> Foo() { ... }
精彩评论