14
votes

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.

4
How are you calling this method? - Yuval Itzchakov
@YuvalItzchakov...I'm using Fiddler - Big Daddy
Is this method a Web API action? - Yuval Itzchakov
@YuvalItzchakov...Yes, it's a method on a Web Api controller - Big Daddy
@YuvalItzchakov...Please see my edit, thanks - Big Daddy

4 Answers

6
votes

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();
2
votes

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.

2
votes

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

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