The callback function for the POST is returning null for my custom HTTP header X-Auth-Token. Chrome is showing the correct POST response headers, but Angular.js is not.
The only ones Angular is returning are Cache-Control and Content-Type. Everything else shows null.
This is my CoffeeScript showing how I'm calling it:
.factory 'loginFactory', ($rootScope, $http, $resource) ->
$resource '/api/auth/login',
email: '#id'
password: '#id'
.controller 'userController', ($scope, $state, $http, loginFactory, userService) ->
$scope.validationError = false
$scope.user =
email: ''
password: ''
$scope.loginUser = ->
loginFactory.save $scope.user, (u, headers) ->
console.log headers('X-Auth-Token')
.$promise.then (response) ->
unless response.error
userService.login($scope.user.email, $scope.user.password)
unless userService.redirIfLoggedIn()
$scope.validationError = true
I also tried running earlier versions Angular 1.3.x, and those had the same issue.
Why is Angular only returning those two headers when I make a request?
Thanks for this solution goes to #dbugger who commented the answer I needed:
Looks like the server needs to give permission to see the headers. Check out this: Reading response headers when using $http of Angularjs
The proper way to make headers available outside of your local domain is to set: Access-Control-Expose-Headers on your webserver. In this particular case, you would put X-Auth-Token.
Related
I need to make a call to an API for my Angular app. Before I can make the call I have to retrieve an AUTH token. This can be accomplished by sending my username and password and then the AUTH token is in the header of the reply. My Angular code seems pretty straightforward, but for some reason two calls are going out to the API. Below is a really basic version of the code as well as the XHR responses in Chrome's debugging tools
'use strict';
var app = angular.module('alpha', ['ngRoute']);
app.controller('apictrl', function($scope, $http, $route, MyAPIAuth) {
MyAPIAuth.then(function(response, status, headers) {
console.log(response);
console.log(status);
console.log(headers);
$scope.authcode = response;
})
});
app.service('MyAPIAuth', function($http) {
var APIauthURL = "https://phoenix.discoverydb.com/papi/login";
var credentials = {
"username": <insert username>,
"password": <insert password>
};
return $http.post(APIauthURL, credentials);
});
As you can see from the image below I receive two response—both 200! Yet, nothing registers in the console. The first response does have the AUTH token that I'm looking for in it. I just can't get to it.
In my angular application I have the following controller (I have deleted some methods due privacy policy):
.controller('ArticleCtrl', ['$http', '$scope', '$location', '$localStorage', '$q', '$templateCache', 'authService',
'uploaderService', 'settings',
function($http, $scope, $location, $localStorage, $q, $templateCache, authService, uploaderService, settings) {
$scope.isAuth = authService.checkAuthStatus() || false;
if ($scope.isAuth == false) {
$location.path('/signin');
}
$scope.username = $localStorage.authStatus.userName;
$scope.getCompany = function(id) {
$http.get(settings.apiBaseUri + '/app/' + id, {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
}
})
.success(function(response) {
$scope.company = response;
$scope.company.Email = $scope.username;
})
.error(function(data, status, headers, config) {
console.log('operation failed, status: ' + data);
$location.path('/signin');
});
$scope.$apply();
};
if ($scope.isAuth == true) {
$scope.company = $localStorage.selectedCompany;
$templateCache.removeAll();
$scope.getCompany($localStorage.selectedCompany.Id);
}
}
]);
I have spend a lot of time, but still I don't understand why does only this contoller gets cached (other controllers were made via copy-paste).
But when this method is called for the first time: all is ok, in debugger I see that it goes to server via GET method, but when I refresh the page, and then go again to this controller - in Firefox and IE I see that there are no new requests to the server. But why? Only when I refresh the page with Ctrl + F5 all is ok. But users will do not do that, I need working application...
Maybe somebody knows how to fix this issue? How to disable angularjs view and controller caching?
UPD:
I see that after update my localstorage isn't changing in IE and Firefox. Why?
Sorry, I do not directly answer the question, but what is coming seems important.
I, and other people, strongly discourage to use ngStorage right now.
Indeed, ngStorage, seems very, very handy. You just directly change the object, and voilà, everything works. I used this a bit, this was cool :)
But, sadly, when you try to make an advanced use, or when you take a look at the source code, you see there are several problems. This awesome "immediate localStorage object modification" is made watching stuff with the $rootScope. That's not a good idea for performance. Moreover, you probably saw that some GitHub issues are stating similar sync problems, like you do. Also, be aware that the project is now completely unmaintained. Use such a library in production is a bad idea.
So, you may give a try to another solution to make the link with the localStorage, such as Angular Locker, becoming more and more used. This will lead to some code refactoring, but you future self will thank you to not have used a problematic library.
First be sure that id parameter is correct. And second be sure that the request headers has no-cache. Probably you have request interceptor and this interceptor override the headers. Track this request in firebug for firefox.
If you see the 'no-cache' values check the server out. Maybe server could cache this.
On my AngularJS sample project, I know that I have a service method which SHOULD throw a JavaScript error.
Using Firebug, I confirm that a JS error is thrown when resolving a promise from a $resource (TypeError: phone.images is undefined); but, the error never appears in the Firebug console.
How can I get the resource to 'fail fast' and propagate the error up the call stack?
Here is the service code:
var phonecatServices = angular.module('phonecatServices', ['ngResource']);
phonecatServices.factory('Phone', ['$resource',
function($resource){
return $resource('phones/:phoneId.json', {}, {
query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
});
}]);
Here is the controller (which fails silently):
phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams', 'Phone',
function($scope, $routeParams, Phone) {
$scope.phone = Phone.get({phoneId: $routeParams.phoneId}, function(phone) {
//JS error SHOULD be thrown here:
$scope.mainImageUrl = phone.images[0];
});
...
}]);
I don't want the code to fail silently! How can I fix it?
Ideally, I would like to fix it throughout the framework, rather than putting in special error handling code for each service or resource call.
You need to add the error callback to your get call:
Phone.get({phoneId: $routeParams.phoneId}, function(phone){
// Do Stuff with phone object
}, function(error) {
alert("Y U NO RETURN PHONE?");
// Handle error accordingly
});
Here is the documentation for $resource
If you'd like to generically handle errors for AJAX requests through the angular framework, then you'd probably like something like an http interceptor (Check the interceptors section). This kind of paradigm requires that all requests pass through your interceptor service to be handled in a generic fashion.
I've been hunting for a few hours now and can't seem to find any information specific to my setup so here goes.
I'm using the MEAN stack and wanting to use the Twitter API in my angular app. I have all the required keys and trigger a twitter api authentication on the server side using Node, then pass the token I get in response to my angular pages. I was hoping to be able to use this token to make requests to the api from an angular service. The request I'm trying to get working the moment is to fetch a given user's profile object. I've attached my service method below. The error I get when I run it is a 405 method no allowed, no access-control-allow-origin header is present.
angular.module('tms.system').factory('Twitter', ['$log', '$q', '$http', '$window', 'twitter', 'Global', function($log, $q, $http, $window, twitter, Global) {
return {
findProfile: function(handle) {
var deferred = $q.defer();
var config = {
timeout:3000,
headers: {
'Authorization': 'Bearer ' + Global.twitterToken,
'X-Testing' : 'testing'
}
};
$http.get('https://api.twitter.com/1.1/users/show.json?screen_name=' + handle, config).
success(function(data) {
$log.info(data);
deferred.resolve(data);
}).
error(function(status) {
$log.error(status);
});
return deferred.promise;
}
};
}]);
Just for future reference, as stated in the comments of maurycy's answer {and being myself trying to get tweets just from Angular without succes}, the best approach for this would be to get them from some backend.
I believe you should use $http.jsonp with a JSON_CALLBACK to get it to work, it's not going to happen with $http.get for sure
In my angularjs application I am communicating with a backend server that requires basic access authentication via http header. I have implemented the authentication mechanism on the client side as described here.
angular.module('myAuthModule')
.config(['$httpProvider', '$stateProvider',
function ($httpProvider, $stateProvider) {
$httpProvider.interceptors.push('securityInterceptor');
}])
.factory('securityInterceptor', ['$location', '$window', '$q',
function ($location, $window, $q) {
return {
request: function (config) {
config.headers = config.headers || {};
if ($window.sessionStorage.token) {
config.headers['Auth-Key'] = $window.sessionStorage.token;
}
return config;
},
response: function (response) {
if (response.status === 401 || response.status === 403) {
$location.path('/login');
}
return response || $q.when(response);
}
};
}
]);
So far so good, handling xhr requests within the angular app works as expected.
The problem is that I need to provide a download link for pdf documents. My backend server has a /Document/Pdf/:id resource that serves a application/pdf response with ContentDisposition: attachment which also requires authentication. I understand that I cannot initiate a download using xhr, however both providing a link to the document download via ngHref and calling a function that does for example $window.open('/Document/Pdf/13') lead to a 401 Unauthorized response by the server.
What am I missing here?
Having explored the possibilities given by #Geoff Genz with the addition of a fourth - data-uri option, which unfortunately does not allow defining filenames - I decided to go for a different approach.
I added a method to the API which generates a one-time download link based on a normally authenticated request and download it straight away. The angular handler becomes very simple
.factory('fileFactory', ['$http', '$window',
function ($http, $window) {
return {
downloadFile: function (fileId) {
return $http(
{
method: "POST",
data: fileId,
url: '/api/Files/RequestDownloadLink',
cache: false
}).success(function (response) {
var url = '/api/File/' + response.downloadId;
$window.location = url;
});
}
};
}]);
This is not perfect but I feel is least hack-ish. Also this works for me because I have full control of the front- and back-end.
There is not a simple solution to this. You've already discovered that you cannot download via Ajax, so you can't set a custom header that way. Nor can you set a custom header on a browser generated GET (like an href) or POST (like a form submit). I can suggest three different approaches, all of which will require some modifications on your server:
(1) Use Basic or Digest auth on your web page, so the browser will generate and send the Authorization header with those credentials.
(2) Set the token in "authorization" cookie that will be passed with the request and validate the token server side.
(3) Finally, the way we've implemented this is to use a POST request instead of a GET for the download. We POST to a hidden IFrame on the same page and have the server set the appropriate Content-Disposition header such as "attachment; filename="blah.pdf"" on the response. We then send the authorization token as a hidden field in the form.
None of these are ideal, and I know our solution feels kind of hacky, but I've not seen any more elegant approaches.