IE tries to download JSON in ASP. NET MVC 3 - javascript

I was trying to return Json from my action and after that IE tried to download it and showed me save dialog.
I tested it in Firefox, and there it works fine.
return Json(new { success = false, message = ex.Message }, "application/json");
What is the reason of that behavior and how can I solve that issue?
After that in Javascript part I try this
if (responseJSON.success == false) {
alert(responseJSON.message);
cancel();
}
But IE doesn't show alert anyway. It brings me save dialog.
I tried to change "application/json" with "text/plain" and save dialog disappeared, but I am not able to see alert yet. What am I missing?
EDIT:
Here is my complect Javascript, I am using Valums qquploader(ex-Ajaxupload) for uploading images
var uploader = new qq.FileUploader({
element: document.getElementById("image-upload"),
action: '/Home/ImageUpload',
allowedExtensions: ['jpg', 'png', 'gif'],
sizeLimlit: 2048,onComplete: function (id, fileName, responseJSON) {
if (responseJSON.success == false) {
alert(responseJSON.message);
cancel();
}
else {
alert("success");
//some code here
}
}
});
I had tested with alert("success"); in my else part and forwarded json as "text/plain" and after that I saw the alert. But in that time responseJSON.success != false for me. Have you any suggestions about that?

I've solved that with this trick
return Json(new { success = false, message = ex.Message }, "text/html");
And now it works. But can me anyone explain why it works with text/html, and didn't work with application/json and text/plain. First is trying to download JSON and second is returning undefined properties for JSON fields.

This problem occurs when using an upload plugin that uses an iframe to do the upload with IE (tested on 9.0).
IE sets the header Accept: text/html, , application/xhtml+xml, */* and so when you reply with Content-type: application/json, it assumes it's the file (or at least that's the only explanation I could find on the web).
Thus, to circumvent that, you need to set Content-type: text/html or Content-type: text/plain.
I would recommend implementing this using an ActionFilter; instead of manually changing the content type, detect IE and a multipart POST and change the content-type accordingly.

Possibly you are not setting a correct mime type for your json content (for IE try text/plain)
See: What problems may using the MIME type application/json cause?

I was using this same uploader and had the same problem.
It has to do with the request headers sent. IE needs the request to have an accept header that indicates json.
xhr.setRequestHeader("Accept", "application/json, text/javascript, */*; q=0.01");
If you insert this after this line:
xhr.setRequestHeader("Content-Type", "application/octet-stream");
in the js file (mine is called fileuploader.js) then you should no longer have the problem, and don't need to indicate text/html in your return.
p.s.
I commented out the content-type line, but am no longer sure why. If just adding this the accept line does not work, try commenting out the content-type header as well.
Edit:
I looked at my file again, and it seems like also made another change.
Instead of the line:
xhr.send(file)
I put in:
var formData = new FormData();
formData.append("image", file);
xhr.send(formData);
This comes after the setrequesrheader line above.
I am not sure if this change will work for all uses, like multiple file upload. I only upload a single image for my use.

Related

How to trigger Spinner for file download or How to download file in browser if AngularJS $resource response received as an attachement with file name

I'm able to download successfully using below line of code in AngularJS controller.
$window.location.href =$rootScope.urlBase+'/dios/download/lineitemexcel/'+$routeParams.id
But, when data is large and it is taking longer time, for this reason I need to enable angular-Spinner. No way I could find a way to start spinner before the call and it should finish after file download finished in the browser.
Root Problem: How can I enable spinner with existing plugin in the project angular-spinner or usSpinnerService? if this has a solution I dont have next question.
To Solve this problem, I have gone through existing spinner working flow.
Spinner is working if there is $resource call.
Hence above url I tried by forming factory call like below:
Service:
factory('Dios', ['$resource','$rootScope',
function($resource, $rootScope){
return $resource($rootScope.urlBase+'/dios/:id/:status/:third/:fourth/:fifth/:pageNo', {}, {
get: {
method: 'GET'
},
update: {
method: 'PUT'
},
getexcel: {
method: 'GET',
transformResponse: function(data, headers,status){
var dataTransformed = {};
dataTransformed.data=data;
dataTransformed.headers=headers;
dataTransformed.status=status;
return dataTransformed;
}
}
});
}])
Controller:
Dios.getexcel({dios:'dios',third:'download',fourth:'lineitemexcel',fifth: $routeParams.id},function(data){
console.log(data);
]);
Using above factory call rest call with an Id of an Object. That Object I need to retrieve and process using Apache POI JAVA library , and Apache POI library will return as an attachement with response header properties in dev tools network tab as follows:
HTTP/1.1 200
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Access-Control-Allow-Origin: http://10.218.39.45:9000
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS, DELETE
Access-Control-Allow-Headers: Content-Type,X-Requested With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers
Access-Control-Expose-Headers:
1,Access-Control-Allow-Origin,Access-Control-Allow-Credentials
Content-Disposition: attachment;
filename=LineItems-of_IO-1553-8531Testing_2018-09-18.xlsx
Content-Type: application/vnd.ms-excel
Transfer-Encoding: chunked
Date: Tue, 18 Sep 2018 08:33:46 GMT
In this way I'm able to get spinner but , I am not sure how to download response received as file with specified name in response headers.
using transformResponse: I am expecting responseheaders info, but it is not getting attached to headers in transformResponse:.
So Kindly help me to get spinner is getting triggered for window.location.href=url or help me how download the received data through $resource.
First this question has nothing to do with angularjs itself, this is pure js problem.
For download there are 2 options:
Download file using Ajax and then store it on disk using createObjectURL.
Download file usual way i.e. simple href.
These two are really different:
-1st wont work in some browsers
-1st gives you full access over request, you can comfortably handle errors etc.
-2nd start download in parallel, e.g. in chrome you can close tab with app, but download will still proceed
In 1st way showing spinner is relatively easy, you do it as for any other request.
In 2nd way you usually do not want to show spinner when download is in progress as browser will show progress for you. However, before download starts - before response came from server you sometimes may want to show some waiting and the only way this can be done is using cookies:
you add some param to download request e.g. my-cookie-id=cookie_123
server sets cookie cookie_123 in response
after link is clicked you check for this cookie value (e.g each 100ms)
when you see this cookie value, your file is started to download
I don't see any way to show spinner while downloading file directly using window.location.
But downloading file from response can be possible.
Can you please try below approach?
Delete trasnformResponse from $resource
Write a function to download file
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
$scope.downloadFile= function(data, fileName, contentType) {
var blob = new Blob([data], {
type: contentType
});
var url = URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
//hide spinner here
window.URL.revokeObjectURL(url);
}
Change you function call as
Dios.getexcel({dios:'dios',third:'download',fourth:'lineitemexcel',fifth: $routeParams.id},function(data, responseHeader){
$scope.downloadFile(data, responseHeader()['Content-Disposition'].split('filename=')[1] , responseHeader()['Content-Type']);
]);

XMLHttpRequest "PUT"

I know how to "GET" data using XMLHttpRequest(), and would also like to use XMLHttpRequest() to "PUT" data to a file. The data I would like to "PUT" is just plain text. There is much on how to "GET" data, but I have done much research and have not found much on how to "PUT"data. There are a few questions online trying to do something similar but usually to "POST" instead, and none I found show a working model. Some say that for "POST"ing or "PUT"ing data, its "Content-type" must be "application/x-www-form-urlencoded" and never "text/plain". I would like to do my request synchronously and not use Node.js instead. To what I have found, this (below) seems to be the way to "PUT" data, but does not work.
How should data be "PUT" using XMLHttpRequest()?
var string = encodeURIComponent("Text String!");
var req = new XMLHttpRequest();
req.open("PUT", "example/data.txt", false);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.setRequestHeader("Content-length", string.length);
req.setRequestHeader("Connection", "close");
req.send(string);
I have tried "text/plain" as the "Content-type", but the browser console gives me a "syntax error" in the resource I want to replace or tells me "no element found" if the resource is blank.
PUT creates or replaces a resource. As such, the Content-Type header field should contain the content type of the representation you are sending. For further details see section 4.3.4 of RFC 7231.

How to send enctype as multipart/form-data in jquery POST

I know there are many questions about encoding forms as multipart/form-data, but it seems as if most are related to uploading files. I am not interested in uploading files; nor do I have an actual form. I simply have a button which calls the following function on click:
$.post("http://html5.validator.nu/",
{
headers: { 'Content-Type': 'multipart/form-data' }, //this doesn't work !
content:"<!DOCTYPE html>"//the value of content doesn't matter at the moment
},
function(data){
print(data);
});
The AJAX request executes, but the response from validator.nu (the server) is this:
application/x-www-form-urlencoded not supported. Please use multipart/form-data
How can I encode the form using multipart/form-data without actually having a form (just in query)? Is there a line or two that I can add to the request that will do this?
Thanks
"multipart/form-data" isn't as important to the httpd as it is to the UA. Your browser sees enctype="multipart/form-data" and does special handling, the most significant part of which is in the way it shapes the MIME request it sends on to the httpd.
Here are the most relevant parts of an actual multipart/form-data HTTP request (captured using requestb.in):
Content-Length: 533
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryEknFTYZrogVNxemQ
------WebKitFormBoundaryEknFTYZrogVNxemQ
Content-Disposition: form-data; name="foo"; filename="foo.png"
Content-Type: image/png
PNG
[PNG data elided]
------WebKitFormBoundaryEknFTYZrogVNxemQ
Content-Disposition: form-data; name="Submit"
Submit
------WebKitFormBoundaryEknFTYZrogVNxemQ--
All of that being said, constructing these MIME parts can be a bit of a PITA, so if you can find a library to do that for you (such as the one suggested by #Innovation) it's probably worth looking into.
I had the same response while accessing validator.nu
with the jQuery.post or $.ajax command and decided
not to use jQuery after exhausting the various
possible ajax parameter configurations. It appears
that validator.nu API is more picky than other API
about the format that it receives from jQuery.
The NON jQuery javascript commands that worked for
me were FormData and Append.
It is also important to note the API requirements
available at:
github dot com/validator/validator/wiki
The full code can be found at:
https://github.com/VividVenturesLLC/validatorNu-JSwebServiceAPI
// Following code formatted to fit in the
// response text area, some lines may fail
// syntax check!
//Initialize some variables
//the entire script is written around the API of validator.nu webservice
var validate = 'https://validator.nu';
// developer.mozilla.org/en-US/docs/Web/API/FormData/FormData
var formData = new FormData();
// developer.mozilla.org/en-US/docs/Web/API/FormData/append
formData.append('charset','UTF-8');
formData.append('out','json');
formData.append('laxtype','yes');
formData.append('parser','html5');
// developer.mozilla.org/en-US/docs/Web/Guide/Using_FormData_Objects
// JavaScript file-like object...
var blob = new Blob([currentPageMarkup], { type: "text/html"});
formData.append("content", blob);
var oXHR = new XMLHttpRequest();
var validationTestResults = null;//expect response from validate webservice
oXHR.onreadystatechange = function(){
if (oXHR.readyState===1){//OPENED
console.log("<------- Opened communication at: " + validate +
" in script: test_valid_html.js -------------->");
}
if (oXHR.readyState===2){//HEADERS_RECEIVED
console.log("<------- Received AJAX headers from: " + validate +
" in script: test_valid_html.js -------------->");
}
if (oXHR.readyState===4){//done
console.log('oXHR.response: ' + oXHR.response);
//requested a response in JSON format
validationTestResults = JSON.parse(oXHR.response);
displayMessages(validationTestResults);//custom display function
var vtrStatus = getStatus(validationTestResults);
/* put id=validation_log in a tag in the web page under
test to output the results because
remember all the variables lose scope when
leaving this function
*/
//document.getElementById("validation_log").innerHTML=vtrStatus;
console.log("HTML5 validation status: " + vtrStatus);// this is the message you want
console.log("<------- Ending AJAX call in script: test_valid_html.js -------------->");
}
};//end onreadystate change
oXHR.open("POST", validate);
oXHR.send(formData);

IE cant print response header (from ajax) as utf8

I use PJAX to change pages on my site and for every new page I send a Response Header with a new a Page-Title header. This header contains can contain åäö, and IE outputs this incorrectly. In IE's Developer Tools I can see the Page-Title response without problem, but when I use document.title = ... to update the title it displays wrong.
My response header looks like this:
Page-Title: Mårtensson
UTF8 is confirmed, I can see Content-Type: text/html; chartset=UTF-8 in both IE's and Chrome's Dev Tools.
This is how I update the title:
$.ajax({
url: State.url,
type: 'get',
beforeSend: function(xhr){
xhr.setRequestHeader('X-PJAX', true);
},
success: function(resp, status, xhr) {
document.title = xhr.getResponseHeader('Page-Title');
// other code
}
});
This works in Chrome but in IE it outputs as MÃ¥rtensson. If I use decodeURIComponent(escape(document.title = xhr.getResponseHeader('Page-Title'))); it outputs fine in IE, but then in Chrome I get Uncaught URIError: URI malformed.
How can I get it to work in both browsers?
Solution
I fixed it by running htmlentities on the string before I output it as a response header. I then decode the string in javascript before I set it as title.
document.title = decodeEntities(xhr.getResponseHeader('Page-Title'));
you can check RFC 5987 that specifically deals with this issue. Basically, keep in mind that if you use UTF-8 characters in a HTTP header field, you are on your own and different browsers will have different behavior.
If you really need to use a header for this, you should use some kind of escaping (server side) and unescaping (client side) to make your title fit into the ISO-8859-1 charset.

File upload using jquery works in Firefox but not in Internet Explorer

This javascript code
$(document).ready(function () {
var uploader = new qq.FileUploader({
element: document.getElementById('file-uploader'),
action: 'api/GradeCheckIn',
debug: true,
onComplete: function (id, fileName, responseJSON) {
alert(responseJSON[0].ValidationErrors[0].ErrorMessage);
}
});
});
works like a charm in Firefox. The file is posted to the server and the result is returned to the client. The data returned is json btw.
But in Internet Explorer I get the message Do you want to open or save GradeCheckIn (2 bytes) from localhost. like I'm trying to download a file.
How can I solve this? I want the same behaviour in Explorer as in Firefox.
Edit after comments:
The content-type in the request header differs between IE and FireFox:
IE: multipart/form-data; boundary=---------------------------7dc2ec8205b2
Firefox: application/json; charset=utf-8
This is even though I explicitly set the content-type in the code where the request is made:
xhr.open("POST", queryString, true);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.setRequestHeader("X-File-Name", encodeURIComponent(name));
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
xhr.send(file);
I still can't solve this. All ideas are highly appreciated. Please let me know if I should provide more information.
Most browsers dont handle application/json as the return content type. If you want to still write the json and read it out of the iframe, you might just consider 'hacking' the response and send back your content, but with the mime type of text/html.

Categories

Resources