Dojo's xhrPost & Firefox - javascript

I have a login script that sends username/password as json to a server. It uses POST so on the client-side it's done with xhrPost. The problem is that when I try to login with Firefox, browser doesn't make POST request but instead it makes OPTIONS request and doesn't actually send any parameters. Code POSTs great in Chrome & Safari so there's definitely something going on with Firefox. Login server is on different IP:port so it's cross-domain request, I don't know if that matters.
Here's the code:
dojo.xhrPost({
url: settings().get('login_server'),
postData: dojo.toJson({username:user,password:pass}),
handleAs: 'json',
headers: { "Content-Type": "application/json", "Accept": "application/json"},
load: function(data,status) { ... },
error: function(error,status) { ... }
})

You cannot reliably use XMLHttpRequest across browsers to do a cross-domain post unless the server supports HTTP access control. That is why Dojo is doing an OPTIONS request, to check for the Access-Control-Allow-Origin header.
You can use dojo.io.iframe to POST to another domain, but checking for successful completion isn't possible unless the login endpoint returns a specially formatted page (basically containing JSON inside a textarea).
Browsers that support XMLHttpRequest Level 2 can make cross-domain requests. That is why it works in Chrome/Safari, etc.

Related

Single HTML / JS (jQuery) app loaded from file:// URL in FF can make network calls, and receive responses, but response body is empty

I have a simple app that I'm writing in a single web page, that is only ever going to live on a network share. It is meant to grab data from a webservice and present it nicely. I'm deving it on my local PC in Firefox 26.0. I'm using jQuery to help with this. The data coming back is XML (from jira, fwiw).
The calls are being made, and the response is being received by the server. The FF web dev tool tells me the response is a 200 OK. However, the response body is empty. The error callback gets called every time.
The code:
$.ajax({
type: "GET",
url: "http://myjiraserver?lalalalala",
dataType: "xml",
crossDomain: true,
processData : false,
success: function(response) {
var result = "";
$(response).find("item type").map( function() {
if (this.text == "Risk") {
result += "<p>" + this.text + "</p>";
}
} );
$("#test").html(result);
},
error: function(xhr, status, error) {
alert('Error: status = ' + xhr.responseText + " " + error + " " + status );
}
});
I run the same code in IE8 and it gets the response, which jQuery successfully parses. (I'd dev it in IE8 but I need the dev tools that FF has; and chrome doesn't work on this PC - another story)
Given I know the webservice is working, and the js works in IE, my working theory is that this is some sort of cross domain issue, but I really have no idea!
Any idea as to what the issue might be? Suggestions for how to work around it?
The fact that it's working in IE is because JQuery uses activex rather than XmlHttpRequest in ie for ajax requests. Standard XHR requests don't allow for local files to access remote servers (or vice versa).
Even if your code ran from a web server, this wouldn't work in firefox or chrome. I see you're using the 'crossDomain' parameter, but you've misinterpreted it. From the jquery docs:
crossDomain (default: false for same-domain requests, true for cross-domain requests)
Type: Boolean
If you wish to force a crossDomain request (such as JSONP) on the same domain, set the value of crossDomain to true. This allows, for example, server-side redirection to another domain. (version added: 1.5)
So you set crossDomain to true if you're using jsonp, but you're using a local server-side redirect to the cross-domain resource.
Normally to access a cross-domain api via javascript, you would either use JSONP, or you would use CORS (Cross Origin Resource Sharing), unfortunately neither of which is supported by jira. (This article states Jira has effectively killed javascript API access). A third option is, you could set up a middle-man server which fetches the data and returns it to your local page. That middle-man page would have to have CORS enabled. Here's an example of how to enable CORS headers in PHP: CORS with php headers
Your javascript would then have to refer to your middleman page and pass it the api page you want to access:
$.ajax({
type: "GET",
url: "http://middeman.com?page="+escape( 'http://myjiraserver?lalalalala' )+"&method=GET"
...
);

Access is Denied error on jQuery ajax call to https URL

I am trying to set up a client script to make an ajax call to a web service on a different domain, to make things more complicated the web service is HTTPS. The service is MVC4 WebApi (REST). The client script is also running from a HTTPS domain. I am getting an "Access is denied" error when I try to make the call. For example, I have a script running at https://domain_a.com/clientpage/script.js and making a call to https://domain_b.com/serviceapi/method/, the ajax error callback reports the Access is Denied error. I took this exact scenario and tested without SSL in the mix, so http to http, and then the ajax call works fine, I also set $.support.cors = true, and also tried crossDomain: true in the ajax options. Is this supposed to work with https, am I missing something, or will this simply not work? Any suggestions?
Here is a sample of my ajax call:
$.support.cors = true;
$.ajax({
url: requestURL,
crossDomain: true,
type: "GET",
headers: { "Authentication": "user#domain.com:" + hashcode },
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
alert("call succeeded");
},
error: function (response) {
alert("call failed");
}
});
jQuery version: 1.10.2
browser: IE9 (hoping this can work with multiple browsers: chrome, firefox, safari)
web service: MVC4 WebAPI .NET 4.5
UPDATE:
I did some more testing and ruled out the web service as the issue, it appears to be an issue with the underlying xmlhttprequest object or the browser. I ran fiddler while testing the https to https scenario (fiddler is set to decrypt https traffic and I tested this) and there is no traffic whatsoever when I try to make the ajax call and it fails. That leads me to believe its the browser. Here's the list of scenarios I tested and the result:
http to http, same domains: success
http to http, different domains: success
https to https, same domains: success
https to https, different domains: fails. <<-- I would expect this to be one of the most common scenarios for web service usage, some ISV makes a service available to customers who call it from a different domain, and they want to use SSL.
Sometimes when developing in a development environment, most companies don't dish out the money for a ssl cert and use a self signed cert which can cause the browser to throw up the big red screen that says warning and you have to continue. In Chrome i know when you do this kind of stuff using an iframe, it crashes the iframe window unless i accept the cert during that session.
With that said, are you sure that domain_b.com has a valid ssl cert? if not have you accepted the SSL cert in your browser session? You can test this by manually copying the URL from the ajax call, paste it in the window and visit it. Once you accept the cert, it should start working.
You may see an image like this:
If so, click Proceed anyway
I didn't directly solve the issue of calling cross-domain on https, however I did come up with a workaround that was acceptable in my situation: Instead of attempting to call cross-domain directly from the client I built a web service that sits between the client and server on the other domain. The web service is on the same domain as the client and so the client can call this service without any trouble. The web service then uses standard HTTP communications to send requests to the remote server and accept responses, the responses are then returned back to the client. Basically the local web service acts like a sort of proxy between the clients and remote server.

REST request from app works, but not from javascript

I'm building an app that has to get and set data at a remote web service through requests. When I use the jQuery GET request it works fine, I can request data from the service without any problems, but when I use PUT I get some erros:
OPTIONS http://myurl.com 501 (Unsupported method
('OPTIONS'))
OPTIONS http://myurl.com Origin null is not allowed by Access-Control-Allow-Origin.
I've tried almost anything to get this to work, but it won't work. I've download a chrome app called REST Console, which can make custom REST requests. The strange thing is that I can interact with my server over that app but not through my javascript!
This is the javascript:
$.ajax({
url: 'http://myurl.com',
type: 'PUT',
data: '<time>16:00</time>',
success: function(data) { alert(data); }
});
Could anybody tell me what is going on here?
First ensure you're serving the page that runs the script from a web server, not directly from the file system.
I'm guessing your service at http://myurl.com is at a different host name to the host name your page is being served from? For it to work in this case you need to implement HTTP headers to support Cross Origin Resource Sharing.
Your service at http://myurl.com needs to handle an HTTP OPTIONS request, the response to which should be a set of HTTP headers (with no content) as follows:
Access-Control-Allow-Origin: http://url-of-page-with-javascript/
Optionally you can also specify Access-Control-Allow-Credentials, Access-Control-Allow-Headers and Access-Control-Allow-Methods. See the full specification here.
You'll also need to add the same headers with the same values when your server responds to the PUT request - obviously the content will also be included with this response.

Connect to webService using AJAX and CORS

I'm developing a phonegap App with html5, css, js and jQuery Mobile and I need to connect to a webservice which is already done and fully working. The problem is the Access Control Allow Origin and the Cross Domain. As if it wasn't hard enough I have to think about the authentication too, essential to connect to the web service. I already done my research, read a lot of tuts, tried a lot of solutions, some of them using jsonP which looked to me the closest one to work. The thing is I'm new at this and no tutorial looked good, so hopefully someone here could lead me the way. The webService was build in asp.net and I have full access to it if it's needed. I'm using AJAX to make the "call" but I can't pass the ForeFront authentication .
Here's the JS+AJAX code:
function conteudoProg() {
var webMethod = "myURL";
var credentials = {
username : "myUser",
password : "myPass"
};
$.ajax({
type : "GET",
url : webMethod,
//data: credentials,
contentType : "application/json; charset=utf-8",
dataType : "jsonp",
success : function(msg) {
alert(msg);
},
error : function(e) {
alert(e.status + " " + e.statusText );
}
});
}
If I change my dataType from jsonp to json, I get this error:
OPTIONS https://myURL 440 (Login Timeout)
XMLHttpRequest cannot load https://myURL Origin http://127.0.0.1:8020 is not allowed by Access-Control-Allow-Origin.
With jsonp, the error looks like this:
Resource interpreted as Script but transferred with MIME type text/html: "https://myURL/CookieAuth.dll?GetLogon?curl=Z2FWSInqueritosZ2FServā€¦1820135927463_1359737732559Z26_Z3D1359737732605&reason=0&formdir=3". jquery-1.8.2.min.js:2
Uncaught SyntaxError: Unexpected token <
Requests to another domain will cause a pre-flight OPTIONS request to see whether the requesting domain can make calls to this domain.
The receiving end needs to emit the correct headers or your browser will block the request and give you the error you posted.
Say you are requesting from mydomain.com to webservice.com
Then webservice.com/api should emit these headers:
Access-Control-Allow-Origin: http[s]://mydomain.com
Access-Control-Allow-Credentials: true # if you want cookies
Access-Control-Allow-Headers: Content-Type, X-Custom-Header # any extra headers you want to send
Make sure the webservice knows about OPTIONS requests. It really only needs to emit some CORS headers, it doesn't need to do anything else (like process a request to it's API).
You don't need to change anything in your AJAX handler, it will pass as any other request. If you want cookies make sure to set http_request.withCredentials = true;
Keep in mind that an HTTPS URL is considered to be different from an HTTP domain and make sure your HTTPS certificate is valid, if it's not valid the request may fail silently. If you're using a self-signed certificate (for testing) add it to your browser or OS whitelist.
Cross domain request from HTTP to HTTPS aborts immediately
As for compatibility. Earlier versions of Internet Explorer (8 and lower) use ActiveXObject, this API is very bad at CORS. It doesn't support authentication/cookies or custom headers (such as Content-Type: application/JSON). I would recommend a JSONp fallback.
The code does not work because when you tell jQuery that the .ajax method expects a dataType json, that's what's trying to parse the response into. If the response is html then you should use a dataType html (or none, to let the default intelligent guess do it's work). See jQuery.ajax() dataType for more info.

Jquery Ajax Request Called Twice and the first request does not send token in header

I am using ajax to call a WCF REST based service.
The ajax method is called before the page gets loaded.
I wish to send a "Token" in the header of ajax request. In fiddler this is what I see:
1.)A request to the service without the token in the header.(AJAX Call failure)
2.)A request to the same service with the token in the header.(AJAX Call Passed)
After that everything works fine on chrome and safari. But there is only one service call on IE 10 and Mozilla. As a result the service call fails in IE 10 and Mozilla since there is no token in the header of the request.
This is the method that I call:
function callservice (method, serviceUrl, params, successHandler, errorHandler) {
$.ajax({
crossDomain: true,
type: method,
url: serviceUrl,
beforeSend: function (XMLHttpRequest) { XMLHttpRequest.setRequestHeader("Authorization", Token); },
contentType: "application/json; charset=utf-8",
dataType: "json",
success: successHandler,
error: errorHandler
});
function photos(data) {
alert(data);
console.log(data);
};
}
I control both the Web Service and the application(Which calls this Web Service). This problem does not arise when both the application and web service are hosted on the local host.In that case there is only one successful service call. But there are two AJAX calls when there is a cross domain call.
My question is why doesn't the AJAX request send the token in the first attempt?
And why does the token get sent only in the second AJAX call?
Any kind of help will be greatly appreciated.
The problem was with CORS.Earlier,browsers did not allow ajax requests to be made to a domain which is different than that of a client as it was considered as a security threat.Modern browser's can make cross domain ajax request's as long as the server co-operates with the client.So this is what actually happens when there is a cross domain request from a browser:
1.)First the browser sends 'Preflight' request to the service to gather authorization information(which was a request with the header method as 'OPTIONS' in my case) from the WCF service. In return the Web Service sends Access Control Allow Origin as a part of its response header.And the error being displayed on fiddler as a result of this request was a HTTP 500 error.This AJAX request has nothing in the data field since it was just a way to find the authorization details of the WCF service.
2.)Chrome and Safari then made a second request to the Web Service now that they have the authorization details of the service.Whereas Firefox and IE did not prefer to make a second ajax request to the service since there was an HTTP 500 error for the pre-flight request. Hence both Chrome and Safari were able to communicate with the service.
So the solution was to modify the response from the WCF service in case there is a 'Preflight request' made to it.I modified the response sent by the service in case there is a 'Preflight request' to send an HTTP 200 OK Response. This allowed browsers like IE and Mozilla to send the actual request after the preflight request.
Here is one of the sources which I referred:
http://www.bennadel.com/blog/2327-Cross-Origin-Resource-Sharing-CORS-AJAX-Requests-Between-jQuery-And-Node-js.htm
Hope this helps people facing the same problem.
Cross domain call is under the same origin policy. You can not make the calls by default. You need to use CORS or JSONP or a proxy.
XMLHttpRequest: Unless they changed it, With MS Explorer you'll need to use ActiveXObject("Microsoft.XMLHTTP"). For my ajax call made in plain JS, i use this line to create the object according tyo the browser:
if (window.XMLHttpRequest){
//for most BRowsers
r = new XMLHttpRequest();
} else{
//for Explorer
r = new ActiveXObject("Microsoft.XMLHTTP");
}
then apply your beforesend to the object created here (r) in my case. I truely believe this is your issue with EI. But not tested.

Categories

Resources