Background
We have our own javascript framework that communicates with server code to download files, upload files, perform actions, generate documents, etc. The server code currently is WebForms 4.5, however, it is earmarked to be migrated to .NET Core Razor Pages/Web Api. I wanted to update as little as possible in our javascript framework so that it communicated with the server via proper web api calls so that it doesn't have to change when the server is updated to the latest framework.
Currently, the client javascript simply posts a FormData object to default.aspx. The WebForms codebehind uses HttpContext.Current.Request.Form[ "" ] to access string variables and parses them into JObject variables, finally processing the request. The javascript currently uses FormData because the code is shared with different actions that do downloads, uploads, and commands and from what I've read, FormData is required for file uploads.
To do the post, the client javascript uses a XMLHttpRequest object. From experimenting and searching stackoverflow, using $.ajax will not work for downloading files and the only pattern that seems to work reliably is to use XMLHttpRequest (code has been omitted in listings below).
Code
I have the following ApiController class as a Web Api Controller in my C# ASP.NET 4.5* WebForms project:
public class KatAppEndpointsController : ApiController
{
public class KatAppEndpointParameters
{
public string View { get; set; }
public string Inputs { get; set; }
public string Configuration { get; set; }
}
[HttpPost]
[Route( "api/files/download" )]
public string DownloadFile( KatAppEndpointParameters parameters )
{
return "Success";
}
}
Note that above is the start of the default.aspx code behind refactor that downloads a file. So the return "Success"; is simply placeholder code for now that needs to be replaced with actually downloading a file.
My javascript framework has the following:
var fd = new FormData();
fd.append("View", "ViewName" );
fd.append("Inputs", JSON.stringify({ Input1: "Value1", Input2: "Value2" }));
fd.append("Configuration", JSON.stringify( { CustomOptions: "CustomValue" }));
var xhr = new XMLHttpRequest();
xhr.open("POST", "api/files/download", true);
// xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function (): void {
// omitted code to set xhr.responseType
};
xhr.onload = function (): void {
// omitted code to process results or download file
};
xhr.send(fd);
Observed Behavior
Given this code a few things happen:
As is, I never get back to my web api controller, I immediately get a
415 Media Type Unsupportedresponse.If I uncomment
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");, I get back to the server web api code, but my parameters object has allnullproperty values, and insideHttpContext.Current.Request.Formthere is only one key (------WebKitFormBoundaryT6DA5YOQpROCIxxU\r\nContent-Disposition: form-data; name) with a largestringvalue with three 'parts' separated byWebKitFormBoundarymarkers.If I leave
setRequestHeadercommented out, but on the server, remove theKatAppEndpointParametersparameter from the method signature, I get back to the server web api code, andHttpContext.Current.Request.Formcorrectly has the three keys/values of View/Inputs/Configuration.
Questions
I'd like to be able to model bind the
parametersvariable. In WebForms 4.5*, I do not have access to[FromForm]attribute like so many questions suggested. That must only be available in mvc web api projects.Even better, I'd like to change
InputsandConfigurationto automatically bind to model classes, or minimallyJObject. Obviously as string, I'd simply do aJObject.Parsebut I'd like the former option if possible.
Is this possible or is scenario 3 above the only solution possible - removing method parameter and doing something like JObject.Parse( Request.Form[ "" ] ) manually in each controller action method?
Note: I could possibly refactor this code to not use FormData and instead try to simply use/bind to regular json string data, but for the 'UploadFile' action method that I would also have to convert, I believe FormData is required, so I'd probably run into the same issue at that point.