1

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():

angular source mod to console

When I log in with a known good email and password, everything works fine:

200 OK with response JSON

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):

enter image description here

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:

angular modifies http codes from XHR response

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?

postman works fine

4

2 回答 2

0

我认为的问题是您将 POST 正文作为 JSON 发送,而不是它需要作为查询参数发送。试试这个改变

var loginObj = $.param({
  'username':   $scope.username,
  'password':   $scope.password,
  'grant_type': 'password',
  'client_id':  'web'
});

这将使您的身体成为查询参数。也不要忘记包含jquery.js

于 2014-03-19T03:41:54.797 回答
0

问题最终出在我的 api 服务器代码上。我的应用程序只发送 Content-Type 标头,但我认为 nginx 能够提供 Allow-Origin 标头并使应用程序不知道任何标头。

请参阅Play 2.2.1 SimpleResult 4xx 响应正文可能通过 CORS XHR?为答案。Play Scala .withHeaders() 部分通过 CORS 解决了该问题。

想知道是否有办法让 nginx 处理所有标头,也许我的 proxy_pass 部分没有正确完成。

于 2014-03-29T05:27:19.230 回答