How does one read the request body in ASP.NET? I'm using the REST Client add-on for Firefox to form a GET request for a resource on a site I'm hosting locally, and in the Request Body I'm just putting the string "test" to try to read it on the server.
In the server code (which is a very simple MVC action) I have this:
var reader = new StreamReader(Request.InputStream);
var inputString = reader.ReadToEnd();
But when I debug into it, inputString
is always empty. I'm not sure how else (such as in FireBug) to confirm that the request body is indeed being sent properly, I guess I'm just assuming that the add-on is doing that correctly. Maybe I'm reading the value incorrectly?
Maybe I'm misremembering my schooling, but I think GET requests don't actually have a body. This page states.
The HTML specifications technically define the difference between "GET" and "POST" so that former means that form data is to be encoded (by a browser) into a URL while the latter means that the form data is to appear within a message body.
So maybe you're doing things correctly, but you have to POST data in order to have a message body?
Update
In response to your comment, the most "correct" RESTful way would be to send each of the values as its own parameter:
site.com/MyController/MyAction?id=1&id=2&id=3...
Then your action will auto-bind these if you give it an array parameter by the same name:
public ActionResult MyAction(int[] id) {...}
Or if you're a masochist you can maybe try pulling the values out of Request.QueryString
one at a time.
I was recently reminded of this old question, and wanted to add another answer for completeness based on more recent implementations in my own work.
For reference, I've blogged on the subject recently.
Essentially, the heart of this question was, "How can I pass larger and more complex search criteria to a resource to GET a filtered list of objects?" And it ended up boiling down to two choices:
- A bunch of GET query string parameters
- A POST with a DTO in the request body
The first option isn't ideal, because implementation is ugly and the URL will likely exceed a maximum length at some point. The second option, while functional, just didn't sit right with me in a "RESTful" sense. After all, I'm GETting data, right?
However, keep in mind that I'm not just GETting data. I'm creating a list of objects. Each object already exists, but the list itself doesn't. It's a brand new thing, created by issuing search/filter criteria to the complete repository of objects on the server. (After all, remember that a collection of objects is still, itself, an object.)
It's a purely semantic difference, but a decidedly important one. Because, at its simplest, it means I can comfortably use POST to issue these search criteria to the server. The response is data which I receive, so I'm "getting" data. But I'm not "GETting" data in the sense that I'm actually performing an act of creation, creating a new instance of a list of objects which happens to be composed of pre-existing elements.
I'll fully admit that the limitation was never technical, it was just semantic. It just never "sat right" with me. A non-technical problem demands a non-technical solution, in this case a semantic one. Looking at the problem from a slightly different semantic viewpoint resulted in a much cleaner solution, which happened to be the solution I ended up using in the first place.
Aside from the GET/POST issue, I did discover that you need to set the Request.InputStream position back to the start. Thanks to this answer I found.
Specifically the comment
Request.InputStream // make sure to reset the Position after reading or later reads may fail
Which I translated into
Request.InputStream.Seek(0,0)
I would try using the HttpClient (available via Nuget) for doing this type of thing. Its so much easier than the System.Net objects
Direct reading from the Request.InputStream dangerous because when re-reading will get null even if the data exists. This is verified in practice.
Reliable reading is performed as follows:
/*Returns a string representing the content of the body
of the HTTP-request.*/
public static string GetFromBodyString(this HttpRequestBase request)
{
string result = string.Empty;
if (request == null || request.InputStream == null)
return result;
request.InputStream.Position = 0;
/*create a new thread in the memory to save the original
source form as may be required to read many of the
body of the current HTTP- request*/
using (MemoryStream memoryStream = new MemoryStream())
{
request.InputStream.CopyToMemoryStream(memoryStream);
using (StreamReader streamReader = new StreamReader(memoryStream))
{
result = streamReader.ReadToEnd();
}
}
return result;
}
/*Copies bytes from the given stream MemoryStream and writes
them to another stream.*/
public static void CopyToMemoryStream(this Stream source, MemoryStream destination)
{
if (source.CanSeek)
{
int pos = (int)destination.Position;
int length = (int)(source.Length - source.Position) + pos;
destination.SetLength(length);
while (pos < length)
pos += source.Read(destination.GetBuffer(), pos, length - pos);
}
else
source.CopyTo((Stream)destination);
}