Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correctly reading HttpRequest body asynchronously in NetCore web app

I am trying to get POST request body as string in C#. I wrote this:

    protected async Task<string> readBodyAsync(HttpRequest req)
    {
        // turns out this is always false...
        if(req.Body.CanSeek)
            req.Body.Seek(0, SeekOrigin.Begin);
        // string buffer
        string str = "";
        // I wouldn't expect to have to do this in 2017
        byte[] buffer = new byte[255];
        int offset = 0;
        int lastLen = 0;
        // On second iteration, CanRead is true but ReadAsync throws System.ArgumentException
        while ( req.Body.CanRead && (lastLen = await req.Body.ReadAsync(buffer, offset, buffer.Length))!=0)
        {
            offset += lastLen;
            // This also adds all the \0 characters from the byte buffer, instead of treating like
            // normal C string
            str += System.Text.Encoding.UTF8.GetString(buffer);
        }
        // This never executes due to the System.ArgumentException
        return str;
    }

The issues:

  • On second iteration, req.Body.CanRead is true, but calling read causes System.ArgumentException
  • All is read on first iteration already. Not only that, System.Text.Encoding.UTF8.GetString adds all remaining zeroes from the buffer to the string.

I handle the request like this:

    app.Run(async (context) =>
    {
        if(context.Request.Method.ToLower() == "post" && context.Request.Path.ToString().EndsWith("/ajax"))
        {
            string requestText = await readBodyAsync(context.Request);
            /// Parsing of the JSON in requestText should be here
               // but I can't get that text
            await context.Response.WriteAsync("{data: \"AJAX TEST\"}");
        }
        else
        {
            await context.Response.WriteAsync("Hello World! And go away. Invalid request.");
        }  
    });
like image 781
Tomáš Zato - Reinstate Monica Avatar asked Sep 26 '17 15:09

Tomáš Zato - Reinstate Monica


2 Answers

    private async Task<string> StreamToStringAsync(HttpRequest request)
    {
        using (var sr = new StreamReader(request.Body))
        {
            return await sr.ReadToEndAsync();
        }
    }
like image 119
Timothy Macharia Avatar answered Oct 02 '22 05:10

Timothy Macharia


Apparently there's a more elegant way that also works:

    protected async Task<string> readBodyAsync(HttpRequest req)
    {
        StreamReader r = new StreamReader(req.Body);
        return await r.ReadToEndAsync();
    }
like image 41
Tomáš Zato - Reinstate Monica Avatar answered Oct 02 '22 06:10

Tomáš Zato - Reinstate Monica