Angular JS Custom Headers on GET Turns to Options - angularjs

So, this has been a huge struggle for us.
We have a web API .net MVC4 back-end. We are using angular for client-side. We have a page that has some JSON that we receive data from. When we make a GET to this page we receive data. As soon as we add custom headers to the call the GET turns into OPTIONS and we get a 200, with no response. We are doing a couple things like creating a BASE64 code on "login" storing it as a cookie and trying to add that to the GET headers. the thing is we have stripped out all of the unnecessary code and we are still at the same problem. Even with authorization turned off for the data. Here is the GET code:
myApp.factory("projectDataService", function ($http, $location, $cookieStore) {
var token = "";
token = $cookieStore.get('token');
return {
getProject: function (successcb) {
$http({ method: "GET", url: "http://dev.projnik.com/api/project", headers: { 'Authorization': 'Basic ' + token } }).
success(function (data, status, headers, config) {
successcb(data);
}).
error(function (data, status, headers, config) {
console.log(data, status, headers, config);
});
},
save: function (project) {
$http({ method: "POST", url: "http://dev.projnik.com/api/project", data: $.param(project) }).
success(function (data, status) {
if (status == '201') {
$location.path('/all');
}
})
}
};
});
And the app.js:
var myApp = angular.module('Project', ['ngResource', 'ngCookies']);
myApp.config(function($routeProvider){
$routeProvider.
when('/new', {templateUrl:'templates/new.html', controller:'EditProjectController'}).
when('/mobile', {templateUrl:'templates/mobile.html', controller:'ProjectController'}).
when('/it', {templateUrl:'templates/it.html', controller:'ProjectController'}).
when('/writing', {templateUrl:'templates/writing.html', controller:'ProjectController'}).
when('/all', { templateUrl: 'templates/all.html' }).
when('/cookie', { templateUrl: 'partials/cookiecontrollerhtml.html' }).
when('/login', { templateUrl: 'partials/_login.html' }).
otherwise({ redirectTo: '/all' });
});
myApp.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}]);
Again, without adding headers attribute to the call, everything works (obviously we get a 401 with Authorization turned on in the API). We are willing to pay for an answer at this point.
We are exposing our actual domains here, and that is fine. If someone goes to www.projnik.com and clicks the login link on the top and enter shane, password for username,password they would receive the cookie and it routes the user back to the #/all page where they would GET the data, though it doesn't work.
PS: I have tried to ad withCredentials = true; to the call and I get the same result.

If you are accessing API in dev.projnik.com from a web page in www.projnik.com, in a browser like Chrome, CORS comes into play. If you make a GET without any custom headers, it is a simple CORS request and I assume you have settings in web.config that sends Access-Control-Allow-Origin header and that makes it work. Once you add a custom header, it is no longer a simple CORS and it becomes pre-flighted CORS with browser making an OPTIONS request. The response to this OPTIONS request must send the correct CORS headers for the browser to make the subsequent GET. To enable CORS, check this out. BTW, in IIS there is a default handler that answers OPTIONS call that you might need to remove for the message handler in Web API to respond to OPTIONS.

I am working with Shane on this - the block of code on the server is:
public void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
{
//These headers are handling the "pre-flight" OPTIONS call sent by the browser
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
HttpContext.Current.Response.End();
}
}
I believe this takes care of the situation. But the problem is still there.

Related

Retrieve a Sharepoint List with Angular JS

I have created an Angular JS application which would output a list of data in a Share Point list. I am trying to make a rest API call to my Share Point List to get the data, however I am unable to do it as I get an error 403 Forbidden.
Below is my controller which tries to fetch the data.
app.controller('RetrieveRecords', function ($q, $http, $scope) {
var url = "https://testapp.sharepoint.com/sites/testmyapplication/_api/web/lists/getByTitl
e('TestAppList')/items?$select=Status,Time";
$http(
{
method: "GET",
url: url,
headers: { "accept": "application/json;odata=verbose" }
}
).success(function (data, status, headers, config) {
$scope.details = data.d.results;
}).error(function (data, status, headers, config) {
});
});
My Angular JS application is not a Share Point page, my application is basically hosted as a Azure Static Website. I checked some online tutorials, however I couldn't find a solution to the problem I am facing.
Thank you.
#John,
You have to supply digest within the header.
You will find the necessary value in the element called '__REQUESTDIGEST'. So with jQuery you can get the value like this: $('#__REQUESTDIGEST').val().
This value needs to be added to the header:
headers: { "Accept": "application/json;odata=verbose", "X-RequestDigest": $('#__REQUESTDIGEST').val() }
can you try adding this tag and see if it works.
Hope it helps.
MV

Angular: $http.post() not called

I am using $http to post data to server.
There are two OPTIONS requestMethod calls before making a POST request.
However, the POST request is not made sometimes.
As I am updating html on return of this call, the page hangs.
$http({
url: scope.Settings.url,
method: "POST",
data:data,
withCredentials: true
}).then(function (data, status, headers, config) {
setBusyState(false);
scope.rModel = scope.search;
scope.Results = data.data;
}, function (data, status, headers, config) {
scope.Results = [];
setBusyState(false);
});
EDIT: This does not happen always. Only a few times.
This appears to happen only in Chrome, whereas it is fine in IE
This is a known issue with AngularJS where it caches the subsequent http requests so if you have multiple http requests lined up you would sometime experience that some of the calls are not actually made.I have mainly seen this issue of aggressive caching in IE.
To overcome this issue you need to apply some sort of global settings which will prevent the http requests from getting cached.The ideal place of doing this in angular is $httpProvider.
Basically you are setting different header parameters with the appropriate values.
Related code from app.config
appConfig.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.cache = false;
//initialize get if not there
if (!$httpProvider.defaults.headers.get) {
$httpProvider.defaults.headers.get = {};
}
//disable IE ajax request caching
$httpProvider.defaults.headers.get['If-Modified-Since'] = <set a time>;
$httpProvider.defaults.headers.get['Cache-Control'] = 'no-cache';
$httpProvider.defaults.headers.get['Pragma'] = 'no-cache';
}
]);
What angular does in chrome is, it makes OPTIONS request.
These Pre-Flight OPTIONS requests are a way of asking permissions for the actual request, before making the actual request.
So, as a work around, adding headers to the request solves the problem.
$http({
url: scope.Settings.url,
method: "POST",
data: data,
headers: {
'Content-Type': 'application/json'
},
withCredentials: true
}).then(function(data, status, headers, config) {
setBusyState(false);
scope.rModel = scope.search;
scope.Results = data.data;
}, function(data, status, headers, config) {
scope.Results = [];
setBusyState(false);
});

How to send json data to web api from $http service in angular js

While making a Ajax Request with Json object to "Web Api" using Angular js $http service.It raise CORS Issue,There is no Resolution by adding the following header "Access-Control-Allow-Origin" also,Please suggest me the right solution
Example:
var testmodule = angular.module('testmod', []);
testmodule.controller('testcontroller', function ($scope, $http)
{
$scope.Member ={'name':'andrew','age':12,'class':10}
$scope.savedata =function(){
$http({
url: "**api url**",
method: 'POST',
data: $scope.Member
}).success(function (data, status) { }).error(function (data, status, headers) { })
}
})
The "Access-Control-Allow-Origin" is a server side issue, you don't need to add any header in angularjs. add the following code to your server side:
("Access-Control-Allow-Origin", "*");
("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, X-Codingpedia");
you can refer to How to enable CORS in AngularJs

Angular REST cannot return simple data

I am trying to get a $http REST GET call working in my Appgyver project working but nothing I do seems to come right, always returns an error.
Please note the angular app will be running on mobile devices eventually and then connect to my remote web service.
I've double checked that my custom API is working and returning data correctly in a number of ways, namely:
hard coded cUrl request running from sh files in terminal - Returns data and correct 200 code
Tested the API end points in both POSTMAN and Firefox's SOA client.
Putting in my test end point of http://somesite.com/quote-and-buy-performance/druidapi/taxonomy_term returns data as below:
[{"tid":"1","vid":"2","name":"ACME Ltd.","description":"","format":"filtered_html","weight":"0","parent":"0","uri":"http://somesite.com/quote-and-buy-performance/druidapi/taxonomy_term/1"},{"tid":"2","vid":"2","name":"ABC Films LTD","description":"","format":"filtered_html","weight":"0","parent":"0","uri":"http://somesite.com/quote-and-buy-performance/druidapi/taxonomy_term/2"}]
Even a simple CSRF Token request gives me errors.
Could someone possibly point out where I am going wrong here, the Appgyver site is badly documented and I have tried the Angular RESTful sample which my code below is based upon from https://docs.angularjs.org/api/ng/service/$http and https://docs.angularjs.org/api/ng/service/$http#setting-http-headers
Please note the code below is basically Angular.js using Javascript syntax (as opposed to Coffeescript), logging output follows the code
angular
.module('main')
.controller('LoginController', function($scope, supersonic, $http) {
$scope.navbarTitle = "Settings";
$scope.stoken = "Response goes here";
$scope.processLogin = function(){
var csrfToken;
steroids.logger.log("START CALL: processLogin");
// $form_login_email_address = $scope.login_email;
// $form_login_password = $scope.login_password;
$local_get = "http://somesite.com/quote-and-buy-performance/services/session/token";
$hal_get_taxterm_index = "http://somesite.com/quote-and-buy-performance/druidapi/taxonomy_term";
// $http.defaults.headers.common.contentType('application/json');
var req = {
method: 'GET',
url: $hal_get_taxterm_index,
headers: {
'Content-Type': 'application/json'
}
}
$http(req)
.success(function(data, status, headers) {
steroids.logger.log("Inside http.get() success");
}).error(function(data, status, headers){
steroids.logger.log("Inside http.get() WITH ERROR");
steroids.logger.log('data: ' + data);
steroids.logger.log('status: ' + status);
}).then(function(data, status, headers){
steroids.logger.log("Inside http.get() then");
});
steroids.logger.log("END CALL: processLogin");
}
});
Logging output from calls to steroids.logger.log
View Time Level Message
main#login 16:01:55.219 info "Inside http.get() WITH ERROR"
main#login 16:01:55.219 info "data: null"
main#login 16:01:55.219 info "status: 0"
main#login 16:01:55.64 info "END CALL: processLogin"
main#login 16:01:55.64 info "START CALL: processLogin"
Here's what I would do:
Separate out your http call into a service. This is a pretty standard way to modularize your code in angular:
angular.module('main').factory("SomeService", function($http) {
return {
get: function() {
$http({
url: "http://somesite.com/quote-and-buy-performance/druidapi/taxonomy_term",
method: "GET",
headers: {
"Content-Type": "application/json"
}
}).success(function(data, status, headers, config) {
console.log("Success!");
console.log(data);
}).error(function(data, status, headers, config) {
console.log("Error!");
console.log(status);
console.log(data);
});
}
}
})
Then to use this in your controller, just include it in your controller declaration and call get like you would a normal method:
angular.module('main').controller('LoginController', function($scope, supersonic, SomeService) {
$scope.navbarTitle = "Settings";
$scope.stoken = "Response goes here";
$scope.processLogin = function(){
var csrfToken;
steroids.logger.log("START CALL: processLogin");
SomeService.get();
steroids.logger.log("END CALL: processLogin");
}
})
Do this and then comment back with your results and we can work from there.
If your angular's app is within a certain domain, then HTTP request must be made within the same domain.
In your case, you are trying a cross domain request (a request on another domain). You must then make a cross domain request.
You can see this question.
The author uses $http.jsonp() to send cross domain requests. There migth be another way to do it.

Getting 304 status code through a web api call only using IE11

I am getting 304 status code through a angular JS webApi get request only using IE11. When I press Ctrl+F5, it corrects and get 200 status code (which is correct behavior). It works fine using Chrome. I am using following code.
factory('StudentService', function ($q, $http, pathProvider, searchParams, student) {
return {
getStudents: function (successcb) {
var deferred = $q.defer();
var url = pathProvider.getPath("Student/GetStudents");
$http({
method: 'GET'
, url: url
, cache:false
}).
success(function (data, status, headers, config) {
deferred.resolve(data);
}).
error(function (data, status, headers, config) {
deferred.reject(status);
alert('getStudents ' + status + ': ' + data);
});
return deferred.promise;
}
}
It appears IE has some problem.
Thanks
IE probably adds a If-Modified-Since or If-None-Match request header to the request. If you press CTRL+F5, you force a browser refresh, which means that IE does not add these headers.
It seems that IE does not interpret the angular option cache:false very well. I don't know whether this is an IE or an angular issue, but you can resolve it in javascript by making sure the request is unique. For instance, by adding a dummy parameter to the query string:
$http({
method: 'GET'
, url: url
, cache:false
, params: { 'foobar': new Date().getTime() }
})
You can also fix this on the server by disallowing any browser caching. How you do that depends on the server technology you use.

Resources