Есть ли способ отключить 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() { ... }
Другие вопросы по тегам