Getting form data from HttpListenerRequest
Asked Answered
E

3

18

I have a HttpListenerRequest which was initiated from a html <form> that was posted. I need to know how to get the posted form values + the uploaded files. Does anyone know of an example to save me the time doing it for myself? I've had a google around but not found anything of use.

Eigenfunction answered 4/3, 2011 at 18:11 Comment(0)
E
1

Odd that an accepted answer with full source code + a link to download a working demo would result in a negative score and votes to be deleted. If I were the kind of person who cared about my score I'd have deleted this answer, and then nobody would have benefited. Psychology, eh :)

I found a few examples of web servers for MonoTouch but none of them parsed the data sent in a POST request. I looked around the web and was unable to find any examples of how to achieve this. So now that I’ve written it myself I’ve decided to share my own implementation. This includes not only the code for processing the form post data but also for registering request handlers etc.

Here is an example of how you would use the web server

 public BookUploadViewController() 
   : base("BookUploadViewController", null)
 {
   RequestHandler = new DefaultRequestHandler();
   var defaultActionHandlerFactory = new DefaultActionHandlerFactory();
   RegisterActionHandlers(defaultActionHandlerFactory);
   RequestHandler.AddActionHandlerFactory(defaultActionHandlerFactory);

   WebServer = new EmbeddedWebServer(RequestHandler);
 }

 void RegisterActionHandlers(DefaultActionHandlerFactory factory)
 {
   factory.RegisterHandler(
     request => request.RawUrl == "/",
     request => new IndexActionHandler(request)
     );
   factory.RegisterHandler(
     request => 
       string.Compare(request.RawUrl, "/Upload", true) == 0 && 
       string.Compare(request.HttpMethod, "POST", true) == 0,
     request => new UploadActionHandler(request)
     );
 }

 public override void ViewDidAppear(bool animated)
 {
   base.ViewDidAppear(animated);
   StatusLabel.Text = string.Format("Server listening on\r\nhttp://{0}:8080", GetIPAddress ());
   WebServer.Start(8080);
 }

 public override void ViewDidDisappear (bool animated)
 {
   base.ViewDidDisappear(animated);
   WebServer.Stop();
 }

And here are two app specific examples of request handlers

 class IndexActionHandler : DefaultActionHandler
 {
   public IndexActionHandler(HttpListenerRequest request)
     : base(request)
   {
   }

   public override ActionResult Execute()
   {
     var result = new HtmlResult();
     result.AppendLine("<html>");
     result.AppendLine("<body>");
     result.AppendLine("<h1>Upload an image</h1>");
     result.AppendLine("<form action='/Upload' enctype='multipart/form-data' method='post'>");
     result.AppendLine ("<input name='Image' type='file'/><br/>");
     result.AppendLine("<input name='Upload' type='submit' text='Upload'/>");
     result.AppendLine("</form>");
     result.AppendLine("</body>");
     result.AppendLine("</html>");
     return result;
   }
 }

 class UploadActionHandler : DefaultActionHandler
 {
   public UploadActionHandler(HttpListenerRequest request)
     : base(request)
   {
   }

   public override ActionResult Execute()
   {
     string errorMessage = null;
     var file = FormData.GetFile("Image");
     if (file == null 
       || file.FileData == null 
       || file.FileData.Length == 0 
       || string.IsNullOrEmpty(file.FileName))
       errorMessage = "No image uploaded";

     if (errorMessage == null)
       ProcessFile(file);

     var result = new HtmlResult();
     result.AppendLine("<html>");
     result.AppendLine("<body>");
     if (errorMessage == null)
       result.AppendLine("<h1>File uploaded successfully</h1>");
     else
     {
       result.AppendLine("<h1>Error</h1>");
       result.AppendLine("<h2>" + errorMessage + "</h2>");
     }
     result.AppendLine("</body>");
     result.AppendLine("</html>");
     return result;
   }

   void ProcessFile(MultiPartStreamFileValue postedFile)
   {
     string fileName = "Where to save the file";
     using (var fileStream = 
       new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
     {
       fileStream.Write(postedFile.FileData, 0, postedFile.FileData.Length);
     }
   }
 }

You can download the source code here https://sites.google.com/site/mrpmorris/EmbeddedWebServerMT.zip

Eigenfunction answered 2/4, 2011 at 17:59 Comment(6)
Public domain / do what you like license :)Eigenfunction
Sadly this link is now dead.Acanthocephalan
Happily I have changed the URL to another copy ;-)Eigenfunction
"EmbeddedWebServerMT.zip"? Don't link to zip files. There are better alternatives to share code.Olympic
Then why is that link in there?Olympic
The source code needed to answer the question is in the answer. The zip contains a full working example.Eigenfunction
E
28

The main thing to understand is that HttpListener is a low level tool to work with http requests. All post data is in HttpListenerRequest.InputStream stream. Suppose we have a form like that:

<form method=\"post\" enctype=\"multipart/form-data\"><input id=\"fileUp\" name=\"fileUpload\" type=\"file\" /><input type=\"submit\" /></form>

Now we want to see the post data. Lets implement a method to do this:

public static string GetRequestPostData(HttpListenerRequest request)
{
  if (!request.HasEntityBody)
  {
    return null;
  }
  using (System.IO.Stream body = request.InputStream) // here we have data
  {
    using (var reader = new System.IO.StreamReader(body, request.ContentEncoding))
    {
      return reader.ReadToEnd();
    }
  }
}

upload some file and see result:

Content-Disposition: form-data; name="somename"; filename="D:\Test.bmp" 
Content-Type: image/bmp
...here is the raw file data...

Next suppose we have simple form without uploading files:

<form method=\"post\">First name: <input type=\"text\" name=\"firstname\" /><br />Last name: <input type=\"text\" name=\"lastname\" /><input type=\"submit\" value=\"Submit\" /></form>

Let's see the output:

firstname=MyName&lastname=MyLastName

Combined form result:

Content-Disposition: form-data; name="firstname"
My Name
Content-Disposition: form-data; name="somename"; filename="D:\test.xls"
Content-Type: application/octet-stream
...raw file data...

As you can see in case of simple form you can just read InputStream to string and parse post values. If there is a more complex form - you need to perform more complex parsing but it's still can be done. Hope this examples will save your time. Note, that is not always the case to read all stream as a string.

Ensue answered 4/3, 2011 at 19:2 Comment(5)
Thanks. I already knew that much, I just hoped someone knew of an example to save me the time of writing one. Someone must have done this before!Eigenfunction
This is certainly a helpful answer, but I'm finding that with large file transfers I get an OutOfMemoryException with my call to reader.ReadToEnd(). Is there a simple way to stream the file in increments or otherwise solve this problem?Ito
Form parsing code example. In response to the OutOfMemoryException your Form data should not be so large that it blows out memory. You need to check the content type before you attempt to parse the stream data into a form.Stony
I wouldn't wrap body in a using statement. body becomes a reference to request.InputStream, so when the using scope closes, request.InputStream would be explicitly disposed.Cauley
And to save the file data, there's a solution here: #8467203Jacobite
S
2

It's an old thread but I think the demand still applies. In newer versions of .Net (tested on net6.0) it is possible to avoid manual parsing of application/x-www-form-urlencoded content using the standard function System.Web.HttpUtility.ParseQueryString(string content).

One still needs to manually read the content as text from the request input stream, as shown in the accepted answer above.

Somniferous answered 10/1, 2023 at 18:7 Comment(0)
E
1

Odd that an accepted answer with full source code + a link to download a working demo would result in a negative score and votes to be deleted. If I were the kind of person who cared about my score I'd have deleted this answer, and then nobody would have benefited. Psychology, eh :)

I found a few examples of web servers for MonoTouch but none of them parsed the data sent in a POST request. I looked around the web and was unable to find any examples of how to achieve this. So now that I’ve written it myself I’ve decided to share my own implementation. This includes not only the code for processing the form post data but also for registering request handlers etc.

Here is an example of how you would use the web server

 public BookUploadViewController() 
   : base("BookUploadViewController", null)
 {
   RequestHandler = new DefaultRequestHandler();
   var defaultActionHandlerFactory = new DefaultActionHandlerFactory();
   RegisterActionHandlers(defaultActionHandlerFactory);
   RequestHandler.AddActionHandlerFactory(defaultActionHandlerFactory);

   WebServer = new EmbeddedWebServer(RequestHandler);
 }

 void RegisterActionHandlers(DefaultActionHandlerFactory factory)
 {
   factory.RegisterHandler(
     request => request.RawUrl == "/",
     request => new IndexActionHandler(request)
     );
   factory.RegisterHandler(
     request => 
       string.Compare(request.RawUrl, "/Upload", true) == 0 && 
       string.Compare(request.HttpMethod, "POST", true) == 0,
     request => new UploadActionHandler(request)
     );
 }

 public override void ViewDidAppear(bool animated)
 {
   base.ViewDidAppear(animated);
   StatusLabel.Text = string.Format("Server listening on\r\nhttp://{0}:8080", GetIPAddress ());
   WebServer.Start(8080);
 }

 public override void ViewDidDisappear (bool animated)
 {
   base.ViewDidDisappear(animated);
   WebServer.Stop();
 }

And here are two app specific examples of request handlers

 class IndexActionHandler : DefaultActionHandler
 {
   public IndexActionHandler(HttpListenerRequest request)
     : base(request)
   {
   }

   public override ActionResult Execute()
   {
     var result = new HtmlResult();
     result.AppendLine("<html>");
     result.AppendLine("<body>");
     result.AppendLine("<h1>Upload an image</h1>");
     result.AppendLine("<form action='/Upload' enctype='multipart/form-data' method='post'>");
     result.AppendLine ("<input name='Image' type='file'/><br/>");
     result.AppendLine("<input name='Upload' type='submit' text='Upload'/>");
     result.AppendLine("</form>");
     result.AppendLine("</body>");
     result.AppendLine("</html>");
     return result;
   }
 }

 class UploadActionHandler : DefaultActionHandler
 {
   public UploadActionHandler(HttpListenerRequest request)
     : base(request)
   {
   }

   public override ActionResult Execute()
   {
     string errorMessage = null;
     var file = FormData.GetFile("Image");
     if (file == null 
       || file.FileData == null 
       || file.FileData.Length == 0 
       || string.IsNullOrEmpty(file.FileName))
       errorMessage = "No image uploaded";

     if (errorMessage == null)
       ProcessFile(file);

     var result = new HtmlResult();
     result.AppendLine("<html>");
     result.AppendLine("<body>");
     if (errorMessage == null)
       result.AppendLine("<h1>File uploaded successfully</h1>");
     else
     {
       result.AppendLine("<h1>Error</h1>");
       result.AppendLine("<h2>" + errorMessage + "</h2>");
     }
     result.AppendLine("</body>");
     result.AppendLine("</html>");
     return result;
   }

   void ProcessFile(MultiPartStreamFileValue postedFile)
   {
     string fileName = "Where to save the file";
     using (var fileStream = 
       new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
     {
       fileStream.Write(postedFile.FileData, 0, postedFile.FileData.Length);
     }
   }
 }

You can download the source code here https://sites.google.com/site/mrpmorris/EmbeddedWebServerMT.zip

Eigenfunction answered 2/4, 2011 at 17:59 Comment(6)
Public domain / do what you like license :)Eigenfunction
Sadly this link is now dead.Acanthocephalan
Happily I have changed the URL to another copy ;-)Eigenfunction
"EmbeddedWebServerMT.zip"? Don't link to zip files. There are better alternatives to share code.Olympic
Then why is that link in there?Olympic
The source code needed to answer the question is in the answer. The zip contains a full working example.Eigenfunction

© 2022 - 2024 — McMap. All rights reserved.