Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Web Api Request Throws "Error while copying content to a stream."

I'm trying to implement this code example, but get a HttpRequestException - "Error while copying content to a stream." when the ReadAsStringAsync() method is called. The inner exception is "Cannot access a disposed object." I'm using Fiddler to make the request. I don't understand. Can someone explain why I'm getting this exception and offer a solution?

Web Api Method:

public async Task<HttpResponseMessage> Post(HttpRequestMessage request)
{
    try
    {
        var jsonString = await request.Content.ReadAsStringAsync();
    }
    catch (Exception ex)
    {                
        throw;
    }
    return new HttpResponseMessage(HttpStatusCode.Created);
}

Fiddler (POST):

User-Agent: Fiddler
Host: localhost:23567
Content-Length: 18
Content-Type: application/json; charset=utf-8
Body{"Test":1}

Edit:

I have a clue, but need verification. On the Web Api controller, I have an ActionFilterAttribute and in its OnActionExecuting override, there's this line:

public override async void OnActionExecuting(HttpActionContext actionContext)
{
    // omitted code
    actionContext.Request.Content.ReadAsStreamAsync();
}

Could it be that because the Content is read here, it's not available again? If so, how can I make it available in the method? Is the Content here the same as the HttpRequestMessage? This may contain an answer.

like image 980
Big Daddy Avatar asked Mar 01 '16 13:03

Big Daddy


4 Answers

Just a guess, should post as comment but I want include a code snippet:

Maybe you call Post function inside a using block, but don't use await.

using (HttpRequestMessage request = ...)
{
    // Maybe you use this:
    Post(request);

    // Instead of this
    var response = await Post(request);
}

Or you don't dispose old connects properly.

Also, try add HttpVersion.Version10 to your request, which change header request from Connection: keep-alive to Connection: close, which can cause exception in some case you reuse a host (Search for more info)

request.Version = HttpVersion.Version10;
var jsonString = await request.Content.ReadAsStringAsync();
like image 173
Thanh Nguyen Avatar answered Nov 04 '22 17:11

Thanh Nguyen


Because the controller's ActionFilterAttribute's OnActionExecuting method is calling ReadAsStreamAsync, the Content can't be read again. I changed ReadAsStreamAsync to ReadAsStringAsync and the request's Content is available in the controller. Apparantly, ReadAsStringAsync buffers the Content so it's still available. This link provided the answer.

like image 39
Big Daddy Avatar answered Nov 04 '22 18:11

Big Daddy


I hope this (late) post will help someone someday...

In short: the accepted answer suggests to read the entire file as string (and not as stream) in order to bypass a read problem

BUT... reading a file as a string is not such a great idea

I figured out that replacing MultipartFormDataStreamProvider with MultipartMemoryStreamProvider works great - and let you read your uploaded file as desired

My code (at least the relevant parts of it)

    [HttpPost]
    [Route("upload/file")] // you may replace this route to suit your api service
    public async Task<IHttpActionResult> Upload()
    {
        if (!Request.Content.IsMimeMultipartContent("form-data"))
        {
            return BadRequest("Unsupported media type");
        }

        try
        {
            var provider = new MultipartMemoryStreamProvider();

            await Request.Content.ReadAsMultipartAsync(provider);

            if (provider.Contents.Count == 0) return InternalServerError(new Exception("Upload failed"));

            var file = provider.Contents[0]; // if you handle more then 1 file you can loop provider.Contents

            var buffer = await file.ReadAsByteArrayAsync();

            // .. do whatever needed here

            return Ok();

        }
        catch (Exception ex)
        {
            return BadRequest(ex.GetBaseException().Message);
        }
    }
like image 2
ymz Avatar answered Nov 04 '22 17:11

ymz


I resolved with this, my problem was that the response was in gzip:

var handler = new HttpClientHandler();
        if (handler.SupportsAutomaticDecompression)
        {
            handler.AutomaticDecompression = DecompressionMethods.GZip |
                                             DecompressionMethods.Deflate;
        }
        client = new HttpClient(handler);

var content = new FormUrlEncodedContent(valoresPost);
var response = await client.PostAsync(url, content);
        
                var contenidoPdf = await response.Content.ReadAsByteArrayAsync();
             
like image 2
Fernando Vera Avatar answered Nov 04 '22 16:11

Fernando Vera