I'm doing it using interceptors. I have created a library file which can be added to the index.html file. This way you'll have global error handling for your rest service calls and don't have to care about all errors individually. Further down I also pasted my basic-auth login library. There you can see that I also check for the 401 error and redirect to a different location. See lib/ea-basic-auth-login.js
lib/http-error-handling.js
/**
* @ngdoc overview
* @name http-error-handling
* @description
*
* Module that provides http error handling for apps.
*
* Usage:
* Hook the file in to your index.html: <script src="lib/http-error-handling.js"></script>
* Add <div class="messagesList" app-messages></div> to the index.html at the position you want to
* display the error messages.
*/
(function() {
'use strict';
angular.module('http-error-handling', [])
.config(function($provide, $httpProvider, $compileProvider) {
var elementsList = $();
var showMessage = function(content, cl, time) {
$('<div/>')
.addClass(cl)
.hide()
.fadeIn('fast')
.delay(time)
.fadeOut('fast', function() { $(this).remove(); })
.appendTo(elementsList)
.text(content);
};
$httpProvider.responseInterceptors.push(function($timeout, $q) {
return function(promise) {
return promise.then(function(successResponse) {
if (successResponse.config.method.toUpperCase() != 'GET')
showMessage('Success', 'http-success-message', 5000);
return successResponse;
}, function(errorResponse) {
switch (errorResponse.status) {
case 400:
showMessage(errorResponse.data.message, 'http-error-message', 6000);
}
}
break;
case 401:
showMessage('Wrong email or password', 'http-error-message', 6000);
break;
case 403:
showMessage('You don\'t have the right to do this', 'http-error-message', 6000);
break;
case 500:
showMessage('Server internal error: ' + errorResponse.data.message, 'http-error-message', 6000);
break;
default:
showMessage('Error ' + errorResponse.status + ': ' + errorResponse.data.message, 'http-error-message', 6000);
}
return $q.reject(errorResponse);
});
};
});
$compileProvider.directive('httpErrorMessages', function() {
return {
link: function(scope, element, attrs) {
elementsList.push($(element));
}
};
});
});
})();
css/http-error-handling.css
.http-error-message {
background-color: #fbbcb1;
border: 1px #e92d0c solid;
font-size: 12px;
font-family: arial;
padding: 10px;
width: 702px;
margin-bottom: 1px;
}
.http-error-validation-message {
background-color: #fbbcb1;
border: 1px #e92d0c solid;
font-size: 12px;
font-family: arial;
padding: 10px;
width: 702px;
margin-bottom: 1px;
}
http-success-message {
background-color: #adfa9e;
border: 1px #25ae09 solid;
font-size: 12px;
font-family: arial;
padding: 10px;
width: 702px;
margin-bottom: 1px;
}
index.html
<!doctype html>
<html lang="en" ng-app="cc">
<head>
<meta charset="utf-8">
<title>yourapp</title>
<link rel="stylesheet" href="css/http-error-handling.css"/>
</head>
<body>
<!-- Display top tab menu -->
<ul class="menu">
<li><a href="#/user">Users</a></li>
<li><a href="#/vendor">Vendors</a></li>
<li><logout-link/></li>
</ul>
<!-- Display errors -->
<div class="http-error-messages" http-error-messages></div>
<!-- Display partial pages -->
<div ng-view></div>
<!-- Include all the js files. In production use min.js should be used -->
<script src="lib/angular114/angular.js"></script>
<script src="lib/angular114/angular-resource.js"></script>
<script src="lib/http-error-handling.js"></script>
<script src="js/app.js"></script>
<script src="js/services.js"></script>
<script src="js/controllers.js"></script>
<script src="js/filters.js"></script>
lib/ea-basic-auth-login.js
Nearly same can be done for the login. Here you have the answer to the redirect ($location.path("/login")).
/**
* @ngdoc overview
* @name ea-basic-auth-login
* @description
*
* Module that provides http basic authentication for apps.
*
* Usage:
* Hook the file in to your index.html: <script src="lib/ea-basic-auth-login.js"> </script>
* Place <ea-login-form/> tag in to your html login page
* Place <ea-logout-link/> tag in to your html page where the user has to click to logout
*/
(function() {
'use strict';
angular.module('ea-basic-auth-login', ['ea-base64-login'])
.config(['$httpProvider', function ($httpProvider) {
var ea_basic_auth_login_interceptor = ['$location', '$q', function($location, $q) {
function success(response) {
return response;
}
function error(response) {
if(response.status === 401) {
$location.path('/login');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
return function(promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(ea_basic_auth_login_interceptor);
}])
.controller('EALoginCtrl', ['$scope','$http','$location','EABase64Login', function($scope, $http, $location, EABase64Login) {
$scope.login = function() {
$http.defaults.headers.common['Authorization'] = 'Basic ' + EABase64Login.encode($scope.email + ':' + $scope.password);
$location.path("/user");
};
$scope.logout = function() {
$http.defaults.headers.common['Authorization'] = undefined;
$location.path("/login");
};
}])
.directive('eaLoginForm', [function() {
return {
restrict: 'E',
template: '<div id="ea_login_container" ng-controller="EALoginCtrl">' +
'<form id="ea_login_form" name="ea_login_form" novalidate>' +
'<input id="ea_login_email_field" class="ea_login_field" type="text" name="email" ng-model="email" placeholder="E-Mail"/>' +
'<br/>' +
'<input id="ea_login_password_field" class="ea_login_field" type="password" name="password" ng-model="password" placeholder="Password"/>' +
'<br/>' +
'<button class="ea_login_button" ng-click="login()">Login</button>' +
'</form>' +
'</div>',
replace: true
};
}])
.directive('eaLogoutLink', [function() {
return {
restrict: 'E',
template: '<a id="ea-logout-link" ng-controller="EALoginCtrl" ng-click="logout()">Logout</a>',
replace: true
}
}]);
angular.module('ea-base64-login', []).
factory('EABase64Login', function() {
var keyStr = 'ABCDEFGHIJKLMNOP' +
'QRSTUVWXYZabcdef' +
'ghijklmnopqrstuv' +
'wxyz0123456789+/' +
'=';
return {
encode: function (input) {
var output = "";
var chr1, chr2, chr3 = "";
var enc1, enc2, enc3, enc4 = "";
var i = 0;
do {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
keyStr.charAt(enc1) +
keyStr.charAt(enc2) +
keyStr.charAt(enc3) +
keyStr.charAt(enc4);
chr1 = chr2 = chr3 = "";
enc1 = enc2 = enc3 = enc4 = "";
} while (i < input.length);
return output;
},
decode: function (input) {
var output = "";
var chr1, chr2, chr3 = "";
var enc1, enc2, enc3, enc4 = "";
var i = 0;
// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
var base64test = /[^A-Za-z0-9\+\/\=]/g;
if (base64test.exec(input)) {
alert("There were invalid base64 characters in the input text.\n" +
"Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\n" +
"Expect errors in decoding.");
}
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
do {
enc1 = keyStr.indexOf(input.charAt(i++));
enc2 = keyStr.indexOf(input.charAt(i++));
enc3 = keyStr.indexOf(input.charAt(i++));
enc4 = keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
chr1 = chr2 = chr3 = "";
enc1 = enc2 = enc3 = enc4 = "";
} while (i < input.length);
return output;
}
};
});
})();