Differences with cookies between getJSON and ajax when doing CORS - javascript

I have a REST based web service running in my Tomcat Java EE servlet container.
I am writing a jquery client that is from another domain and is therefore using CORS. It hits the login web service, followed by another call. I originally implemented these two calls using getJSON and the calls were working fine, but I wasn't getting the JSESSIONID cookie to stay on the second call, so the web service had an unauthenticated session and threw and error.
After doing research, I ran across doing ajax withCredentials and thought that this is what I needed to do. The AJAX login call failed in preflight.
So, when I sniff the traffic to my webserver, the getJSON calls run as two gets and look fine except that the cookie doesn't come back with the second call. When I run the ajax call, it does an OPTIONS call to my server, gets a 200 status back on the client and then fails inside of jQuery for reasons I can't seem to find.
var jqxhr = jQuery.getJSON(loginUrl, {
xhrFields: {
withCredentials: true
},
crossDomain: true
})
.done(function(response) {
AgileJurySessionKey = response;
AgileJuryLoggedIn = true;
doneCallback();
})
.fail(function() {
failCallback();
});
Here is the AJAX version of the same call:
jQuery.ajax(loginUrl, {
type: "GET",
contentType: "application/json; charset=utf-8",
success: function(data, status, xhr) {
alert(data);
doneCallback;
},
error: function(jqxhr, textStatus, errorThrown) {
alert(errorThrown);
failCallback;
},
xhrFields: {
withCredentials: true
},
crossDomain: true
});
What's different between these two?
Here's the filter I put in to the web server for CORS:
/**
* This filter allows access to our web services from clients that are not on the local domain
*
* #author Xerox
*/
public class CorsFilter extends OncePerRequestFilter {
/* (non-Javadoc)
* #see org.springframework.web.filter.OncePerRequestFilter#doFilterInternal(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain)
*/
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
response.addHeader("Access-Control-Allow-Origin", "*");
if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
// CORS "pre-flight" request
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.addHeader("Access-Control-Allow-Headers", "X-Requested-With,Origin,Content-Type,Accept,Set-Cookie");
response.addHeader("Access-Control-Max-Age", "1800");//30 min
}
filterChain.doFilter(request, response);
}
}

jQuery.getJSON(loginUrl, {
xhrFields: {
withCredentials: true
},
crossDomain: true
})
The second parameter of $.getJSON is the data you want to send, not an options object. To use those, you will need to call $.ajax directly.

getJSON isn't really a method, it's just a convenience function that is basically a shortcut for:
$.ajax({
dataType: "json",
});
So basically, $.getJSON() should behave the same as $.ajax() with the dataType set to "json"

Due to continual issues with CORS, I finally gave up on this one as intractable and worked the problem from the other end.
I used a session key coming back to track the length of the session and then re-attaching security based on this, which is how I'd designed security to work in the first place.

Related

Response for preflight has invalid HTTP status code 400

I'm trying to make a REST call (POST) using AJAX. This is my AJAX code
<script>
var settings = {
"async": true,
"crossDomain": true,
"dataType": "json",
"url": "http://localhost:port/service/myservice",
"method": "POST",
"data": '{jsondata}',
"headers": {
"accept": "application/json",
"Authorization": "authValue"
}
}
$.ajax(settings)
.done(function (response) {
console.log(response);
});
</script>
Initially I got this error: XMLHttpRequest cannot load http://localhost:port/service/myservice. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 400.
To resolve this issue I added the following code in my dropwizard application
Dynamic filter = env.servlets().addFilter("CORS", CrossOriginFilter.class);
filter.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,PUT,POST,DELETE,OPTIONS");
filter.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
filter.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
filter.setInitParameter("allowedHeaders", "Content-Type,Authorization,X-Requested-With,Content-Length,Accept,Origin");
filter.setInitParameter("allowCredentials", "true");
filter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
After adding this my initial exception went away, but I'm getting the following exception: XMLHttpRequest cannot load http://localhost:port/service/myservice. Response for preflight has invalid HTTP status code 400
Is this issue related to CORS? What am I doing wrong here?
UPDATE
After doing more debugging I found this behavior. When sending the request without the Authorization header I'm getting 415 (Unsupported Media Type) error.
I think something wrong with my AJAX code, can someone please help me find the issue? Thanks.
You may try here mentioned as complete answer in this thread.
$.ajax({
type:"POST",
beforeSend: function (request)
{
request.setRequestHeader("Authority", authValue);
},
url: "http://localhost:port/service/myservice",
data: "json=" + escape(JSON.stringify(createRequestObject)),
processData: false,
success: function(msg) {
$("#results").append("The result =" + StringifyPretty(msg));
}
});
try to add the following to your settings?
xhrFields: { withCredentials: true }
if you need to pass JSON data in the AJAX call, you need to specify content-type as json/application, so the server knows you are trying to send JSON data. But that will change the default content-type of the call and the call will qualify for pre-flight checking, which need proper CORS enabled client & server request.
For easier use case, do not use JSON.stringify() when you pass data, just make a simple string with {key:value, key:value, ...} format, and pass the string as the data. The Ajax call serializes the data by default and does the right thing, and the call stays as a single POST call to the server, where as the pre-flight mode is two calls.

Trying to make a Cross domain post call in IE9

I am trying to make a cross domain post call in IE9 below is my code:
$.support.cors = true;
var data = {"userid":uid,"email":email,"password":password};
if (isIE () && isIE () <= 9) {
$.ajax({
type: 'post',
crossDomain: true,
url: postUrl,
cache:false,
contentType: 'application/json; charset=utf-8',
dataType: 'jsonp',
data:data,
jsoncallback:'localJsonpCallback',
jsonp:false,
success: function (data) {
console.log(data);
},
error: function (status){
console.log(status);
$("#error").html("Incorrect E-mail Entered. Please Re-Enter Your E-mail ");
}
});
}
function localJsonpCallback(json) {
if (!json.Error) {
alert("success");
}
else {
alert(json.Message);
}
}
However, When I look at the call in fiddler I am getting a 405 error and the request header is showing a GET:
GET postUrl?format=json&userid=123456&email=test%40test.com&password=Password1&_=1434232587917 HTTP/1.1
Why is it if I am making a post that in the request header it is showing a Get? Am I doing anything syntactically wrong with my call?
Your request looks okay and from the server response you're describing, it's a "problem" with server, HTTP status code 405 means bad method, i.e. server doesn't allow POST requests. But it's still strange that it would translate those to GET, but I still think it's because of server implementation, not an error on your side. You could try with a tool like curl and see what response headers you get, but it won't help much if it's an server bug/error.
If you do not have control over the server, the only things remaining is to contact the owner and ask them to allow post request or send a GET request, although it's really bad to send non-encoded login data.

Why is the Cookie Header not set on my same-domain $.ajax request within phantomjs?

I am issueing requests within the same domain via jQuery's $.ajax(request) function. Unfortunately the Cookie header is never set (neither POST nor GET) and I don't know why.
If i understand jQuery Ajax correctly, the Cookie header should be set according to the pages cookies (like document.cookie). I've traced document.cookie and to me it seems that it is updated correctly (according to the Set-Cookie header) with every xhr response. But when debugging the Requests all have the Cookie header not set.
Simplified version of how my requests are created:
var request = {
type: 'POST',
data: 'theDataIsCorrect=true',
async: false,
url: '/a/relative/url/like/this?maybe=even&with=this',
// withCredentials does not help (ignored on same domain request anyways)
xhrFields: { withCredentials: true },
beforeSend: function(jqXHR) { doStuff(); },
error: function(jqXHR, textStatus, errorThrown) { errorHandling(); },
complete: function(jqXHR, textStatus, errorThrown) { complete(); }
};
$.ajax(request);
How my scripts work:
Domain is opened in the browser (no cookies yet)
Login via $.ajax xhr Request (response has cookies)
Requests via $.ajax to pages I want to see/modify/ect. (requests miss Cookie header)
GO TO 3. || GO TO 5.
Logout via $.ajax
EDIT:
I am using phantomjs [1.9] as browser. This problem does not occur when using Firefox or Chrome. I've found this question, where someone is describing a similar behaviour: phantomjs - Cookie is not being sent for any XHR/POST/GET AJAX requests

No response for ajax PUT, but response on ajax GET and POST

Making a same-domain (http://example.host.com -> http://example.host.com) POST, the responseXML contains the expected data. Making the same request, but as a PUT, the responseXML is null for a successful request. I'm using jQuery.ajax, but I tested a manual implementation as well with the same results.
var settings = {
url: '//' + window.location.host + window.location.pathname,
type: getRequestType(),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
data: JSON.stringify(parms),
error: function(jqxhr, status, error) {
console.log('error');
},
success: function(data, status, jqxhr) {
console.log('success:', data);
}
};
$.ajax(settings);
I am using a very simple server request handler that returns a simple json-formatted string.
#require_http_methods(["PUT"])
def proxy_update(request):
out = {
'type': 'update',
'success': True
}
return HttpResponse(json.dumps(out), mimetype="application/json")
What is the explanation for this?
Per jQuery documentation, not all browsers support all verbs. Quite possibly the browser you are using does not support the PUT verb. Try another browser. If the failure is not with the browser then it is also possible that the server may be configured to ignore the PUT verb.

XmlHttpRequest status 0 even when the call to service succeeded

I call to Wcf Service via Jquery(JS), after a long time of trying it works.
The call to service is as follows:
function CallService() {
var request = { userName: "aaa", password: "123" };
var jsondata = JSON.stringify(request);
$.ajax({
type: "POST", //GET or POST or PUT or DELETE verb
url: "http://localhost:xxxx/Service1.svc/GetUser", // Location of the service
data: jsondata, //Data sent to server
contentType: "application/json; charset=utf-8", // content type sent to server
dataType: "json", //Expected data format from server
processdata: true, //True or False
crossDomain: true, //True or False
success: function (result) {
alert('success');
},
complete: function () {
alert('completed');
},
error: function (xhr, status, message) {
alert('error with status - ' + xhr.status);
}
});
}
I put BreakPoint in GetUser function that is in service and when I call the function CallService I go to the BreakPoint of Service (which means it works!).
Service function works great and returns the correct data, but to Jquery I get the Error function with status 0.
In addition, in the console I see a red error:(Not generally understood as an error)
POST http://localhost:xxx/Service1.svc/GetUser
What could be the problem?
It could be that you're making a cross-domain request. Note that the same host name with different ports are considered different domain. For example: http://localhost:20 and http://localhost:40 are considered different domains.
In your case, it could be that your browser supports CORS and therefore requests to a different domain are still sent. That's why when you debug on server side you see it works, but when the browser receives response from the server, the response is discarded and an error is raised because of missing Access-Control-Allow-Origin header from the response or having the Access-Control-Allow-Origin header but with a value different than your domain.
My problem was that in User object I had DateTime.
When I turned it to String it worked.

Categories

Resources