Reputation: 123
I have a (ViewResult) Controller which receives a string parameter, generates a PDF file, and sends the PDF file back to the browser. I’ve tested the controller itself, and it works fine. Unfortunately, when I try to post to this controller from the $.ajax jQuery function (passing a simple string), the Controller always receives the string parameter as null. I've tried a hundred different configurations of the $.ajax function. Here's the controller, which returns a PDF to the browser (it works...as long as I create the HTML within the method):
[HttpPost]
public ActionResult HtmlToPdf(String htmlData)
{ }
Here's the jQuery I'm using in my view (triggered by a button click):
function getPdf() {
var htmlData = “blah, blah, etc.”;
$.ajax({
url: '/Home/HtmlToPdf',
type: 'post',
data: JSON.stringify(htmlData),
contentType: 'application/json; charset=utf-8',
success: handleSuccess,
error: handleError
});
}
I've tried 'post', 'get', json, text, html, stringify, different content types, etc. Does anyone know how to correctly send a string (the var 'htmlData' above) to a controller? Post? Get? Something else? Thanks.
Upvotes: 2
Views: 20746
Reputation: 700
Using the double POST method and a free jQuery extension 'fileDownload' from here
/*
* jQuery File Download Plugin v1.4.1
*
* http://www.johnculviner.com
*
* Copyright (c) 2013 - John Culviner
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*/
Which basically wraps a POST in some code which helps handling some of the complexity...
We have (with large chunks removed (!)...
$.ajax({
url: emr.baseURL + '/Control/SaveData',
type: 'POST',
data: JSON.stringify(dataToPost),
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: function (json) {
// stuff deleted which updates page
$.fileDownload(
'/Control/Export',
{
data: {
// data list deleted...
))
},
httpMethod: 'POST'
});
}
},
error: function () {
// deleted
}
});
With controller methods (cut down)
public JsonResult SaveData(/* ... */)
{
/*
... do stuff ...
*/
return /* JsonResult... */;
}
[HttpPost]
public void Export(/* ... */)
{
/*
...
*/
HttpContext context = System.Web.HttpContext.Current;
/*
...
*/
byte[] XLSXdata;
/*
...Fill XLXSdata...
*/
context.Response.Clear();
context.Response.ClearHeaders();
context.Response.ClearContent();
context.Response.Buffer = false;
context.Response.ContentType = "application/vnd.ms-excel";
context.Response.AddHeader("content-disposition", "attachment;filename="+filename);
context.Response.Charset = "";
context.Response.BinaryWrite(XLSXdata);
context.Response.Flush();
context.Response.End();
}
Obviously, this is explicitly controlling what goes into the reponse.
Hope this helps!/
Upvotes: 0
Reputation: 700
You need to send it as a json object:
function getPdf() {
var htmlData = “blah, blah, etc.”;
var dataToPost = { htmlData: htmlData };
$.ajax({
url: '/Home/HtmlToPdf',
type: 'POST',
data: JSON.stringify(dataToPost),
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: handleSuccess,
error: handleError
});
}
You then access in HomeController by just declaring it as an argument as you have done. The point is that you need to pass the name of the argument as part of the JSON object. The fact that you variable name is also 'htmlData' is irrelevant. The above could as easily be ...
var stuff = "blah, etc...";
var dataToPost = { htmlData: stuff };
Obviously for multiple arguments you just have more items in your object...
var dataToPost = { arg1: someData, arg2: 2, arg3: true }
... with ...
public ActionResult DoStuff(string arg1, int? arg2, bool? arg3) {}
If you want to pass an array add traditional:true
to the $.ajax
argument object.
I also note that I've always put 'POST' in upper case since, if you look at the documentation here the datatype:
arguments (e.g. 'json') are listed in lower case, but 'GET' and 'POST' for type:
are in upper case.
---------------------------- Update -------------------------
As mentioned in comments, for a simple file download, it's probably easier to use a GET.
The basic controller is something like (I'm trimming down code so haven't tested this)
public FileResult DownloadFile(string filename)
{
byte[] fileContent = new byte[0];
using(FileStream fs = File.OpenRead(filename)) {
fileContent = new byte[fs.Length];
fs.Read(fileContent, 0, Convert.ToInt32(fs.Length));
}
UTF8Encoding encoder = new UTF8Encoding();
return File(encoder.GetBytes(fileContent), "applicaton/text", filename);
}
Then in the javascript do:
$.get('/Home/DownloadFile',
function(data, textStatus, jqXHR) { /* don't think anything needs to be done */ }
).error(alert('Download failed'));
This obviously downloads a text file so you'd need to play with the encoding (possibly/probably don't need one?) and contentType string for PDF (I think it's just 'application/PDF'). Also, to repeat, I haven't tested this exact code, just edited the logic out of an existing application. Good luck.
Upvotes: 5
Reputation: 123
Thanks for the answers. I think all of them were correct, and good. I gave up on this one, though, because I think I was trying to do something that's not really possible: sending an ajax call to the server and having the server "finish the job" by returning a binary file to the browser (as a PDF). The ajax function was executing, getting a success message, and running its success function. Lost in all this was the fact that the browser was supposed to be receiving a file response, not an ajax response. Sorry if I'm not explaining it very well. Anyway, I dropped back and punted: I'm generating my file by rendering a .NET report (.rdlc) as a PDF, and sending that to the browser. Crude, but effective. In hindsight, I don't think ajax was even the way to go on this. :-p. Thanks again for the help.
Upvotes: 0
Reputation: 14873
Try sending your data
as a json object and setting the dataType
to 'json'
:
function getPdf() {
var htmlData = “blah, blah, etc.”;
$.ajax({
url: '/Home/HtmlToPdf',
type: 'post',
data: { htmlData: htmlData },
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: handleSuccess,
error: handleError
});
}
Upvotes: 2