7

I am trying to build a JSONP request with AngularJS's $http Service and on errors I am getting wrong http status code 404 instead of 500, and the body of the page is missing too.

So here is the scenario:

The URL I am calling returns a 500 Internal Server Error with a JSON output containing the error message:

http://private.peterbagi.de/jsfiddle/ng500status/api.php?code=500&callback=test

index.html
(see it in action: http://private.peterbagi.de/jsfiddle/ng500status/ )

<!DOCTYPE HTML>
<html lang="en-US">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src= "//ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
    <script src= "app.js"></script>
</head>
<body ng-app="app" ng-controller="UploadController">
    <button ng-click="upload(200)" value="OK">OK</button>
    <button ng-click="upload(500)" value="Fail">Fail</button>
    <pre>{{response | json}}</pre>
</body>
</html>

app.js

var app = angular.module('app', []);

app.constant('BASE_URL','http://private.peterbagi.de/jsfiddle/ng500status/');

app.controller("UploadController", ['$scope','Upload','BASE_URL',
    function($scope,Upload,BASE_URL) {

    $scope.upload = function(code) {

        Upload(code).then( function(response) {
                $scope.response = response;
            }, function(error) {
                $scope.response = error;
            } );
    }
}]);

app.factory('Upload', ['$http','BASE_URL', function($http,BASE_URL) {
    return function (code) {
        var callUrl = BASE_URL + "api.php?code="+code;
        return $http({
            method: 'JSONP',
            url : callUrl+"&callback=JSON_CALLBACK"
        });
    }
}]);

When you click on the Fail button the returned status is 404 and the response body is missing too.

output

{
  "status": 404,
  "config": {
    "method": "JSONP",
    "transformRequest": [
      null
    ],
    "transformResponse": [
      null
    ],
    "url": "http://private.peterbagi.de/jsfiddle/ng500status/api.php?code=500&callback=JSON_CALLBACK",
    "headers": {
      "Accept": "application/json, text/plain, */*"
    }
  },
  "statusText": "error"
}

In Chrome DevTools Network panel you see the correct response code (500) and I would expect to see the same result in the Angular output.

Why is this happening or what am I doing wrong?

Update:

I built the a similar example, but with GET-Requests instead of JSONP and it works well: http://private.peterbagi.de/jsfiddle/ng500status2/

It seems to be a JSONP specific problem. Nevertheless it doesn't solve my initial problem, because I have to work with Cross-Domain Requests. Any ideas?

buggy1985
  • 919
  • 8
  • 24

1 Answers1

8

According to the angular source's httpBackend.js line 63 and line 190,

the status of a 'JSONP' request can only be -1, 404, or 200.

The reason is that in angular 'JSONP' request is not an XMLHttpRequest. AFAIK there is no way to handle the specific error code in this way. The only thing javascript know is whether this request succeeded.

UPDATE:

Here in my answer above:

the status of a 'JSONP' request can only be -1, 404, or 200.

This means the status given by angular for the response of this request.

XMLHttpRequest is the standard request type we normally used in ajax.It sends request to the server and fetch the response so you can see the HTTP status code(200,404,500,401,etc.) given by the server side.

However, it has limits like in most cases you cannot make a cross-domain XMLHttpRequest (unless you've set up the allow-cross-domain header on the server side).

In this way we need JSONP to help us make cross-domain requests. In fact 'jsonp' is appending <script> tags to your document (which are always removed when request finished) whose src attributes are the URLs you given.

In angular, listeners are added to this <script> to track the status of the request. However, these listeners can only tell whether the script is loaded successfully. If it succeeded, angular set the status of your response to 200. If not, 404 is assigned. That is why the status field is either 404 or 200, regardless of what the server actually gives.

MMhunter
  • 1,431
  • 1
  • 11
  • 18
  • I meant the response, not the request, actually I can see from Firefox the network received a status code other than 404, but in Angular JS, no matter what, the response status code is always 404. Could you explain more the difference between JSONP and XMLHttpRequest, and their responses? – tomriddle_1234 Aug 01 '16 at 15:02
  • @tomriddle_1234 yep the 'status' i mentioned is exactly for the response. I have updated my answer and hope it helps. – MMhunter Aug 01 '16 at 15:46
  • thank you so much, this made me thing through lots of things, but back to the original question, if the JSONP request can not actually give me the response code from the serve, how can I get it through a JSONP request-response? Is it possible, or not? – tomriddle_1234 Aug 02 '16 at 08:39
  • I am afraid not. The error event given by the script element shows no additional information than an 'error' for the event's type. The only workaround i can think of is to use protocols like *json-rpc*, making every response success and wrapping the actually status code in the response body. But that seems not practical since if you could modify the server, there was no reason for you to use *jsonp* instead of *xhr*? – MMhunter Aug 02 '16 at 09:00
  • The reason I chose JSONP is the Cross-Origin Resource Sharing issue I experienced, it keeps giving CORS error if I just use a GET. Some resource indicated I can use JSONP instead, but now it apparently not appropriate. Indeed I have control on my Phalcon backend, it's on a different domain, since I knew it is good to separate the front and backend. I intended to make a RESTful API, and I believe there's no problem there and all headers were set properly in the .htaccess file, so how can AngularJS frontend fetch some data from the backend in a different domain? – tomriddle_1234 Aug 02 '16 at 13:59
  • @tomriddle_1234 i believe if you have control on the backend, maybe the best practice is to add the 'access-control-allow-origin' header and make a CORS request. Making CORS GET requests in angularjs has no difference with making GET requests in same domain.. – MMhunter Aug 02 '16 at 15:36