I'm using Rotativa to generate a PDF file from a view, which works well, but now on the browser I get the raw file thrown at the console, no download dialog box, no warning, nothing. Here's my code:
Controller
public ActionResult DescargarPDF (int itemId) {
var presupuesto = ReglasNegocio.Fachada.Consultas.ObtenerPresupuesto(itemId);
return new Rotativa.PartialViewAsPdf("_PresupuestoFinal", presupuesto) {
FileName = "Presupuesto_" + itemId + ".pdf",
PageSize = Rotativa.Options.Size.A4
};
}
JQuery script:
$(".convertirPDF").on("click", function (id) {
var itemId = $(this).data('itemid');
Pdf(itemId);
});
function Pdf(itemid) {
var id = itemid;
$.ajax({
method: "POST",
url: 'DescargarPDF',
data: { itemId: id },
cache: false,
async: true,
});
};
Button on the HTML
<button class="convertirPDF btn btn-secondary btn-info" data-itemid="#item.Id">PDF</button>
I've tried several codes on the controller (with same result) since the script and view seems to work fine. However, I'm suspecting, maybe the html or the script need some tuning to inform the browser it has to download the file?
Thanks everyone in advance.
I found a solution. It's not elegant, but it works.
So I didn't need to use ajax necessarily to make the request, neither to give function to the button. I'm kind of sure that the issue has something to do with JS and/or jQuery. Nevertheless, there's a simpler way to do this.
I changed my html button to:
PDF
so it looks like a button but it's really a link to my controller¡s method. I also removed the script for that button and now it downloads the file. Not with the name intended, but still.
Thanks to everyone. Happy coding.
UPDATE
I've been working on the same project, and I think I found out why my PDF file was being thrown into console.
The thing is, jQuery makes the request, so jQuery manages the response. Is that simple. If you check official docs for .post(), you'll see the following:
The success callback function is passed the returned data, which will be an XML root element or a text string depending on the MIME type of the response. It is also passed the text status of the response.
As of jQuery 1.5, the success callback function is also passed a "jqXHR" object (in jQuery 1.4, it was passed the XMLHttpRequest object).
Most implementations will specify a success handler.
And I wasn't, so, by default, it just drop it to console. I hope this throws some light into the issue and helps. Happy coding.
Related
I've noticed a strange behaviour on Microsoft Edge and an ajax request with jQuery.
In a page I have this code
$("#btnSend").click(function () {
$("#sending").show();
$.ajax({
type: 'GET',
url: '/Report/SaveQuoteOnFile?quote=18',
crossDomain: true,
success: function (msg) {
if (msg == 'True') {
alert('Email sent to the client');
}
$("#sending").hide();
},
error: function (request, status, error) {
$("#sending").hide();
}
});
});
The WebApi is build in C# and the code is something like
public bool SaveQuoteOnFile(int quote)
{
bool rtn = false;
Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST");
Response.Headers.Add("Access-Control-Allow-Headers", "accept, authority");
Response.Headers.Add("Access-Control-Allow-Credentials", "true");
// code to send an email
// if the email is sent rtn = true
...
return rtn;
}
When I open the page with Chrome and click on the button btnSend, Chrome calls the WebAPI, the email is sent, the result is true and I show a message. If I insert a breakpoint in the WebApi, I can follow the code step by step. I'm assuming is always the same.
Now I open the same open with Microsoft Edge and click on the button btnSend, it seems calling the WebApi, the result is true and I show a message but the email isn't sent. The same breakpoint doesn't fire.
The first thing I thought was the WebApi code is wrong. Obviously. I tried it with Mozilla and Safari but it's working fine.
Idea. I cleaned the cache and other stuff on Microsoft Edge. I click on the button and now everything is working fine! Then I'm so happy and I show my job to my boss on my desktop. On Microsoft Edge I click again on the button and nothing happened. It's working fine with other browsers. I clean again the cache and other stuff on Microsoft Edge and... it's working fine again...
Ok, the problem is the cache (???) but how I can solve in general this problem?
Thank you in advance for any suggestions.
I'm aware that data can be passed in through the URL, like "example.com/thing?id=1234", or it can be passed in through a form and a "submit" button, but neither of these methods will work for me.
I need to get a fairly large xml string/file. I need to parse it and get the data from it before I can even display my page.
How can I get this on page load? Does the client have to send a http request? Or submit the xml as a string to a hidden form?
Edit with background info:
I am creating a widget that will appear in my customer's application, embedded using C# WebBrowser control, but will be hosted on my server. The web app needs to pass some data (including a token for client validation) to my widget via xml, and this needs to be loaded in first thing when my widget starts up.
ASP.NET MVC 4 works great with jQuery and aJax posts. I have accomplished this goal many times by taking advantage of this.
jQuery:
$(document).ready(function() {
$.ajax({
type: "POST",
url: "/{controller}/{action}/",
data: { clientToken: '{token}', foo: 'bar',
success: function (data, text) {
//APPEND YOUR PAGE WITH YOUR PARSED XML DATA
//NOTE: 'data' WILL CONTAIN YOUR RETURNED RESULT
}
});
});
MVC Controller:
[HttpPost]
public JsonResult jqGetXML(string clientToken, string foo)
{
JsonResult jqResult = new JsonResult();
//GET YOUR XML DATA AND DO YOUR WORK
jqResult.Data = //WHATEVER YOU WANT TO RETURN;
return jqResult;
}
Note: This example returns Json data (easier to work with IMO), not XML. It also assumes that the XML data is not coming from the client but is stored server-side.
EDIT: Here is a link to jQuery's Ajax documentation,
http://api.jquery.com/jQuery.ajax/
Assuming you're using ASP.NET, since you say it's generated by another page, just stick the XML in the Session state.
Another approach, not sure if it helps in your situation.
If you share the second level domain name on your two sites (i.e. .....sitename.com ) then another potential way to share data is you could have them assert a cookie at this 2nd level with the token and xml data in it. You'll then be provided with this cookie.
I've only done this to share authentication details, you need to share machine keys at a minimum to support this (assuming .Net here...).
You won't be able to automatically upload a file from the client to the server - at least not via a browser using html/js/httprequests. The browser simply will not allow this.
Imagine the security implications if browsers allowed you to silently upload a file from the clients local machine without their knowledge.
Sample solution:
Background process imports xml file and parses it. The background process knows it is for customer YYY and updates their information so it know the xml file has been processed.
A visitor goes to the customer's web application where the widget is embedded. In the markup of the widget the customer token has been added. This could be in JavaScript, Flash, iFrame, etc.
When the widget loads, it makes a request to you app which then checks to see if the file was parsed for the provided customer (YYY) if it has, then show the page/widget.
If the XML is being served via HTTP you can use Liqn to parse the data.
Ex.
public partial class Sample : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string url = "http://news.yahoo.com/rss/";
var el = XElement.Load(url).Elements("channel");
StringBuilder output = new StringBuilder();
foreach (var c in el.Elements())
{
switch (c.Name.LocalName.ToLower())
{
case "title":
output.Append(c.Value);
output.Append("<br />");
break;
}
}
this.Label1.Text = output.ToString();
}
}
It is not exactly clear what the application is and what kind of options you have, and what kind of control over web server you have.
If you are the owner of the web server/application your options are way wider. You can first send a file to web-server with HTTP POST or PUT, including a random token, and then use the same token for GET with token in the query string
or use other options, applicable to third party-owned websites
if you are trying to consume some auth api, learn more about it. since you are hosting web browser control, you have plenty of options to script it. including loading whatever form, setting textarea or hidden field text with your xml and then simulating a submit button click. you can then respond to any redirects and html responses.
you can also inject javascript inside the page that would send it to server with ajax request.
the choice heavily depends on the interaction model.
if you need better advice, it would be most helpful if you provided sample/simplified url/url pattern, form content, and sequence of events that is expected from you from code/api/sdk perspective. they are usually quite friendly.
There are limited number of ways to pass data between pages. Personally for this I would keep in session during the generating page and clear it when it is retrieved in the required page.
If it is generated server side then there is no reason to retrieve it from client side.
http://msdn.microsoft.com/en-us/library/6c3yckfw(v=vs.100).aspx
Create a webservice that your C# app can POST the XML to and get back HTML in response. Load this HTML string into the WebBrowser control rather than pointing the control to a URL.
I have an ASP.NET page, where the user is able to trigger a console app on our server, which generates a file. What I want is the user to see a message that states 'Requesting file', then as soon as the file becomes available (i.e. File.Exists for the file returns true), the Requesting file message will change to a link of the file.
Is this easy to accomplish in ASP.NET?
In you web page implement a JSON call to a certain WebMethod which checks if the file has be been generated or not.
you can add your message in the calling function and clear it in the complete event, where you can create the link to the file also
function ddlLeaveChanged(sender, e) {
if (leaveTypeId != '-1' && dateFrom != '' && leaveStatusId != '-1') {
$('#lblMessage').show();
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
url: WebServiceUrl + 'YourMethod',
success: FunctionSuccedded,
error: FunctionFailed
});
}
function FunctionsSuccedded(result, e) {
if (result) { $('#lblMessage').hide(); }
}
This can be achieved using a combination of:
Javascript setInterval
[jquery.Ajax][2]
ASP.NET Web Method
A javascript function can be triggered every x seconds (using setInterval) to trigger a Web Method using AJAX to check whether the file exists.
Check the links I've provided above. These should be all you need to get this working.
If you want to avoid file systems all together you could key off a database field as well, i.e. when the linked is clicked an entry is made into a database table of some sort that signifies file creation pending, and then periodically check if the status has been updated. The server side code would update that same key and have a url in the datastore as well, so you could just check for the status of file created and the link the datastore. This would also result in you having a history of file creation, which you could purge as you saw fit, so from an ASP.NET perspective you would only be relying on data access code to determine if your file was created.
Not much different in ASP.NET vs any other platform. You can go about it like this:
Make AJAX call when the triggering link is clicked.
Server-side code that handles the call launches an external process to generate the file, and waits for it to complete.
After checking that the file is indeed there, reply to the AJAX call with a meaningful message (i.e. one that contains enough information to build a link to the file).
I need to refresh sections of my page to update when there is new data! what do i do? use jquery?
examples:
Yes, jQuery's great for this. Look into these methods:
http://api.jquery.com/category/ajax/
jQuery is usually not needed for basic AJAX. A simple example could be as follows:
liveSection = document.getElementById('latest-news');
request = new XMLHttpRequest;
request.open('GET', '/news-ajax', true);
request.send(null);
request.addEventListener('readystatechange', function() {
if (request.readyState == 4 && request.status == 200)
liveSection.innerHTML = request.responseText;
}, false);
If you're using Asp.NET, why not use an UpdatePanel? It's simple and reliable.
Edit
I just re-read your question and it looks (based on how you worded it) that you want to update a user's web page when the data changes on the server. I just want to make sure you understand that in a web app, the server can't trigger the browser to do anything. The server can only respond to browser requests, so you'll need to have the browser poll the server periodically.
I've created a simple example (using jQuery) to help you understand the breakdown of the things that will need to happen, which are:
1 - Periodically polling the server (via ajax) using Javascript's setTimeout to check that what is loaded into the browser is the latest content. We can achieve this by fetching the latest item ID or whatever and comparing it to a variable, which was initialised when the page first loaded.
2 - If the item ID does not match (a bit of an oversimplification) then we can assume that there has been an update, so we replace the content of some element with some content from some page.
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script>
function getLatestStuff() {
// fetch the output from a context which gives us the latest id
$.get("isthereanupdate.aspx", function(response) {
// we have the response, now compare to the stored value
if(resp != lastItemId) {
// it's different, so update the variable and grab the latest content
lastItemId = response;
$("#latestStuffDiv").load("updates.aspx");
}
});
}
$(document).ready(function() {
// the value which initializes this comes from the server
var lastItemId = 7;
setTimeout(getLatestStuff, 10000);
});
</script>
If you want to update when there is new data, you should look into comet or pubsubhubbub. jQuery can help you display the data in a pretty way, but you'll need to write stuff on the serverside to send the data.
I have an Send.aspx page that has an uploadify control on it. Upload.ashx handles the file upload.
I am adding a file record to a sql database in the Upload.ashx file and I need to get the ID of that record back from Upload.aspx when it is done.
Can't get it working with Sessions. =( Something to do with an Adobe bug?
What would the best way to handle this be?
Here is the uploadify control:
<script type="text/javascript">
// <![CDATA[
var contestID = $('[id$=HiddenFieldContestID]').val();
var maxEntries = $('[id$=HiddenFieldMaxEntries]').val();
var userID = $('[id$=HiddenFieldUserID]').val();
$(document).ready(function() {
$('#fileInput').uploadify({
'uploader': '../uploadify/uploadify.swf',
'script': '../uploadify/Upload.ashx',
'scriptData': { 'contestID': contestID, 'maxEntries': maxEntries, 'userID': userID },
'cancelImg': '../uploadify/cancel.png',
'auto': true,
'multi': false,
'fileDesc': 'Image Files',
'fileExt': '*.jpg;*.png;*.jpeg',
'queueSizeLimit': 1,
'sizeLimit': 4000000,
'buttonText': 'Choose Image',
'folder': '/uploads',
'onAllComplete': function(event, queueID, fileObj, response, data) {
document.getElementById('<%= ButtonCleanup.ClientID %>').click();
}
});
});
// ]]></script>
This took a while for me to figure out. But in retrospect it is extremely simple. For this reason, I mad a video tutorial to help newcomers get started quickly and understand how this awesome control works.
Video Tutorial from start to finish:
http://casonclagg.com/articles/6/video-tutorial-uploadify-asp-net-c-sharp.aspx
I noticed in my own script that I'm using an onComplete event instead of onAllComplete. Unless a config option escapes me, onComplete will trigger after each Upload.aspx call (the files are uploaded individually - again maybe this is configurable). According to the documentation, onAllComplete doesn't actually pass back request data (which makes sense because it's done outside of the scope of the individual uploads).
Anything that Upload.aspx outputs should appear in the response parameter. You can simply have it output the id of the element the script created and the response should contain the appropriate string.
As promised, I made a video tutorial on how to use an Uploadify control in an ASP.Net application.
http://casonclagg.com/articles/6/video-tutorial-uploadify-asp-net-c-sharp.aspx
Hope this helps.
The short answer to my questions is this:
When you return context.Response.Write("foo"); out of your Upload.ashx handler...
And put this in your uploadify control:
'onComplete': function(event, queueID, fileObj, response, data) {
alert(response);
}
An alert box with the word foo will pop up.
You can make this something more complicated like
context.Response.Write("id=55&title=This is the title");
and parse the values out yourself in your OnComplete or OnAllComplete in JQuery.