How to check response from $httpProvider.interceptors responseError? - angularjs

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!

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.

Why does angularjs discard the message response for http status codes <200 && >300?

I've been tracking down the reason why I cannot see the response from my webapi in angularjs using $http if the status code is less than 200 and greater than 300.
I've debugged angular.js to the point where I understand whats happening (basically its discarding the promise that contains the data i care about and creating a new one without it if the status codes are not deemed a success)
-see code at https://github.com/angular/angular.js/blob/master/src/ng/q.js#L270-L280
this.$$state.value (holds the http respone) is lost at this point for some reason.
Does it make sense to omit the original values? I'm not going to submit a bug report without asking here if I'm right or wrong.
The reason behind all this is that I am processing some credit card info on the server side. If the processor says its an invalid card, I wouldn't think it would be a 200 code, would it? See the answer to this question..... Suggesting 400 for business rules. This still fails in angularjs.
What is the appropriate HTTP status code response for a general unsuccessful request (not an error)?
Also, FYI, httpInterceptors do not work either, since they are utilized after this 'promise replacement' occurs.
From the Docs:
A response status code between 200 and 299 is considered a success status and will result in the success callback being called. Any response status code outside of that range is considered an error status and will result in the error callback being called.
— AngularJS $http Service API Reference - General Usage
The $http service rejects responses outside of the range of 200 to 299 but it does not "discard the message response". Both the resolve and the reject methods of the $http promise are invoked with the entire response object.
This means all of the data is available in the rejection handler.
var dataPromise = $http(configObject)
.then (function onFulfilled(response) {
//return data for chaining
return response.data;
})
.catch (function onRejected(response) {
if (response.status == 400) {
console.log("400 Bad Request");
console.log("REASON:", response.data);
//chain with default data
return defaultData;
} else {
//throw to chain rejection
throw response;
}
});
In the above example, the rejection handler logs the response for messages with status 400 and converts the rejection to fulfilled with default data. All other status responses get chained as a rejection.
Data is not discarded; it is available and can be used in the rejection handler.
For more information on chaining promises, see Angular execution order with $q.
Do you write status codes when you use promise callbacks then, catch and finally after the $resource call.
Here is what I would check for :
MyRestAPI.XXX({resource}, {data}, MyCallbackFunction).$promise.catch(function(response) {
//should be called upon error
//check response.status for HTTP code.
}).then(function() {
// OK
});

Unexpected token u in Restangular Error Interceptor

I'm using Restangular interceptor to handle errors with status code as 412,409 etc.
But I'm not getting status in the requestInterceptor function.
I have declared the Restangular RequestInterceptor using Typescript.
In my app.ts file,
app.run(Restangular){
Restangular.setErrorInterceptor(function(response) {//code to handle error});
}
Error:
Restangular provides an option to access a full response access, probably setting up this might help. By using Restangular.setFullResponse(true) you should be able to access the status code in your interceptor.
So, I guess according to your snippet now it could look like this(since there is where you are injecting Restangular if you have any other configuration section that where the setting should go)
app.run(Restangular){
Restangular.setFullResponse(true);
Restangular.setErrorInterceptor(function(response) {//code to handle error});
}
It's important to point out that setting up this option, now the access to your responses will be sightly different for all your services unless you create something to handle the next .data behavior:
function(response){
var result = response.data;
var statusCode = result.status;
var responseData = result.myApiResponse;
}
For more information please check: https://github.com/mgonto/restangular#setfullresponse, and hope my post helps.

$.ajax works, $http does not (status 0; CORS?)

I have a cloud service I am attempting to download data from. I can use jQuery's $.ajax function to obtain this data with no issue - all status codes expected are returned.
AngularJS is a different story and I have no idea why. I am using the $http service to get(...) my data. I know there are a few errors the $http is likely to fail on (a 404 if the user mistypes something in the registration box, or a 403 if they are not authenticated).
Yet, no matter what I attempt - I receive a status: 0 response everytime and this is pretty useless as you can imagine.
I have a basic function as follows:
function get(config) {
$ionicLoading.show();
return $http(config)
.then(function (data) {
console.log(data);
return data.data;
},
function (data) {
console.log(data);
throw 'Connection error';
})
.finally(function () {
$ionicLoading.hide();
}
);
}
I use this to test the connection of one of my cloud services.
Which is fine; however - if I pass it an incorrect subdomain for my service, e.g. incorrect.myservice.com - I receive the following error:
GET https://incorrect.myservice.com/ net::ERR_NAME_NOT_RESOLVED
Which is good - that should result in a 404 error(?).
But, the data returned in the error callback is:
Object {data: "", status: 0, headers: function, config: Object, statusText: ""}
Which is bad - it should not be 0? It should be 404. I done some research, and it appears that CORS is a bit of a headache in AngularJS $http.
However, from what I have read - it appears that CORS is enabled on my server because looking at the response in Fiddler/Chrome/IE etc., all responses are returning the Access-Control-Allow-Headers: * and Access-Control-Allow-Headers: * which is what is required for CORS.
So I am completely lost on how to further debug this, as I require this functionality in my application. But $http does not appear to be behaving how it should be?
Please can somebody assist and provide a pointer.
All error codes are returning with status: 0 and I have no idea why?
GET https://incorrect.myservice.com/ net::ERR_NAME_NOT_RESOLVED
Which is good - that should result in a 404 error(?).
Nope. If you can't resolve the host name to an IP address, you can't make a TCP connection to it, so you can't even send the HTTP GET, and if you can't send the request, you can't get the response, which is where the 404 would come from. This is a lower level networking error and you don't even get to do any HTTP, so you get no HTTP status code.
This is also not a CORS error. The browser (at least Chrome) will print a really clear and explicit error message if anything goes wrong with CORS.

Angular opt out of Interceptor behavior

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.

Resources