Есть ли способ отключить JSON ModelBinder в ASP.NET MVC 3 RC2?
В ASP.NET MVC 3 RC2 ModelBinder по умолчанию автоматически анализирует тело запроса, если Content-Type
установлен в application/json
, Проблема в том, что это оставляет Request.InputStream
в конце потока. Это означает, что если вы попытаетесь прочитать входной поток, используя свой собственный код, вы сначала сбросите его обратно в начало:
// 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();
Так как я использую Json.NET
чтобы выполнить сериализацию / десериализацию, я бы хотел отключить ModelBinder по умолчанию от выполнения этого дополнительного анализа. Есть ли способ сделать это?
2 ответа
Вы можете поместить следующее в Application_Start в вашем Global.asax:
ValueProviderFactories.Factories.Remove(
ValueProviderFactories.Factories.OfType<JsonValueProviderFactory>().First());
Это предполагает, что есть только один из этого типа (который по умолчанию есть), но его можно легко изменить на работу, если их больше одного. Я не верю, что есть более чистый путь, если это то, что вы ищете.
Я, очевидно, довольно поздно отвечаю на это, но я разработал способ изменить IValueProvider
для конкретного действия в MVC5. Я не пытался выяснить, возможно ли это в MVC3, так как этот вопрос старый, но я предполагаю, что он несколько похож.
Отказ от ответственности: это не красиво.
Во-первых, мы создаем новый интерфейс, который мы можем реализовать в атрибуте для создания конфигураций, специфичных для действий:
internal interface IActionConfigurator
{
void Configure(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
}
Затем мы создаем ControllerActionInvoker
(или же AsyncControllerActionInvoker
если вы используете async
) чтобы подключить наш новый интерфейс:
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;
}
}
Теперь мы должны реализовать кастом DefaultControllerFactory
установить 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;
}
}
Наконец, мы устанавливаем нашу фабрику пользовательских контроллеров по умолчанию в коде запуска:
ControllerBuilder.Current.SetControllerFactory(typeof(CustomControllerFactory));
и реализовать наш IActionConfigurator
Интерфейс в пользовательском атрибуте:
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);
}
}
Поскольку новый экземпляр Controller создается при каждом запросе, мы можем установить значения для конкретного действия на контроллере, чтобы изменить способ обработки действия MVC.
[AcceptVerbs(HttpVerbs.Post)]
[IgnoreJsonActionConfigurator]
public async Task<ActionResult> Foo() { ... }