简体   繁体   中英

Unable to read input stream

I am using ActionFilterAttribute to get the request before hitting the controller as below :

 public override void OnActionExecuting(HttpActionContext actionContext)
 {
     using (var stream = new MemoryStream())
     {
        HttpContextBase context = (HttpContextBase)actionContext.Request.Properties["MS_HttpContext"];
        context.Request.InputStream.Seek(0, SeekOrigin.Begin);
        context.Request.InputStream.CopyTo(stream);
        requestBody = Encoding.UTF8.GetString(stream.ToArray());
     }
 }

The above method is working for small request but for a large json it is giving me this error :

Either BinaryRead, Form, Files, or InputStream was accessed before the internal storage was filled by the caller of HttpRequest.GetBufferedInputStream.

And the input stream gives this error

context.Request.InputStream threw an exception of type System.InvalidOperationException System.IO.Stream {System.InvalidOperationException}

As I found in my research that it is an issue with the timeout but I am unable to change the timeout in the code. I tried changing the values in the web.config file maxRequestLength="102400000" and maxAllowedContentLength="209715100" but still I am facing the same error.
If I read the GetBufferedInputStream but still same issue it is reading just a part of the buffer, not the entire stream.

I also tried the below :

 Stream InStream;
 int Len;
 InStream = HttpContext.Current.Request.InputStream;
 Len = System.Convert.ToInt32(InStream.Length);
 byte[] ByteArray = new byte[Len + 1];
 InStream.Seek(0, SeekOrigin.Begin);
 InStream.Read(ByteArray, 0, Len);
 var jsonParam = System.Text.Encoding.UTF8.GetString(ByteArray); 

Note that if I set the content type application/xml or application/x-www-form-urlencoded it works, but if I set it to application/json it gives me this error!!

Please advise!

There are couple of points:

First, if you try and read 0 bytes from a stream, then it will throw a System.InvalidOperationException exception. So, I will change your code like below and add a check for ContentLength > 0 .

using (var stream = new MemoryStream())
     {
        HttpContextBase context = (HttpContextBase)actionContext.Request.Properties["MS_HttpContext"];
        if(context.Request.Contentlength > 0)
        {
            context.Request.InputStream.Seek(0, SeekOrigin.Begin);
            context.Request.InputStream.CopyTo(stream);
            requestBody = Encoding.UTF8.GetString(stream.ToArray());
        }
     }

Also, I once experienced the same issue and increasing the maxRequestLength in web.config seems to have resolved the issue. This link further provides more info here

This is how I do it inside my Model binder but I'm not sure how it will work with your Action filter. I checked online and there's conflicting information; Some say you cannot read the input stream since it's not seekable and ASP.NET would need to read it to bind the model. Some say it's indeed seekable, and use the method you shared above. So the only way to figure out what would really work is to test.

I hope my code sample helps you figure it out.

object request = null;
if (actionContext.Request.Method == HttpMethod.Post && "application/json".Equals(actionContext.Request.Content.Headers.ContentType.MediaType))
{
    var jsonContentTask = actionContext.Request.Content.ReadAsStringAsync();
    Task.WaitAll(jsonContentTask);
    string jsonContent = jsonContentTask.Result;
    //... other stuff
}

I may be wrong but here is what I found. The action filters are executed after the model binding, which means the request stream has already been read. In your case i am not sure what that means. https://exceptionnotfound.net/the-asp-net-web-api-2-http-message-lifecycle-in-43-easy-steps-2/ explains the lifecycle in detail. Changing the content type wouldn't change the lifecycle events but would rather change the request content which in turn might affect the model binding. If you have a model set for the action, then How to get current model in action filter should help. So the solution would be to get model object from the actionContext and then modify it accordingly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM