UPDATE Mar 21
After more investigation it looks like this is also showing a Play framework bug an XHR limitation on error status:
Play 2.2.1 SimpleResult 4xx Response Body Possible via CORS XHR? That, or the RESTfulness I seek is not possible with 401 codes.
Still an issue in both spaces since a 401 gets a 0 status code in Angular $http as below. So much for RESTfulness! All I want to do is provide a response body for 401 Unauthorized. From the W3C page on 4xx: "User agents SHOULD display any included entity to the user."
UPDATE Mar 20
Further investigation points to an issue within XMLHttpRequest itself, but please let me know if I am missing something below. I am spitting out the raw XHR object inside the xhr.onreadystatechange() callback as defined inside Angular's createHttpBackend():
When I log in with a known good email and password, everything works fine:
But the same XHR object for a 400 Bad Request (wrong password) has an empty response but more importantly status=0, which from Mozilla docs denotes the request was never sent (but I am seeing my API server get the request and do a DB lookup, as Postman works as above):
What gives? How does Postman get a response but XMLHttpRequest on latest Chrome and Firefox is confused?
I am sending a POST request using $http and getting conflicting responses in the browser (Chrome and FF), and Postman. Postman works fine and has the body, but it looks like angular is "cleaning" up some response codes to handle weirdness on Android browsers when templates are cached, but I can't pinpoint the spot in $httpBackend where the raw XHR is happening to intercept the response body--maybe that's because the XHR is blocking/canceling the response? Because security? I do not have any interceptors, either.
The "happy path" login works fine and I get matching 200 OK responses in the console/firebug, but 400 Bad Request doesn't have a response body.
CORS is configured on nginx (see http://enable-cors.org/server_nginx.html) and since the happy login success works the setup is functional.
My call:
var onLoginSuccess = function(data, status, headers, config){
console.log('[onLoginSuccess]')
console.log(data)
console.log(status)
console.log(headers)
console.log(config)
}
var onLoginError = function(data, status, headers, config){
console.log('[onLoginError]')
console.log(data)
console.log(status)
console.log(headers)
console.log(config)
}
var loginObj = {
'username': $scope.username,
'password': $scope.password,
'grant_type': 'password',
'client_id': 'web'
}
var doLogin = $http({
method: 'POST',
url: 'https://api/v1/token',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: function(obj) {
var str = [];
for(var p in obj) {
str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]));
}
return str.join('&');
},
data: loginObj,
responseType: 'json'
})
doLogin.then(
onLoginSuccess,
onLoginError
)
Angular is modifying the error code so the browser and angular do not agree:
When I send the same request via Postman, the 400 Bad Request code is the same, but the body is also a json string, as expected.
Why is this not available via $http CORS inside the onLoginError() callback?