在到达ModelBinder之前推进Request.InputStream

时间:2014-12-03 13:36:49

标签: c# asp.net-mvc asp.net-mvc-4

在我们的MVC 2应用程序中,我们有一个像这样实现的JSON模型绑定器:

    public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        string input;

        using (var reader = new StreamReader(controllerContext.HttpContext.Request.InputStream))
        {
            input = reader.ReadToEnd();
        }

        return JsonConvert.DeserializeObject(
            input,
            bindingContext.ModelType);
    }

更新到MVC 4后,我注意到我们收到了传入JSON帖子的空传入模型。当深入挖掘时,上游的某些东西正在推进。这很容易修复,就像这样

    public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        string input;

        //something upstream after MVC 4 upgrade is advancing the stream to end before we can read it
        controllerContext.HttpContext.Request.InputStream.Position = 0;

        using (var reader = new StreamReader(controllerContext.HttpContext.Request.InputStream))
        {
            input = reader.ReadToEnd();
        }

        return JsonConvert.DeserializeObject(
            input,
            bindingContext.ModelType);
    }

但我想知道发生了什么事情使得必要的改变?先前的实施只是巧合吗?

1 个答案:

答案 0 :(得分:3)

不,先前的实施并非巧合。

ASP.NET MVC 3引入了内置JSON绑定支持,使行为方法能够接收JSON编码数据并将其模型绑定到操作方法参数。

JsonValueProviderFactory默认在ASP.NET MVC 3及之后注册。 JSON值提供程序在模型绑定之前运行,并将请求数据序列化为字典。然后将字典数据传递给model binder

让我们看看JsonValueProviderFactory是如何工作的。这是ASP.NET MVC开源代码JsonValueProviderFactory.cs

中提供的JsonValueProviderFactory源代码的链接 GetDeserializedObject中定义的

JsonValueProviderFactory.cs方法会在stream设置为Content-Type时读取application/json,因此会离开Request.InputStream在流的最后。所以首先调用GetDeserializedObject,然后调用BindModel。以来 GetDeserializedObject已经读过一次流并将Request.InputStream推进到流的末尾,我们需要在Request.InputStream

再次重置BindModel
private static object GetDeserializedObject(ControllerContext controllerContext)
{
    if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
    {
            // not JSON request
            return null;
    }
    StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream);
    string bodyText = reader.ReadToEnd();
    if (String.IsNullOrEmpty(bodyText))
    {
        // no JSON data
        return null;
    }
    JavaScriptSerializer serializer = new JavaScriptSerializer();
    object jsonData = serializer.DeserializeObject(bodyText);
    return jsonData;
}