46

I'm trying to capture the raw request data for accountability and want to pull the request body content out of the Request object.

I've seen suggestions doing a Request.InputStream, but this method is not available on the Request object.

Any idea of how to get a string representation of the Request.Content body?

Watch variable

abatishchev
  • 95,331
  • 80
  • 293
  • 426
Mark Kadlec
  • 7,478
  • 15
  • 59
  • 94

5 Answers5

84

In your comment on @Kenneth's answer you're saying that ReadAsStringAsync() is returning empty string.

That's because you (or something - like model binder) already read the content, so position of internal stream in Request.Content is on the end.

What you can do is this:

public static string GetRequestBody()
{
    var bodyStream = new StreamReader(HttpContext.Current.Request.InputStream);
    bodyStream.BaseStream.Seek(0, SeekOrigin.Begin);
    var bodyText = bodyStream.ReadToEnd();
    return bodyText;
}
Gh61
  • 8,457
  • 4
  • 26
  • 37
  • this code working fine in HTTP , but when i use HTTPS connection it gives empty string in bodytext , can you help me on this ? – Thom Apr 16 '18 at 13:27
  • Weird, for me it's working even on HTTPS connection. – Gh61 Apr 17 '18 at 08:12
  • Getting empty string when i call from HTTPS , Did i need to add anything in web config ? – Thom Apr 17 '18 at 09:52
  • 1
    In case you are wondering the ".Current"-property is not available from the HttpContext, it was removed due to bad architecture. Explanation can be found here: https://stackoverflow.com/a/38574489/4100453 – Alexander Falk Aug 08 '19 at 06:42
  • I created [an extension method for this answer](https://stackoverflow.com/a/70516793/569302). – Jesus is Lord Dec 29 '21 at 08:16
42

You can get the raw data by calling ReadAsStringAsAsync on the Request.Content property.

string result = await Request.Content.ReadAsStringAsync();

There are various overloads if you want it in a byte or in a stream. Since these are async-methods you need to make sure your controller is async:

public async Task<IHttpActionResult> GetSomething()
{
    var rawMessage = await Request.Content.ReadAsStringAsync();
    // ...
    return Ok();
}

EDIT: if you're receiving an empty string from this method, it means something else has already read it. When it does that, it leaves the pointer at the end. An alternative method of doing this is as follows:

public IHttpActionResult GetSomething()
{
    var reader = new StreamReader(Request.Body);
    reader.BaseStream.Seek(0, SeekOrigin.Begin); 
    var rawMessage = reader.ReadToEnd();

    return Ok();
}

In this case, your endpoint doesn't need to be async (unless you have other async-methods)

Ahmed El-Hansy
  • 141
  • 1
  • 14
Kenneth
  • 27,326
  • 6
  • 58
  • 82
  • 9
    Thanks Kenneth, but the rawMessage returns an empty string, have I done it right? I can see the Content contains data, why is the ReadAsStringAsync() returning an empty string? – Mark Kadlec Feb 24 '16 at 02:40
  • same issue as @MarkKadlec , this solution doesn't work – Dan Hastings Jul 12 '18 at 14:03
  • 1
    Check if you already read it before `ReadAsStringAsync`. It is a stream that this method will read to the end and leave the pointer there. – Miro J. Dec 19 '19 at 22:04
  • 1
    You cannot seek on the Request.Body stream. – zwcloud Jun 22 '20 at 05:34
  • If you are debugging and want to view the content as a string in the Watch window. Use this: `Request.Content.ReadAsStringAsync().Result` – David Klempfner Jan 11 '21 at 00:30
22

For other future users who do not want to make their controllers asynchronous, or cannot access the HttpContext, or are using dotnet core (this answer is the first I found on Google trying to do this), the following worked for me:

[HttpPut("{pathId}/{subPathId}"),
public IActionResult Put(int pathId, int subPathId, [FromBody] myViewModel viewModel)
{

    var body = new StreamReader(Request.Body);
    //The modelbinder has already read the stream and need to reset the stream index
    body.BaseStream.Seek(0, SeekOrigin.Begin); 
    var requestBody = body.ReadToEnd();
    //etc, we use this for an audit trail
}
C Bauer
  • 4,847
  • 3
  • 31
  • 60
  • 4
    Important: If your method has no arguments, then don't use `Seek` otherwise you'll get "NotSupportedException: Specified method is not supported. Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestStream.Seek" – JustAMartin Aug 22 '19 at 19:39
5

If you need to both get the raw content from the request, but also need to use a bound model version of it in the controller, you will likely get this exception.

NotSupportedException: Specified method is not supported. 

For example, your controller might look like this, leaving you wondering why the solution above doesn't work for you:

public async Task<IActionResult> Index(WebhookRequest request)
{
    using var reader = new StreamReader(HttpContext.Request.Body);

    // this won't fix your string empty problems
    // because exception will be thrown
    reader.BaseStream.Seek(0, SeekOrigin.Begin); 
    var body = await reader.ReadToEndAsync();

    // Do stuff
}

You'll need to take your model binding out of the method parameters, and manually bind yourself:

public async Task<IActionResult> Index()
{
    using var reader = new StreamReader(HttpContext.Request.Body);

    // You shouldn't need this line anymore.
    // reader.BaseStream.Seek(0, SeekOrigin.Begin);

    // You now have the body string raw
    var body = await reader.ReadToEndAsync();

    // As well as a bound model
    var request = JsonConvert.DeserializeObject<WebhookRequest>(body);
}

It's easy to forget this, and I've solved this issue before in the past, but just now had to relearn the solution. Hopefully my answer here will be a good reminder for myself...

Chris
  • 7,546
  • 9
  • 61
  • 95
  • This helped me on the right path. Note: I was using dotnet core and had to remove `[FromBody] myObject` from the header. This was reading the stream and putting the seek at the end. – benk Apr 20 '22 at 11:06
0

Here's this answer as an extension method:

using System.IO;
using System.Text;

namespace System.Web.Http
{
    public static class ApiControllerExtensions
    {
        public static string GetRequestBody(this ApiController controller)
        {
            using (var stream = new MemoryStream())
            {
                var context = (HttpContextBase)controller.Request.Properties["MS_HttpContext"];
                context.Request.InputStream.Seek(0, SeekOrigin.Begin);
                context.Request.InputStream.CopyTo(stream);
                var requestBody = Encoding.UTF8.GetString(stream.ToArray());
                return requestBody;
            }
        }
    }
}

Jesus is Lord
  • 14,323
  • 10
  • 60
  • 90