AngularJS $http wrong http status on error - angularjs

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?

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.

Related

AngularJS getting strange response from using get on json rest service with no data

This has been frustrating me a bit. I have a restful services giving JSON data running on the link: http://localhost:51133/API/SalesSystem/
So its running locally on my computer. I can get the data from this service with no problem using my WPF based interface.
Another service I am testing with is this one: http://rest-service.guides.spring.io/greeting
But from my own service something seems to go wrong somehow and I cannot figure out what goes wrong. The spring service gives me a response using a response function, but my service gives a response using a success function function. Very confusing.
Response seems to be:
{"data":null,"status":-1,"config":{"method":"GET","transformRequest":[null],"transformResponse":[null],"jsonpCallbackParam":"callback","url":"http://localhost:51133/API/SalesSystem/","headers":{"Accept":"application/json, text/plain, */*"}},"statusText":"","xhrStatus":"error"}
I looked it up and it seemed I might be needed to enable Cors, but I am not entirely sure how. I installed it from NuGet, the service is running with Visual Studio, and I added config.EnableCors(); to my WebApiConfig.
My JS script:
angular.module('demo', [])
.controller('Hello', function ($scope, $http) {
$http.get('http://localhost:51133/API/SalesSystem/')
//http://rest-service.guides.spring.io/greeting
//http://localhost:51133/API/SalesSystem/
.then(function (response) {
$scope.hello = 'hello';
$scope.district = response.data;
console.debug(response.data);
console.log(response.data)
}, function success(response) {
$scope.hello = 'hello2';
$scope.district = response;
$scope.rep = response.data;
console.debug(response.data[0]);
$scope.district = response.data[0];
console.log(success.data)
}, function error(response) {
$scope.hello = 'hello3';
$scope.district = error.data;
console.log(error.data)
});
});
My html:
<!doctype html>
<html ng-app="demo">
<head>
<title>Hello AngularJS</title>
<script src="Scripts/angular.min.js"></script>
<script src="Scripts/hello.js"></script>
</head>
<body>
<div ng-controller="Hello">
<li>Id: <button>{{hello}}</button></li>
<li>Area: {{district}}</li>
<ul ng-repeat="obj in hello">
<li>Area: {{obj.area}}</li>
</ul>
<p>The ID is {{rep.Id}}</p>
<p>The content is {{hello.Area}}</p>
</div>
</body>
</html>
I am getting it all to work with my WPF based frontend using this service, not sure why it wont work with AngularJS.
The CORS issue has nothing to do with angular. It has got more to do with the browser .
In layman terms , the cors situation is where the browser does not allow responses of other origins than the url to be processed unless the response has a certain set of headers .Of course, it is a bit more complicated than that .Since the response does not have those headers you are getting the CORS issue
For a quick fix you can disable CORS in your browser .
for example :- if you are using chrome you can run it --disable-web-security flag

Twitter API get tweets - returns CORS origin blocked

I am trying to get tweets from a hashtag. I get following erros Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://api.twitter.com/oauth2/token. (Reason: CORS header 'Access-Control-Allow-Origin' missing).
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://api.twitter.com/1.1/search/tweets.json?f=tweets&q=%23shruthirajoli&src=typd?get=%5Bobject+Object%5D. (Reason: CORS header 'Access-Control-Allow-Origin' missing)
Below is my code:
app.js
var app = angular.module('Twitter', ['ngResource']);
app.controller('TwitterCtrl', function($scope,$http,$resource){
// Create Base64 Object
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},
decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");var t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}else if(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}else if(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}}
var consumerKey = encodeURIComponent('');
var consumerSecret = encodeURIComponent('');
var credentials = Base64.encode(consumerKey + ':' + consumerSecret);
// Twitters OAuth service endpoint
var twitterOauthEndpoint = $http.post('https://api.twitter.com/oauth2/token', "grant_type=client_credentials"
, {headers: {'Authorization': 'Basic ' + credentials, 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'}});
twitterOauthEndpoint.success(function (response) {
// a successful response will return
// the "bearer" token which is registered
// to the $httpProvider
console.log(twitterOauthEndpoint );
app.$httpProvider.defaults.headers.common['Authorization'] = "Bearer " + response.access_token})
.error(function (response) {
// error handling to some meaningful extent
});
$scope.twitter = $resource('https://api.twitter.com/1.1/search/tweets.json?f=tweets&q=%23shruthirajoli&src=typd',
// {action: 'search.json', q:'angularjs', callback:'JSON_CALLBACK'},
{get:{method:'JSONP'}});
$scope.twitterResult = $scope.twitter.get();
});
app.config(function ($httpProvider) {
app.$httpProvider = $httpProvider
});
html
<!doctype html>
<html ng-app="Twitter">
<head>
<script src="http://code.angularjs.org/angular-1.0.0rc4.min.js"></script>
<script src="http://code.angularjs.org/angular-resource-1.0.0rc4.min.js"></script>
<script src="app.js"></script>
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css">
</head>
<body>
<div ng-controller="TwitterCtrl">
<table class="table table-striped">
<tr ng-repeat="tweet in twitterResult.results">
<td>{{tweet.text}}</td>
</tr>
</table>
</div>
</body>
</html>
I am running this on localhost:8000, I followed twisters instructions to troubleshoot this. Can someone let me know, if I am making any mistake. thanks.
The reason why you are getting this error is because your making the request through the browser. Browsers don't allow cross domain requests because like #allenru mentioned in the comments below: Servers need to response according to the CORS spec so the browser can proceed with the request.
You can learn more about it here :
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
The proper way to fetch tweets would be making a call to the server and have the server fetch tweets for you.
This also seems like a buggy call.
https://api.twitter.com/1.1/search/tweets.json?f=tweets&q=%23shruthirajoli&src=typd?get=%5Bobject+Object%5D.
Its not normal to have [object + object] in requests. It seems like an object that was not stringified.

No 'Access-Control-Allow-Origin' error from local domain to public API

I'm trying to create an app that will retrieve the stock name, asking and buy price from Yahoo Finance API, using Restangular. I'm having a problem with accessing a public API in the local application I'm creating when I do a GET request. This is the URL with parameters that I'm sending:
http://finance.yahoo.com/d/quotes.csv?s=MSFT+GE&f=nab
Using Postman, this get request returns an array which contains the stock name, asking price and buy price of the stock. Doing the request in postman returns the desired information without any errors.
When I do this request in my local, I get the following error:
XMLHttpRequest cannot load http://finance.yahoo.com/d/quotes.csv?s=MSFT+GE&f=nab. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
The HTML code is as follows:
<html ng-app="app">
<head>
<title>Restangular</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="//code.angularjs.org/1.2.27/angular-resource.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<script src="//cdn.rawgit.com/mgonto/restangular/master/dist/restangular.min.js"></script>
</head>
<body>
<div ng-controller="IndexCtrl" ng-cloak>
<ul>
<li ng-repeat="person in people">{{person.Name}}</li>
</ul>
</div>
And the JS:
var app = angular.module('app', ['restangular'])
.config(function(RestangularProvider) {
RestangularProvider.setBaseUrl('http://finance.yahoo.com/d');
});
app.controller('IndexCtrl', function($scope, Restangular) {
$scope.people = Restangular.all('quotes.csv?s=MSFT+GE&f=nab').getList();
});
I'm confused by this error because since this is a public API and POSTMAN can access it, I'd assume that the header is already present in the API server. Why would I get this error when requesting from my local?
Any help would be GREATLY appreciated.
Here's me making a JSONP request which fails because it's trying to receive a CSV file. I don't think restangular knows how to read CSV files either which is what you are attempting. I think you need to do the same thing as you are now but using the YQL API instead because I don't think this is going to work.
Anyway here's the code:
http://plnkr.co/edit/GokYqzJ0dXw86C6AM2UP?p=preview
The request (which goes through but fails due to the file type):
app.controller('MainCtrl', function($scope, $http) {
$scope.name = 'World';
$http.jsonp('http://finance.yahoo.com/d/quotes.csv', {
params: {
callback: 'JSON_CALLBACK',
s: 'MSFT GE',
f: 'nab'
}
}).success(function(response) {
console.log(response);
}).catch(function(response) {
console.log(response);
})
});

Web API loads via URL but get Error 404 from Angular script

I have a WebAPI method here:
http://localhost:50463/api/movies
and when accessing it from a browser it loads perfectly.
In my project (the same project as where Web API resides) when calling the method from AngularJS I get an error 500:
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
When I click the link in the error it loads the data perfectly.
The routing for WebAPI is as follows:
config.Routes.MapHttpRoute("DefaultApiGet", "Api/{controller}",
new {action = "Get"},
new {httpMethod = new HttpMethodConstraint(HttpMethod.Get)}
);
This is the angular call
app.factory('dataFactory', function ($http) {
var factory = {};
factory.data = function (callback) {
$http.get('/api/movies').success(callback);
};
return factory;
});
I added this javascript just to rule-out angular, I get the same:
$.ajax({
url: "/api/movies",
type: 'GET',
//data: "{ 'ID': " + id + "}",
contentType: "application/json; charset=utf-8",
success: function(data) {
alert(data);
},
error: function (xhr, ajaxOptions, thrownError) {
alert(thrownError);
}
});
Any idea what I have done wrong?
I assume your Web API and AngularJS app are running on a different port. In this case you are running in a Same-Origin-Policy issue.
Ensure your Web API is responding with HTTP CORS headers e.g.:
Access-Control-Allow-Origin: http://<angular_domain>:<angular_port>
or
Access-Control-Allow-Origin: *
Doesn't look like a CORS issue to me as you are using a relative URL in your $.ajax() request.
Did you try $.getJSON("/api/movies").then(successHandler, faulureHandler)
Not sure of that will help but for one you are sending a contentType header with a GET request which contains no content. There should be an Accept header instead, but WebAPI should be fine without one.
I would also remove the constrains from the routing and revert back to the default route mapping to see if the problem is there.
config.Routes.MapHttpRoute("DefaultApi",
"api/{controller}/{id}",
new {id = RouteParameter.Optional});

Can't perform CORS request using Angularjs

I am writing my first AngularJS app and have run into a problem with CORS in Angular. Have tried what was mentioned in this reply but still have no luck solving this.
The JS code (main.js) is as follows:
var app = angular.module("app",[]);
app.config(['$httpProvider', function($httpProvider) {
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
]);
app.controller("AppCtrl", function ($scope, $http) {
var postData = "username=demo&password=demo";
$http.post("http://sampleurl.com/login",postData).success(function(data, status, headers, config)
{
console.log(data);
});
});
The Index.html file is:
<!DOCTYPE html>
<html>
<head>
<title>Sample Angular App</title>
</head>
<body ng-app="app" ng-controller="AppCtrl as app">
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js"></script>
<script src="main.js"></script>
</body>
</html>
The error I get is the following:
Failed to load resource: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
I have tried running this request using Ajax and it works just fine with Ajax. For some reason can't get this to work using Angular. Any idea as to what I am doing wrong here?
Thanks!
Ok. So basically after alot of tinkering around I realised that params were not getting passed using
$http.post("http://sampleurl.com/login",postData)
So I rewrote this request as follows:
$http({
url:'http://sampleurl.com/api',
method:"POST",
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: postData
})
This got the request to go through just fine. Hope this is of help to someone else.
Cheers!
There are two things you will need to do, in the .config you need to put this:
$httpProvider.defaults.withCredentials = true;
And then on your server you need to return an an Access-Control-Allow-Origin header, it would possibly look like this:
Access-Control-Allow-Origin:http://URL_OF_SITE_YOUR_ANGULARJS_APP_IS_AT
If you want to send a CORS request 'withCredentials', you'll need to have your server reply with 'Access-Control-Allow-Origin':'http(s)://your.request.origin'. A wildcard (*) reply will be refused by most browsers for requests with credentials.
I was stuck with the same issue, then I read from this link:
http://better-inter.net/enabling-cors-in-angular-js/
and things worked like a charm :)
var myApp = angular.module('myApp', [
'myAppApiService']);
myApp.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
]);
I had the same problem before and I resolve it by adding this header:
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},

Resources