Receive byte array over HTTP in an ASP.NET 2.0 app - asp.net

Trying to transfer a byte array to an ASP.NET 2.0 app in a light-weight manner without using SOAP. I decided to use a generic HTTP handler (.ashx) that interprets the HTTP request body as a Base64 string, decoding it to a byte array and saving to disk.
<%# WebHandler Language="C#" Class="PdfPrintService" %>
using System;
using System.Web;
public class PdfPrintService : IHttpHandler {
public void ProcessRequest (HttpContext context) {
string body;
using (System.IO.StreamReader reader =
new System.IO.StreamReader(context.Request.InputStream))
{
body = reader.ReadToEnd();
}
byte[] bytes = System.Convert.FromBase64String(body);
String filePath = System.IO.Path.GetTempFileName() + ".pdf";
System.IO.File.WriteAllBytes(filePath, bytes);
// Print file.
XyzCompany.Printing.PrintUtility.PrintFile(filePath);
}
public bool IsReusable {
get {
return false;
}
}
}
The client application (an iOS app in my case) will simply have to encode the bytes as Base64 and post them to the URL of this generic handler (ashx).
I imagine there is a better, more orthodox way to do this. Any ideas are appreciated!

The thing that comes to mind is POST & GET Requests handled through classes like the HttpWebResponse class. http://msdn.microsoft.com/en-us/library/system.net.httpwebresponse%28v=vs.71%29.aspxx
You can have your iOS app try to POST to the ASP.NET app, which is then set up to receive the POST and parse it for your byte array, which you'd include. More, or less, this is how some data was sent across the internet before SOAP.. all SOAP is is a schema for these types of requests.

Related

Asp.net Webforms: Create webhook receiver for typeform

How I can make a webhook receiver with asp.net web forms for Typeform and how will i get data on my app whenever someone submits my form.
To expose an endpoint to receive a POST request, I would create a HTTP handler, in the asp.net world, known as a "Generic Web handler", which is a file with a .ashx extension.
You can see a guide on how to create one here:
https://briancaos.wordpress.com/2009/02/13/the-ashx-extension-writing-your-own-httphandler/
The implementation could look something like this:
using System.Web;
using Newtonsoft.Json.Linq; // From https://www.newtonsoft.com/json
namespace MyNamespace
{
public class MyClass : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string body = String.Empty;
context.Request.InputStream.Position = 0;
using (var inputStream = new StreamReader(context.Request.InputStream))
{
body = inputStream.ReadToEnd();
}
dynamic json = JObject.Parse(body);
// Access the webhook payload data ie, get first answer:
var answers = json.form_response.answers;
Console.WriteLine(answers)
context.Response.StatusCode = 200;
context.Response.End();
}
public bool IsReusable
{
get { return true; }
}
}
}
You can find a complete overview of different HTTP handlers here:
https://msdn.microsoft.com/en-us/library/bb398986.aspx?f=255&MSPPError=-2147217396

Returning binary file from controller in ASP.NET Web API

I'm working on a web service using ASP.NET MVC's new WebAPI that will serve up binary files, mostly .cab and .exe files.
The following controller method seems to work, meaning that it returns a file, but it's setting the content type to application/json:
public HttpResponseMessage<Stream> Post(string version, string environment, string filetype)
{
var path = #"C:\Temp\test.exe";
var stream = new FileStream(path, FileMode.Open);
return new HttpResponseMessage<Stream>(stream, new MediaTypeHeaderValue("application/octet-stream"));
}
Is there a better way to do this?
Try using a simple HttpResponseMessage with its Content property set to a StreamContent:
// using System.IO;
// using System.Net.Http;
// using System.Net.Http.Headers;
public HttpResponseMessage Post(string version, string environment,
string filetype)
{
var path = #"C:\Temp\test.exe";
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
return result;
}
A few things to note about the stream used:
You must not call stream.Dispose(), since Web API still needs to be able to access it when it processes the controller method's result to send data back to the client. Therefore, do not use a using (var stream = …) block. Web API will dispose the stream for you.
Make sure that the stream has its current position set to 0 (i.e. the beginning of the stream's data). In the above example, this is a given since you've only just opened the file. However, in other scenarios (such as when you first write some binary data to a MemoryStream), make sure to stream.Seek(0, SeekOrigin.Begin); or set stream.Position = 0;
With file streams, explicitly specifying FileAccess.Read permission can help prevent access rights issues on web servers; IIS application pool accounts are often given only read / list / execute access rights to the wwwroot.
For Web API 2, you can implement IHttpActionResult. Here's mine:
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
class FileResult : IHttpActionResult
{
private readonly string _filePath;
private readonly string _contentType;
public FileResult(string filePath, string contentType = null)
{
if (filePath == null) throw new ArgumentNullException("filePath");
_filePath = filePath;
_contentType = contentType;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(File.OpenRead(_filePath))
};
var contentType = _contentType ?? MimeMapping.GetMimeMapping(Path.GetExtension(_filePath));
response.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
return Task.FromResult(response);
}
}
Then something like this in your controller:
[Route("Images/{*imagePath}")]
public IHttpActionResult GetImage(string imagePath)
{
var serverPath = Path.Combine(_rootPath, imagePath);
var fileInfo = new FileInfo(serverPath);
return !fileInfo.Exists
? (IHttpActionResult) NotFound()
: new FileResult(fileInfo.FullName);
}
And here's one way you can tell IIS to ignore requests with an extension so that the request will make it to the controller:
<!-- web.config -->
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
For those using .NET Core:
You can make use of the IActionResult interface in an API controller method, like so.
[HttpGet("GetReportData/{year}")]
public async Task<IActionResult> GetReportData(int year)
{
// Render Excel document in memory and return as Byte[]
Byte[] file = await this._reportDao.RenderReportAsExcel(year);
return File(file, "application/vnd.openxmlformats", "fileName.xlsx");
}
This example is simplified, but should get the point across. In .NET Core this process is so much simpler than in previous versions of .NET - i.e. no setting response type, content, headers, etc.
Also, of course the MIME type for the file and the extension will depend on individual needs.
Reference: SO Post Answer by #NKosi
While the suggested solution works fine, there is another way to return a byte array from the controller, with response stream properly formatted :
In the request, set header "Accept: application/octet-stream".
Server-side, add a media type formatter to support this mime type.
Unfortunately, WebApi does not include any formatter for "application/octet-stream". There is an implementation here on GitHub: BinaryMediaTypeFormatter (there are minor adaptations to make it work for webapi 2, method signatures changed).
You can add this formatter into your global config :
HttpConfiguration config;
// ...
config.Formatters.Add(new BinaryMediaTypeFormatter(false));
WebApi should now use BinaryMediaTypeFormatter if the request specifies the correct Accept header.
I prefer this solution because an action controller returning byte[] is more comfortable to test. Though, the other solution allows you more control if you want to return another content-type than "application/octet-stream" (for example "image/gif").
For anyone having the problem of the API being called more than once while downloading a fairly large file using the method in the accepted answer, please set response buffering to true
System.Web.HttpContext.Current.Response.Buffer = true;
This makes sure that the entire binary content is buffered on the server side before it is sent to the client. Otherwise you will see multiple request being sent to the controller and if you do not handle it properly, the file will become corrupt.
The overload that you're using sets the enumeration of serialization formatters. You need to specify the content type explicitly like:
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
You could try
httpResponseMessage.Content.Headers.Add("Content-Type", "application/octet-stream");
You can try the following code snippet
httpResponseMessage.Content.Headers.Add("Content-Type", "application/octet-stream");
Hope it will work for you.

how do I handle and deflate a GZipped form post in asp.net MVC?

I have an iPad app that submits orders to an ASP.NET MVC web site via form post. It is posting JSON which can be fairly large for a mobile device to send (200~300K) under certain conditions. I can GZip the form post but then my asp.net mvc chokes on the gzipped content.
How can I handle a GZipped form post in asp.net mvc?
UPDATE:
Darin's answer puts me on the right track but I still have no idea how to do what he suggests, so here is where I am at:
Have this code to decompress a string:
http://dotnet-snippets.com/dns/compress-and-decompress-strings-SID612.aspx
And I get the string like so:
StreamReader reader = new StreamReader(Request.InputStream);
string encodedString = reader.ReadToEnd();
but this gives me the error:
The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or a non-white space character among the padding characters.
EDIT - COMPLETED CODE
I am using asp.net MVC and this is working great for me. I also had to deal with some other encoding that happens when my gzipping occurs:
[Authorize]
[HttpPost]
[ValidateInput(false)]
public ActionResult SubmitOrder()
{
GZipStream zipStream = new GZipStream(Request.InputStream, CompressionMode.Decompress);
byte[] streamBytes = ReadAllBytes(zipStream);
var result = Convert.ToBase64String(streamBytes);
string sample = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(result));
string escaped = Uri.UnescapeDataString(sample);
// escaped now has my form values as a string like so: var1=value1&var2=value2&ect...
//more boring code
}
public static byte[] ReadAllBytes(Stream input)
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
You can do this without a custom model binder. Write an Action that accepts HttpPostedFileBase, i.e, treat this as a file upload.
[HttpPost]
public ActionResult UploadCompressedJSON(HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
GZipStream zipStream = new GZipStream(file.InputStream, CompressionMode.Decompress);
byte[] streamBytes = ReadAllBytes(zipStream);
var result = Convert.ToBase64String(streamBytes);
}
return RedirectToAction("Index");
}
You are going to need to change your client side code to send a file upload request but that should be fairly easy. For example you can look at this code.
How can I handle a GZipped form post in asp.net mvc?
You could write a custom model binder that will directly read the Request.InputStream, unzip it and then parse the contents and instantiate some view model you want to bind to.
Use the System.IO.Compression.GZipStream class.
Codeproject example

Increase Http Runtime MaxRequestLength from C# code

How can I increase
from my C# code ? I can't do this in Web.config, My application is created to deploy web
application in IIS.
Take a look at http://bytes.com/topic/asp-net/answers/346534-how-i-can-get-httpruntime-section-page
There's how you get access to an instance of HttpRuntimeSection. Then modify the property MaxRequestLength.
An alternative to increasing the max request length is to create an IHttpModule implementation. In the BeginRequest handler, grab the HttpWorkerRequest to process it entirely in your own code, rather than letting the default implementation handle it.
Here is a basic implementation that will handle any request posted to any file called "dropbox.aspx" (in any directory, whether it exists or not):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Example
{
public class FileUploadModule: IHttpModule
{
#region IHttpModule Members
public void Dispose() {}
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}
#endregion
void context_BeginRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
HttpContext context = application.Context;
string filePath = context.Request.FilePath;
string fileName = VirtualPathUtility.GetFileName( filePath );
string fileExtension = VirtualPathUtility.GetExtension(filePath);
if (fileName == "dropbox.aspx")
{
IServiceProvider provider = (IServiceProvider)context;
HttpWorkerRequest wr = (HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest));
//HANDLE REQUEST HERE
//Grab data from HttpWorkerRequest instance, as reflected in HttpRequest.GetEntireRawContent method.
application.CompleteRequest(); //bypasses all other modules and ends request immediately
}
}
}
}
You could use something like that, for example, if you're implementing a file uploader, and you want to process the multi-part content stream as it's received, so you can perform authentication based on posted form fields and, more importantly, cancel the request on the server-side before you even receive any file data. That can save a lot of time if you can determine early on in the stream that the upload is not authorized or the file will be too big or exceed the user's disk quota for the dropbox.
This is impossible to do with the default implementation, because trying to access the Form property of the HttpRequest will cause it to try to receive the entire request stream, complete with MaxRequestLength checks. The HttpRequest object has a method called "GetEntireRawContent" which is called as soon as access to the content is needed. That method starts with the following code:
HttpRuntimeSection httpRuntime = RuntimeConfig.GetConfig(this._context).HttpRuntime;
int maxRequestLengthBytes = httpRuntime.MaxRequestLengthBytes;
if (this.ContentLength > maxRequestLengthBytes)
{
if (!(this._wr is IIS7WorkerRequest))
{
this.Response.CloseConnectionAfterError();
}
throw new HttpException(SR.GetString("Max_request_length_exceeded"), null, 0xbbc);
}
The point is that you'll be skipping that code and implementing your own custom content length check instead. If you use Reflector to look at the rest of "GetEntireRawContent" to use it as a model implementation, you'll see that it basically does the following: calls GetPreloadedEntityBody, checks if there's more to load by calling IsEntireEntityBodyIsPreloaded, and finally loops through calls to ReadEntityBody to get the rest of the data. The data read by GetPreloadedEntityBody and ReadEntityBody are dumped into a specialized stream, which automatically uses a temporary file as a backing store once it crosses a size threshold.
A basic implementation would look like this:
MemoryStream request_content = new MemoryStream();
int bytesRemaining = wr.GetTotalEntityBodyLength() - wr.GetPreloadedEntityBodyLength();
byte[] preloaded_data = wr.GetPreloadedEntityBody();
if (preloaded_data != null)
request_content.Write( preloaded_data, 0, preloaded_data.Length );
if (!wr.IsEntireEntityBodyIsPreloaded()) //not a type-o, they use "Is" redundantly in the
{
int BUFFER_SIZE = 0x2000; //8K buffer or whatever
byte[] buffer = new byte[BUFFER_SIZE];
while (bytesRemaining > 0)
{
bytesRead = wr.ReadEntityBody(buffer, Math.Min( bytesRemaining, BUFFER_SIZE )); //Read another set of bytes
bytesRemaining -= bytesRead; // Update the bytes remaining
request_content.Write( buffer, 0, bytesRead ); // Write the chunks to the backing store (memory stream or whatever you want)
}
if (bytesRead == 0) //failure to read or nothing left to read
break;
}
At that point, you'll have your entire request in a MemoryStream. However, rather than download the entire request like that, what I've done is offload that "bytesRemaining" loop into a class with a "ReadEnough( int max_index )" method that is called on demand from a specialized MemoryStream that "loads enough" into the stream to access the byte being accessed.
Ultimately, that architecture allows me to send the request directly to a parser that reads from the memory stream, and the memory stream automatically loads more data from the worker request as needed. I've also implemented events so that as each element of the multi-part content stream is parsed, it fires events when each new part is identified and when each part is completely received.
You can do that in the web.config
<httpRuntime maxRequestLength="11000" />
11000 == 11 mb

Capturing SOAP requests to an ASP.NET ASMX web service

Consider the requirement to log incoming SOAP requests to an ASP.NET ASMX web service. The task is to capture the raw XML being sent to the web service.
The incoming message needs to be logged for debug inspection. The application already has its own logging library in use, so the ideal usage would be something like this:
//string or XML, it doesn't matter.
string incomingSoapRequest = GetSoapRequest();
Logger.LogMessage(incomingSoapRequest);
Are there any easy solutions to capture the raw XML of the incoming SOAP requests?
Which events would you handle to get access to this object and the relevant properties?
Is there anyway IIS can capture the incoming request and push to a log?
You can also implement by placing the code in Global.asax.cs
protected void Application_BeginRequest(object sender, EventArgs e)
{
// Create byte array to hold request bytes
byte[] inputStream = new byte[HttpContext.Current.Request.ContentLength];
// Read entire request inputstream
HttpContext.Current.Request.InputStream.Read(inputStream, 0, inputStream.Length);
//Set stream back to beginning
HttpContext.Current.Request.InputStream.Position = 0;
//Get XML request
string requestString = ASCIIEncoding.ASCII.GetString(inputStream);
}
I have a Utility method in my web service that I use to capture the request when something happens that I am not expecting like a unhandled exception.
/// <summary>
/// Captures raw XML request and writes to FailedSubmission folder.
/// </summary>
internal static void CaptureRequest()
{
const string procName = "CaptureRequest";
try
{
log.WarnFormat("{0} - Writing XML request to FailedSubmission folder", procName);
byte[] inputStream = new byte[HttpContext.Current.Request.ContentLength];
//Get current stream position so we can set it back to that after logging
Int64 currentStreamPosition = HttpContext.Current.Request.InputStream.Position;
HttpContext.Current.Request.InputStream.Position = 0;
HttpContext.Current.Request.InputStream.Read(inputStream, 0, HttpContext.Current.Request.ContentLength);
//Set back stream position to original position
HttpContext.Current.Request.InputStream.Position = currentStreamPosition;
string xml = ASCIIEncoding.ASCII.GetString(inputStream);
string fileName = Guid.NewGuid().ToString() + ".xml";
log.WarnFormat("{0} - Request being written to filename: {1}", procName, fileName);
File.WriteAllText(Configuration.FailedSubmissionsFolder + fileName, xml);
}
catch
{
}
}
Then in web.config I store several AppSetting values that define what level I want to use to capture the request.
<!-- true/false - If true will write to an XML file the raw request when any Unhandled exception occurrs -->
<add key="CaptureRequestOnUnhandledException" value="true"/>
<!-- true/false - If true will write to an XML file the raw request when any type of error is returned to the client-->
<add key="CaptureRequestOnAllFailures" value="false"/>
<!-- true/false - If true will write to an XML file the raw request for every request to the web service -->
<add key="CaptureAllRequests" value="false"/>
Then in my Application_BeginRequest I have it modified like so. Note that Configuration is a static class I create to read properties from web.config and other areas.
protected void Application_BeginRequest(object sender, EventArgs e)
{
if(Configuration.CaptureAllRequests)
{
Utility.CaptureRequest();
}
}
One way to capture the raw message is to use SoapExtensions.
An alternative to SoapExtensions is to implement IHttpModule and grab the input stream as it's coming in.
public class LogModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += this.OnBegin;
}
private void OnBegin(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
HttpContext context = app.Context;
byte[] buffer = new byte[context.Request.InputStream.Length];
context.Request.InputStream.Read(buffer, 0, buffer.Length);
context.Request.InputStream.Position = 0;
string soapMessage = Encoding.ASCII.GetString(buffer);
// Do something with soapMessage
}
public void Dispose()
{
throw new NotImplementedException();
}
}
You know that you dont actually need to create a HttpModule right?
You can also read the contents of the Request.InputStream from within your asmx WebMethod.
Here is an article I wrote on this approach.
Code is as follows:
using System;
using System.Collections.Generic;
using System.Web;
using System.Xml;
using System.IO;
using System.Text;
using System.Web.Services;
using System.Web.Services.Protocols;
namespace SoapRequestEcho
{
[WebService(
Namespace = "http://soap.request.echo.com/",
Name = "SoapRequestEcho")]
public class EchoWebService : WebService
{
[WebMethod(Description = "Echo Soap Request")]
public XmlDocument EchoSoapRequest(int input)
{
// Initialize soap request XML
XmlDocument xmlSoapRequest = new XmlDocument();
// Get raw request body
Stream receiveStream = HttpContext.Current.Request.InputStream;
// Move to beginning of input stream and read
receiveStream.Position = 0;
using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8))
{
// Load into XML document
xmlSoapRequest.Load(readStream);
}
// Return
return xmlSoapRequest;
}
}
}
There are no easy ways to do this. You will have to implement a SoapExtension. The example at the previous link shows an extension that can be used to log the data.
If you had been using WCF, then you could simply set the configuration to produce message logs.
According to Steven de Salas, you can use the Request.InputStream property within the webmethod. I have not tried this, but he says that it works.
I would want to test this with both http and https, and with and without other SoapExtensions running at the same time. These are things that might affect what kind of stream the InputStream is set to. Some streams cannot seek, for instance, which might leave you with a stream positioned after the end of the data, and which you cannot move to the beginning.

Resources