The server is running CGI, C++ Web API. There is not MVC or PHP involved. The host is using JavaScript, AJAX and some JQuery.
My root question is two fold:
1) How to squirt a raw PDF onto a new window when it returns with that PDF from the POST. Below is an example of a web page that calls a Web API to generate a PDF, which I then do not properly put that PDF into a new web page. or
2) How to do a POST via an HREF using JavaScript and AJAX.
1 above is preferable. Please reference SQUIRT or HREF in your replies.
If you are an uber Web programmer - search for "Just Not Working" to solve the Squirt issue for me. That will save you a lot of reading.
My code works if the HREF is a GET. The Web API I am calling is returning a PDF. I open a new window and I wrapped the PDF with the proper HTML and all works exactly as I like when I return a static PDF from a GET. But the issue is that I need to push data into the Web API to tell it how to generate the PDF. Since I also want this to atomic, I don't want a post followed by a get. The web api must remove all trace of the data once it returns the PDF. So a single POST would be ideal.
There are other threads on stackoverflow that debate why it is bad to push data in the body of a GET, and since that violates some standards, that is off the table.
Also the data submitted must be secure - submitted via https, so putting it in the URL is no good.
I figured out the dual posting from the form, the same data is submitted to my Web API on a submit - to change the devices settings, and on a button click to generate a PDF.
A stripped down form:
<html>
<head>
<script src="./jquery/jquery-2.1.4.js" type="text/javascript"></script>
<script src="ToolKit.js" type="text/javascript"></script>
<script src="setupFunctions.js" ></script>
<link rel="stylesheet" type="text/css" href="yadayada.css" />
<title>My Linux Device with CGI scripts</title>
</head>
<div class="body">
<form name="MyForm" id="MyForm" hidden action="/api/config/form" method="POST">
<div class="variables">
<input type="hidden" id="ETH0_IP_MODE" name="ETH0_IP_MODE" value="DHCP">
<input type="hidden" id="WIFI_AP_BROADCAST" name="WIFI_AP_BROADCAST" value="ON">
</div>
<!-- HTML That does amazing and spectacular stuff, and sets name/value items to post. -->
<div id="bottomButtons">
<input type="submit" name="update" value="Apply settings" />
<input type="button" value="Cancel" onclick="location.reload(true);" />
<input type="button" id="GeneratePDF" value="Create PDF" onclick="GeneratePDF()" style="float:right;" />
</div>
</form>
</div> <!-- body -->
</html>
Above is a stripped down form with two buttons. Submit does a post but I overload the post to change the error/success.
<!-- now some Javascript -->
<script type="text/javascript">
function UpdateStatus(st, color) { <!-- here for completeness... --> }
$('form[name=MyForm]').submit(function (e) {
e.preventDefault(); // Keep the form from submitting
var form = $(this);
$.ajax({
url: form.attr('action'), // '/api/config/form',
type: form.attr('method'), // 'POST',
data: form.serialize(),
cache: false,
async: false,
dataType: 'html',
error: function (jqXHR, textStatus, errorThrown) {
UpdateStatus('Status: Error setting the network configuration! ' + textStatus + ': ' + errorThrown, "Crimson");
},
success: function (data) {
UpdateStatus('Status: Success set the network configuration!', "Chartreuse");
my_window = window.open("", "Update Network Configuration Results", "location=1,status=1,scrollbars=1,width=800,height=640");
my_window.document.write(data);
}
});
return false;
});
</script>
So far this is just a normal form that calls a Web API. Nothing special. After submit/update button is pressed, status is updated and on success a window is opened. That window receives a block a text that is returned from the Web API that is there for debugging. It reports back everything that has changed.
The main issue is the second button - the GeneratePDF. I do call a POST (see below), but my primary issue is how to squirt the PDF into the HTML on successful return from the post.
More JavaScript - linked to that second button:
function GetPDF() {
try {
var elem = document.getElementById('MyForm');
WebToolkit.readPDF(GeneratePDF_callback, elem); // POST
} catch (e) {
debugger;
throw e;
}
}
Now some AJAX: var WebToolkit = { host: document.location.host, // Javascript Get Website URL servicePath: "/api/config/", getValueMethod: "value?", getPDFMethod: "GeneratePDF?", httpsPortNumbers: [443], _networkErrorCallback: null, _serviceURL: null,
readPDF: function (callback, form) {
var url = this._serviceURL + this.getPDFMethod;
$.ajax({
type: "POST",
contentType: "application/pdf",
dataType: "text",
data: $("form").serialize(),
url: url,
timeout: 5000,
success: callback,
cache: false,
async: false
});
}
}
Obviously -serviceURL is setup in a setup function which I did not show. But you get the idea. All of this works. When I press the GeneratePDF button - it posts the data and the Web API (not shown) returns a raw PDF file. My callback is : GeneratePDF_callback. And THIS is the problem code that I need help with. I am leaving in commented out attempts to wrap the PDF - which do not work.
function GeneratePDF_callback(data)
{
try {
my_window = window.open("", "GeneratePDF Results", "location=1,status=1,scrollbars=1,width=1024,height=800");
// Next two lines are just not working !!!
var PDFData = encodeURI(data);
my_window.document.body.innerHTML = "<head><Title>Generate PDF</Title><style></style></head><body><object data=:application/pdf;base64,\"" + PDFData + "\" type=\"application/pdf\" width=\"100%\" height=\"100%\"><p>It appears you don't have a PDF plugin for this browser. Not a problem... you can <a href=\"/api/config/GeneratePDF\">click here to download the PDF file.</a></p></object></body></html>";
UpdateStatus('Status: Success reading PDF from device!', "Chartreuse");
} catch (e) {
debugger;
UpdateStatus('Error reading PDF: ' + e.message, "Crimson");
throw e;
}
}
FRIGGIN HELP ! This is driving me batty. Once this works - as you can see I have an href after the "Not a problem". This is why I think getting an HREF to work is preferable. Clicking that link will resubmit the data and request a new PDF document. And this in itself may be a major design flaw, since I haven't quite tackled how I'd resubmit the data from the new window.
Alternately clicking the button could do a POST of the data, and a GET of the results. The POST would return an ID, the GET would obtain the ID, and only allow ONE get of that ID. There is no refresh on the GET since it simply displays or stores the PDF. A refresh on the Window showing the PDF would not need to resubmit the query. This must be SECURE - HTTPS with none of the data visibly insecure from a sniffer.
If you're still with me - THANK YOU just for reading.
SOLUTION: I got this working, and am a bit overdue getting back to update this post: I use GET and pass in the parameters to tell the server how to build the PDF. I get back the html with an embedded object, which represents the PDF stream.
HTTP/1.1 200 OK X-Frame-Options: DENY Content-Length: 265129 Content-Type: application/pdf Date: Wed, 17 Feb 2016 07:19:29 GMT Server: lighttpd/1.4.35
%PDF-1.3 ... all the PDF binary data...
This is done in JavaScript via: form = $('form[name=myForm]');
PDF_window = window.open("", "PDF Results", "location=1,status=1,width=700,height=800");
if PDF_window == undefined) {
UpdateStatus("Status: Error opening window to display the PDF! Turn on POP UPS.", "Chartreuse");
}
else {
Data = WebToolkit._serviceURL+"PDF?" + form.serializeAndEncode();
}
WebPage = "<html><Title>PDF Page</Title><body><p><a href=\"" + Data + "\">click here to download the PDF file for saving.</a></p><object data=\"" + Data + "\"type=\"application/pdf\" width=\"100%\" height=\"100%\"></object></body></html>";
PDF_window.document.clear();
PDF_window.document.write(WebPage);
The only issue I have run into is that embedded PDF will not allow me to press the SAVE icon. It works for PRINT, this is why I put the URL to redraw the page and that redraws the page exactly as it had previously - but not inside the 'object data'. Then it allows SAVE and PRINT. Weird, I see lots of people asking how to draw a PDF and disallow SAVE. Well I want the opposite, my solution is OK, for now.
Update - Solution, which is pretty simple after much experimentation:
function DisplayPDFResult() {
var form;
form = $('form[name=MyForm]');
PDFWindow = window.open("", "Yada Header", "location=0,status=1,width=700,height=800,", false);
if (PDFWindow == undefined) {
UpdateStatusPop("Status: Error opening window to display the EasySetup PDF! Turn on POP UPS.", "ForestGreen");
return;
}
else {
// This url will cause the web api to generate a raw pdf, and squirt into the window
PDFWindow.location.href = WebToolkit._serviceURL+"ShowPDF?" + form.serializeAndEncode();
}
}
Vinnie