Promoting a request. Input stream before reaching ModelBinder

In our MVC 2 app, we have a JSON binder implemented like this:

    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);
    }

      

After upgrading to MVC 4, I noticed that we are getting empty incoming models for incoming JSON messages. When digging into it it became apparent that something was upstream propelling the stream. It was easy enough to fix, so

    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);
    }

      

But I'm wondering what happened, what made the change necessary? Was the previous implementation only working by coincidence?

+3


source to share


1 answer


No, the previous implementation did not work by coincidence.

ASP.NET MVC 3

there is built-in support for JSON binding, which allows action methods to receive data in JSON encoding and bind a model to action method parameters.

JsonValueProviderFactory

by default logs in ASP.NET MVC 3

or more JsonValueProviderFactory

. The value provider JSON

runs before binding the model and serializes the request data into a dictionary. The dictionary data is then passed to model binder

.



Let's see how it works JsonValueProviderFactory

Here is a link to the source code JsonValueProviderFactory

provided in ASP.NET MVC Open source JsonValueProviderFactory.cs

The method GetDeserializedObject

defined in JsonValueProviderFactory.cs

reads stream

if Content-Type

set to, application/json

and therefore leaves Request.InputStream

at the end of the stream. So here GetDeserializedObject

is called GetDeserializedObject

and then BindModel

. Since we GetDeserializedObject

have already read the stream once and pushed Request.InputStream

to the end of the stream, we need to flush Request.InputStream

toBindModel

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;
}

      

+3


source







All Articles