问题 有没有办法在ASP.NET MVC 3 RC2中禁用JSON ModelBinder?


在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进行额外的解析。有没有办法做到这一点?


3311
2017-12-21 04:16


起源



答案:


您可以在Global.asax中的Application_Start中添加以下内容:

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

这假设只有一种类型(默认情况下有),但如果有多种类型,可以很容易地将其更改为有效。如果那是你想要的,我不相信有一个更清洁的方式。


15
2017-12-21 04:27



谢谢,这就是诀窍!这是一个没有记录的功能,还是我只是错过了什么? - Daniel T.
MVC默认包含一组值提供程序工厂。使用MVC3,它们将JsonValueProviderFactory作为默认值之一。以上所有代码都是找到它并在应用程序启动时将其删除。另一种方法可能是没有让你的ajax请求使用内容类型application / json,但我觉得删除值提供程序工厂可能更正确。 - Brian Ball
感谢您的解释。我甚至都不知道有像查询字符串和路线这样的提供商工厂。我假设ModelBinder处理了所有这些。 - Daniel T.
不,我的理解是值提供者实质上解析请求数据,然后模型绑定器获取解析的数据并填充模型的值。 - Brian Ball
为特定操作禁用此绑定器而不是完全禁用它仍然是很好的。 - Softlion


答案:


您可以在Global.asax中的Application_Start中添加以下内容:

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

这假设只有一种类型(默认情况下有),但如果有多种类型,可以很容易地将其更改为有效。如果那是你想要的,我不相信有一个更清洁的方式。


15
2017-12-21 04:27



谢谢,这就是诀窍!这是一个没有记录的功能,还是我只是错过了什么? - Daniel T.
MVC默认包含一组值提供程序工厂。使用MVC3,它们将JsonValueProviderFactory作为默认值之一。以上所有代码都是找到它并在应用程序启动时将其删除。另一种方法可能是没有让你的ajax请求使用内容类型application / json,但我觉得删除值提供程序工厂可能更正确。 - Brian Ball
感谢您的解释。我甚至都不知道有像查询字符串和路线这样的提供商工厂。我假设ModelBinder处理了所有这些。 - Daniel T.
不,我的理解是值提供者实质上解析请求数据,然后模型绑定器获取解析的数据并填充模型的值。 - Brian Ball
为特定操作禁用此绑定器而不是完全禁用它仍然是很好的。 - Softlion


我显然很晚才回答这个问题,但我已经开发出一种方法来改变它 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() { ... }

0
2018-03-27 13:08