Cannot figure out how this jQuery code is working - c#

So there is a camera taking images every couple seconds and storing those new images with new files names on the server. When a request is made to "mypage", server side the latest images are loaded up and returned in the response. The images subsequently being refreshed with this jQuery code:
(function($) {
$(function() {
var refreshInterval = 5; // Number of seconds between image refreshes
$('#deskshare-grid img').each(function() {
$(this).data('src', $(this).attr('src'));
});
function refreshImages() {
$.get('?r=' + Math.random(), function(response) {
$('#deskshare-grid img').each(function(index) {
$(this).attr('src', $(response).find('#deskshare-grid img').eq(index).attr('src'));
});
setTimeout(refreshImages, refreshInterval * 1000);
});
}
setTimeout(refreshImages, refreshInterval * 1000);
});
})(jQuery);
The jQuery code I shared works and that is great, I didn't write the code and I want to know how it works.
My mind is stuck on the fact that a request was made for the page, the most recent image was retrieved on the server using C# and those images are included in the response. When a more recent image is created, it has a new file name.
How can jQuery refresh the photo for a file name it does not know client side?
Particularly this part of the code is confusing me:
$.get('?r=' + Math.random(), function(response) {
What is the url request for this $.get? I see the network tab of my F12 tools showing the new image responses but I do not understand how an image with a different file name could be requested with jQuery.
UPDATE
The accepted answer is correct but I wanted to elaborate. This jQuery is requesting the entire page again. The HTML response contains new image urls from the server. jQuery is used to parse the response, get the latest image urls and than update the existing HTML content with those new image urls parsed out of the response. This way there is no page flicker by trying to just refresh the entire page.

It does get request to the same page, Math.random() is to make easier to view each request. When you make a request to ? that is the same page.

What is the url request for this $.get?
The first parameter in the $.get is a relative url. This means that the url it's trying to access will be something along the lines of www.yourdomain.com/whatever?r=.
The "?" indicates the start of a query string, the "r" is the start of the request variable and whatever follows the equal sign is the query itself. In this particular case the query is just a randomly generated number that is sent up to the server. Without seeing the server-side code it would appear as if the filename is generated on the client and sent up to the server in this manner, and is probably used to name the file then on the server-side then.

The $.get('?r=' + Math.random(), function(response) { could be this two things:
Its changing the URL as a trick to not to get cached content.
Is server side required as dummy or something that we don't really know.
I recommend to look at the request's and response's headers for each 5'' call.

Related

How to send GET request from internet explorer to Asp.Net Core API?

I am developing Asp.Net Core 3.1 API, Everything working as expected when I send a GET request from google chrome, Edge, Postman. But when I send GET request from internet explorer it starts to download a file default.json with the content as the response of GET request.
Defualt Action method:
public IEnumerable<string> Get()
{
return new string[] { "Welcome" };
}
default.json content:
[
"Welcome"
]
I search on the internet but could not find anything useful.
FVI, I have the same observation when I run the API using visual studio or the deployed API on the server using IIS.
IE Version: 11.900.18362.0
So I have to questions.
Does IE not support this, Is this default behavior of IE?
If Yes then how can it be fixed?
This is IE default behavior, and comes down to it simply doesn't know how to treat content with mime types like */json, hence suggest a download.
Assuming this is for users in general, and you simply want to display the json data in a browser, you could convert the content server side to text.
public ContentResult Get()
{
var jsondata = new string[] { "Welcome" };
return Content(JsonSerializer.Serialize(jsondata));
}
If you are going to do something with the actual json data, which one usually does when consuming an api, you will use some kind of client side script (e.g. Ajax as in below sample, or similar) to get the content, and in those cases there won't be any problem, like the one you encountered.
var xhr = new XMLHttpRequest();
xhr.open('GET', '/api/your-method', true);
xhr.onload = function (e) {
if (this.status == 200) {
var jsonstring = this.responseText;
// do something with the json string, e.g. JSON.parse(jsonstring)
}
};
xhr.send();
Here's a couple of posts that suggests to change the registry, though they won't be viable unless it is for a local computer of your own (and if it is, picking a browser that works out-of-the-box must be easier).
Display JSON in IE as HTML Without Download
How can I convince IE to simply display application/json rather than offer to download it?
Edit
As suggested in a comment, yet another option would be to change the mime type explicit:
Json response download in IE(7~10)

How can I load a WebForms page into an iframe without two full page renders from the server?

I have a sort of shell page that contains filter controls and an iframe for displaying a gridview. When you click the 'filter' button, or when a timer is fired, filter data is collected from the filter controls and fed in the querystring to the gridview iframe, as shown:
var URL = "/mypage/gridview.aspx";
var dest = URL + '?' + getFilterData();
var frame = $('#gridiframe');
$.ajax({
type: "HEAD",
async: true,
url: dest
}).success(function () {
frame.attr('src', dest);
})
.error(loadError);
My conundrum is this: the gridview can be very costly to render. It performs a HUGE database trip and this method is causing the page to be rendered twice. The first time, the client just looks at the header to make sure the user is authenticated (page returns HTTP unauthorized if session has timed out) and that all the parameters are in a good format. But I don't want to change the iframe source to show an error message, I just want to call loadError which basically just notifies the user what went wrong.
Is there anything I can do with the client code to populate my iframe in only one exchange with the server?
edit: thanks all for your input. At the end of the day, I hate webforms. End of story.
Instead of querying the iframe's source URL in the ajax query, query a different endpoint that doesn't run the same DB query. You said it does two things, 1 validate the user's login token is still valid and 2 that the filter parameters are valid. Neither of those validation steps require running the query on the database that get the full query results. So, create a new API endpoint that does your basic validation, and if that returns a success message, then set the iframe source to what you want.
Another option is to return a HTML fragment, rather than a full HTML page, from gridview.aspx. Then your AJAX can replace the contents of a div with the HTML result if successful, rather than setting the iframe src, while still allowing you to handle errors.
A more trendy option (not saying it's better or worse, but certainly more aligned with the current web development industry) is to change gridview.aspx to a web service/api that returns the data in simplified json, then use a javascript framework, or template engine to generate the html table on the client itself.
Maybe I'm over-simplifying things, but couldn't you just use a QueryString parameter in the second request?
$.ajax({
type: "HEAD",
async: true,
url: dest
}).success(function () {
frame.attr('src', dest + '&getGrid=true');
})
And then on the aspx page check if the QueryString is present.
protected void Page_Load(object sender, EventArgs e)
{
//validate user always
if (Request.QueryString["getGrid"] != null)
{
//rebuild grid
}
}
Instead of an iFrame you can a normal <div> and load only the gridView from another page as follows:
First, Wrap the gridView around a container in the other page
Something like
<div id='grid-wrapper'><!-- GridView here --></div>
Then use your AJAX code like this to retrieve only the grid:
var URL = "/mypage/gridview.aspx";
var dest = URL + '?' + getFilterData();
var div = $('#divId');
$.ajax({
type: "HEAD",
async: true,
url: dest
}).success(function (res) {
div.html($('#grid-wrapper',$(res)).html); //this gets only the contents of grid-wrapper and not the whole page.
})

File download via Webapi controller - handling errors

I am working on an application that provides some links for users to download files.
The page itself is served up by an MVC controller but the links are pointing to a WebAPI controller running on a separate domain.
(I would have preferred same domain but for various reasons it has to be a separate project and it will run on a separate domain. I don't think CORS is part of the issue anyway as this is not using XHR, but I mention it just in case).
So in development, the main MVC project is http://localhost:56626/Reports/
And the links on the page might look like this:
Report 12345
where port 51288 is hosting the Web API.
The WebAPI controller uses ReportID to locate a file, and write its contents into the response stream, setting the disposition as an attachment:
//security.permission checks and scaffolding/database interaction
//left out for clarity
try
{
string filename = #"C:\ReportFiles\TestReport.csv";
var stream = new FileStream(path, FileMode.Open);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
var disp = new ContentDispositionHeaderValue("attachment");
disp.FileName = "TestReport.csv";
result.Content.Headers.ContentDisposition = disp;
return result;
}
catch(Exception ex)
{
//how to return a response that won't redirect on error?
}
By doing this the user can then click on the link and without any redirection, the user gets prompted to save or open the file, which is what I want; they stay on the original page with the links and just get an Open/Save dialog from the browser.
The problem arises when something goes wrong in the Web API controller - either an exception or some internal logic condition that means the file cannot be downloaded.
In this case when clicking the link, the download doesn't happen (obviously) and they get taken to the target URL instead i.e http://localhost:51288/api/ReportDownload?ReportID=12345 which is not desirable for my requirements.
I would much rather be able to catch the error somehow on the client-side by returning for e.g. HTTP 500 in the response, and just display a message to the user that the download failed.
Now to be honest, I don't even understand how the browser can do the "in place" File/Save dialog in the first place:
I always thought if you click a link that has no explicit target attribute,the browser would just open the new request in your current tab i.e it's just another GET request to the target URL, but it seems this is not the case
The browser seems to be doing a hidden background fetch of the target URL in this case (same behaviour in FF,Chrome and IE) which I cannot even see in the F12 tools.
The F12 Network log shows no activity at all except in the specific case where the response has NOT been setup as Content-Disposition: attachment i.e an error -only in this case do I see the (failed) HTTP GET being logged in the Network request list.
I suppose I could just catch any exception in the controller and send back a dummy file called "Error.csv" with contents "Ha Ha Nope!" or something similar, but that would be a last resort...any ideas welcome!
If the user clicks on the link, the browser will follow it - then depending on the response headers and browser configuration, it'll either show the file dialog or render directly - you can't really change that behavior (apart from using preventDefault when the link is clicked, which kind of defeats the purpose).
I'd suggest taking a closer look at http://jqueryfiledownload.apphb.com/ which lets you do something like this:
$.fileDownload('some/file/url')
.done(function () { alert('File download a success!'); })
.fail(function () { alert('File download failed!'); });
Then you could bind the download action using jQuery.

IE wants to download JSON result....MVC3

My MVC3 app uploads documents from the user to our server. I am returning a JsonResult to display any errors, if any:
[HttpPost]
public JsonResult SaveDocument(DocumentModel model, HttpPostedFileBase postedFile)
{
//my wonderful code
return Json(new { success = true, message="ok" });
}
Heres how i submit the request:
var isSubmitting = false;
var addDocumentOptions = {
beforeSubmit: beforeAddDocumentSubmit, // pre-submit callback
success: afterDocumentSubmit // post-submit callback
};
$('#btnCreateDocument').click(function (e) {
e.preventDefault();
$('#divError').html('');
if (!isSubmitting) {
$('#createDocForm').submit();
}
});
This javascript function runs when the upload is complete:
function afterDocumentSubmit(responseText, statusText, xhr, $form) {
if (responseText.success) {
//no errors
} else {
$('#divError').html('Error: ' + responseText.message);
}
}
In FF, Chrome etc., my javascript code runs fine, but in IE, the browser wants to download the Json result as text. I get a download/open file dialog box that shouldnt appear. How do i make IE not download my Json result and behave like the other browsers? Thanks
I ran into a similar problem doing the same in Spring MVC on Java. The problem was that Spring was returning the content-type of the JSON result as application/json, which seems to make IE want to download it. You can try changing the content-type to text/plain; IE won't prompt you to download the file in this case. I suspect that something similar might be happening here.
You could try:
return Json(new { success = true, message = "ok" }, "text/plain");
In response to your new problem: the issue is that responseText is just a string. What you need to do is to convert it into a Javascript Object. You can do it like this:
var response = JSON.parse(responseText);
if(response.success) {
...
}
Most browsers support JSON.parse(). If you're having issues with non-compliant browsers, you can always use the JSON Javascript Library.
Wild guess: you are using the jquery.form plugin which enables you to upload files using AJAX and you haven't read the documentation which states the following:
Browsers that support the XMLHttpRequest Level 2 will be able to
upload files seamlessly and even get progress updates as the upload
proceeds. For older browsers, a fallback technology is used which
involves iframes since it is not possible to upload files using the
level 1 implmenentation of the XMLHttpRequest object. This is a common
fallback technique, but it has inherent limitations. The iframe
element is used as the target of the form's submit operation which
means that the server response is written to the iframe. This is fine
if the response type is HTML or XML, but doesn't work as well if the
response type is script or JSON, both of which often contain
characters that need to be repesented using entity references when
found in HTML markup.
To account for the challenges of script and JSON responses when using
the iframe mode, the Form Plugin allows these responses to be embedded
in a textarea element and it is recommended that you do so for these
response types when used in conjuction with file uploads and older
browsers. Please note, however, that if there is no file input in the
form then the request uses normal XHR to submit the form (not an
iframe). This puts the burden on your server code to know when to use
a textarea and when not to.
Now that you have read it you should take the respective actions if you want your code to work under IE as I have exemplified in this post.
The proper JsonResult return should look like so:
[HttpPost]
public JsonResult SaveDocument(DocumentModel model, HttpPostedFileBase postedFile)
{
...
return Json(new { success = true, message="ok" }, "application/json; charset=utf-8", JsonRequestBehavior.AllowGet);
}

Using .ajaxForm and C# MVC, how can I prevent Internet Explorer from trying to save Json result?

I'm returning some Json via a C# MVC Controller. Other browsers work fine, but Internet Explorer (IE9) tries to Save the returned Json data as a Text file. Any ideas how to keep this from happening?
//MVC Controller Code...
return Json(new { redirectToUrl = Url.Action("Create", "Album",
new { url = url, isLocalFile = isLocalFile})});
//Clientside Javascript Code
$("#uploadImageForm").ajaxForm({
beforeSubmit: function () {
},
success: function (data, textStatus, xhr) {
window.location.href = data.redirectToUrl;
},
error: function (data, textStatus, xhr) {
}
});
I've tried adding "text/plain" and "text/json" to the second argument of the return Json method, it doesn't work.
Many thanks!
Quote from the documentation of the jquery.form plugin:
Browsers that support the XMLHttpRequest Level 2 will be able to
upload files seamlessly and even get progress updates as the upload
proceeds. For older browsers, a fallback technology is used which
involves iframes since it is not possible to upload files using the
level 1 implmenentation of the XMLHttpRequest object. This is a common
fallback technique, but it has inherent limitations. The iframe
element is used as the target of the form's submit operation which
means that the server response is written to the iframe. This is fine
if the response type is HTML or XML, but doesn't work as well if the
response type is script or JSON, both of which often contain
characters that need to be repesented using entity references when
found in HTML markup.
To account for the challenges of script and JSON responses when using
the iframe mode, the Form Plugin allows these responses to be embedded
in a textarea element and it is recommended that you do so for these
response types when used in conjuction with file uploads and older
browsers. Please note, however, that if there is no file input in the
form then the request uses normal XHR to submit the form (not an
iframe). This puts the burden on your server code to know when to use
a textarea and when not to.
This means that if your form contains file input fields and you are submitting this form to a controller action that returns JSON, you must wrap this JSON in a <textarea> tags.
So your response should not look like this:
{ "redirectToUrl":"some url" }
it should look like this:
<textarea>{ "redirectToUrl":"some url" }</textarea>
In order to achieve that you could use a custom action result that will wrap the response with those tags:
public class TextareaJsonResult : JsonResult
{
public TextareaJsonResult(object data)
{
this.Data = data;
}
public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
bool shouldWrap = !context.HttpContext.Request.IsAjaxRequest();
if (shouldWrap)
{
response.Write("<textarea>");
}
base.ExecuteResult(context);
if (shouldWrap)
{
response.ContentType = "text/html";
response.Write("</textarea>");
}
}
}
and then have your controller action return this custom result:
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
// ... some processing
var redirectToUrl = Url.Action(
"Create",
"Album",
new { url = url, isLocalFile = isLocalFile }
);
return new TextareaJsonResult(new { redirectToUrl = redirectToUrl });
}
Now obviously in your AJAX success callback you also need to account for this difference by testing the typeof result and in the case of a legacy browser (such as Internet Explorer) manually parse the response. You may take a look at the source code of the page I have linked to.
But this being said, I can see that in your success callback you are redirecting to a controller action contained in the JSON response returned by the server. Here comes my question: What's the point of using AJAX in the first place if you are going to redirect? Why don't you use a standard form post to the controller action and have the controller action directly perform the redirect? AJAX should be used when you want to stay on the same page.
I agree with Jesse's comment, this is probably a duplicate of the link he provided.
As such I'll provide an alternative. I prefer to view json coming over the wire using a http proxy similar to fiddler http://www.fiddler2.com/fiddler2/ . I mention fiddler because it works with all browsers. The advantage is that you get a treeview of the parsed json. It's MUCH easier to read and find what you're looking for.
Also, I believe firebug for firefox, chrome dev tools, and ie dev tool all provide the same functionality. (I know chrome has the treeview, I think I remember firebug having it, and I would be surprised if modern IE doesn't have it considering the other 2 do.)

Categories