Angular opt out of Interceptor behavior - angularjs

In my angular app, I'm looking for a way to handle all server response errors (with a popup box showing an error message) but allow a controller to opt out of this behavior and do it's own error handling if needed when calling a $resource.
I was planning to just use an interceptor and have it handle all errors. Something like
$httpProvider.interceptors.push(function($q) {
return {
'responseError': function(rejection) {
displayMessage(rejection);
return $q.reject(rejection);
}
};
});
Then from the controller, if I call
$scope.widget = WidgetResource.get();
and the server responds with a 401 error, the user would be shown an error message saying they are not authorized.
But it occurred to me that sometimes a controller may want to handle error responses on it's own (namely, a better way to display the error in the given context). What would be a way that it could opt out of what the interceptor is doing?
Alternately, maybe interceptors is not the way to go for this. What else could I use to handle this cross cutting concern and allow opting out?

You can use interceptors to handle errors which are not context specific. We've found it to actually be a good practice. Something like a session timeout(401) or an internal server error(500) are errors that can be handled by your interceptor. While context specific errors are better handled in their respective service callbacks.
You can differentiate based on HTTP status codes in the following way:
function responseError(rejection) {
switch (rejection.status) {
case 401:
//handle unauthenticated request
case 500:
//Oops something has gone wrong due to an internal server error
default:
break;
}
return $q.reject(rejection);
}
Upon $q.reject(rejection) the control is forwarded to the error handler of the respective $resource instance. So in the above example, a 404 from the server will be forwarded to your respective controller or service's error handler.

Related

Restangular error interceptor - how to pass a custom object to a controller that does not know about restangular?

I am stuck with the approach I am taking probably due to my lack of knowledge about angular promises VS restangular promises, etc.
I have an AngularJs application with TypeScript (although typescript is mostly irrelevant here and the same applies to any javascript). These are the players:
controller: it gets injected a service, through this service the controller can send a POST to an API
service: it wraps restangular. The idea is that this service does not expose any restangular functionality to the controller. It abstracts the controller from knowing how to save an item. It has a method that accepts an object and returns an angular promise.
export interface IRegistrationService {
addRegistration(model: registration.BusinessRegistration): ng.IPromise<void>;
}
Restangular error interceptor: it handles Http Responses with status 400 coming from an API because they are validation errors and transforms them in a custom object. The idea is that eventually the controller can either succeed saving an item (posting it through the service) or get a validation error (that comes from this interceptor).
This is what I have so far:
The restangular error interceptor
restangularProvider.setErrorInterceptor((response: restangular.IResponse, deferred: ng.IDeferred<any>, responseHandler: any) => {
if (response.status === 400) {
let validationError: myTsd.IApiValidationErrors = getAsValidationError(response.data);
// How to pass this validationError as an errorCallback to the controller?
//deferred.notify(validationError);
//deferred.reject(validationError); //this stops the chain
//return true; // if error not handled. But where to put the validationError?
//return false; // if error handled. But where to put the validationError?
}
});
The service that abstracts the controller from knowing anything about restangular Notice that it should return an angular promise, not a restangular promise.
public addRegistration(model: registration.BusinessRegistration): ng.IPromise<void> {
return this.restangular.all("registration")
.post<registration.BusinessRegistration>(model)
.then(() => {
console.log("Registration posted successfully");
}, (error: any) => {
//if I get the object here, how to make it available in the errorCallback of the controller caller?
}, (notify: any) => {
//if I get the object here, how to make it available in the errorCallback of the controller caller?
});
}
The controller that uses that service but knows nothing about restangular
//public static $inject = ["app.services.RegistrationService"];
//.. controller code
this.registrationService.addRegistration(this.model)
.then(() => {
console.log("model posted successfully in remote API")
}, (error: myTsd.IApiValidationErrors) => {
// if there was any validation error I need the object here
console.log(error);
});
How should I chain everything? My "only" requirements are:
the logic to create that object is in a central place like the setErrorInterceptor, and it should distinguish between http responses 400 or any other. If the response is neither 2xx or 400 it can handle the error or pass it to the service that uses restangular. It doesn't matter
the service that uses restangular must allow the controller to either succeed or have a callbackError with the custom validation error object. It abstracts the controller from everything else.
Thanks a lot!
I don't fully understand the docs here https://github.com/mgonto/restangular#seterrorinterceptor and whether there is something else other than notifying or rejecting that I could do.
Restangular's .setErrorInterceptor() is a rather odd beast, which, as far as I can gather, won't do what you want it to do.
It can be made to sense error code(s) (eg your 400) and do stuff when that condition arises, but has no further ability other than to return false (block) or return anything else (not block).
The non-blocking action allows the promise chain to take its natural, unintercepted course.
The blocking action inhibits both the error path and the success path of the promise chain.
Therefore think of .setErrorInterceptor() as a "selective blocker", not a "filter" or a "catch", and contrast it with promise.catch() behaviour, by which :
an error state can be converted to success by returning some value/object,
the error can be rethrown, or some new error can be thrown, keeping the promise chain on the error path.
The inability of .setErrorInterceptor() to propagate anything other than the original error seems to mitigate against it in favour of a named "catch handler" (eg. getAsValidationError() or a function that wraps getAsValidationError()) that can be included wherever relevant. That should give you the feature you require.
The only problem I can foresee is getting the catch handler to recognise the "400" condition - possibly simple - requires research.
Don't get too hung up on Angular promises versus Restangular. They should inter-operate.

How to catch wrong password/username response in angular?

I’m trying to implement authentication in my angularjs app.
I’ve read some articles about doing this properly. Here is one, for instance: https://medium.com/opinionated-angularjs/techniques-for-authentication-in-angularjs-applications-7bbf0346acec
But I can’t realize how to make it respond on wrong username/password pair.
Let’s have a look at this code :
authService.login = function (credentials) {
return $http
.post('/login', credentials)
.then(function (res) {
// populate user info
}
);
};
I’ve tried to add here a second callback to “then” method and use “success”/”error” methods instead of “then” and I’ve tried to response on $http.post request with different error like 400 and 500 via status, but in any case all the errors are handled by the success method and callback.
What am I doing wrong? How to catch wrong password/username response in angular?
It was actually my own fault. My custom Intereptor simply swallowed authenticaion errors.

Prevent http errors from being logged in browser console [duplicate]

This question already has answers here:
Suppress Chrome 'Failed to load resource' messages in console
(3 answers)
Closed 5 years ago.
I'm trying to figure out how to suppress http request and response errors in my app, for example when a bad request is made to my server, I don't want the error to be logged into the browser console. I've tried overriding the $exceptionHandler, but that logic works for all exceptions except HTTP errors, those are still logged to the browser. I've also created HTTP interceptors but the global 400 Bad Request error appears before the logic that I've put in my responseError:
'responseError': function (rejection) {
console.log(rejection);
return $q.reject();
}
That doesn't suppress the error either, any ideas?
EDIT
The error I'm getting is this:
POST http://localhost:38349/token 400 (Bad Request)
Actually Angular doesn't log this.
It's the XMLHttpRequest object that doing that (same issue here with JQuery). I extracted the logic from Angular and made a working standalone example:
var method = 'POST';
var url404 = '/tnseiartneiasrt/arsntiearsntiasrntiarnstsie';
var xhr = new window.XMLHttpRequest();
xhr.open(method, url404, true);
xhr.onreadystatechange = function() {
if (xhr && xhr.readyState == 4) {
console.log('should have logged an error already :(');
}
};
xhr.send(null);
As you can see, it's the xhr object that logs it. There might be a way to disable all console.log before triggering the xhr request but I think it's better to either
Redesign your error flow to return a status code of 200 embeded with an internal error code in the body
Live with the console logging (the average user doesn't even know about the console anyways)
I chose the second option because I think it's easier to understand as it doesn't require extra logic when someone reads your code.
Hope this helps :)

How to properly handle server side errors?

I'm developing web app with angular.js, I'm currently a little confused about what's the proper way to handle errors. In my app, I have used ngResource to call rest API of server. So I'll have a lot of ngResource api calls.
e.g. user resource, there're user.query( ), user.get( ) , user.save( ) ......
Do I suppose to put an error callback into all of the ngResource api calls?
Just to handle all kinds of errors: like server down or no internet access ??
I just don't think put an error callback in every ngResource api call is a good idea. That'll produce a lot of redundant code and make my code not neat .
What will you do to handle various error types?
You can use an interceptor and do whatever you want when an error occured :
var app = angular.module("myApp", []);
app.config(function ($provide, $httpProvider) {
$provide.factory('ErrorInterceptor', function ($q) {
return {
responseError: function(rejection) {
console.log(rejection);
return $q.reject(rejection);
}
};
});
$httpProvider.interceptors.push('ErrorInterceptor');
});
With this interceptor you can read the status code and do what you need (a perfect use case is to redirect your user to a login page if status code is 401).
Since ngResource use $http, your interceptors will also be executed when you call a resource method.
Of course, you can do more and add an interceptor before / after a request is made.
See the full documentation here : http://docs.angularjs.org/api/ng.$http
See this fiddle : http://jsfiddle.net/4Buyn/ for a working sample.

How to check response from $httpProvider.interceptors responseError?

in AngularJs 1.2.x, the docs only provide a rejection object, how can we see the actual response object? (to see things like http status code)
edit: here's an example from the docs:
// register the interceptor via an anonymous factory
$httpProvider.interceptors.push(function($q, dependency1, dependency2) {
return {
'responseError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
};
});
that example shows some unknown rejection object (no docs on what it's members are). the old (deprecated responseInterceptors allow query of the response object. (check for status=401, for example) how are you supposed to query for 401 service errors with the new interceptor functionality?
i am coding my usage of this to the point of being able to run it,
and so i set a breakpoint, and see that the "rejection" object has a .status property.
if a server doesn't exist, it will == 0
otherwise, it seems to return the http status code. (i see 404 so far)
I havent coded a real service point so i'll update this answer and/or accept it once i get that done.
update: yes, the .status field returns the http response status, and 0 if server not found. so this is the valid answer!

Resources