Angularjs resource status description - angularjs

I have an issue with angularjs app communicating with a REST API. The problem is the api returnes a status code and also adds a status description like HTTP/1.1 417 Invalid quantity.
With jquery ajax the jqXHR object had a statusText property but with angularjs I can't seem to find how can I access this in my error handler. Needless to say I can't modify the API which for a 417 status code for example can return different status descriptions.
I have found that I would need to change the angularjs library but in case of an update this would not be handy. I would have to change xhr.responseText to xhr.statusText. Can I somehow overwrite this function from my code and don't modify angular library?
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
completeRequest(
callback, status || xhr.status, xhr.responseText, xhr.getAllResponseHeaders());
}
};

Here's the work around to get StatusText (as pointed out that some mobile browsers strip out DATA from successfull responses: https://stackoverflow.com/a/28470163/1586498
I have also discovered that .success and .error on $http in Angular does not pass the StatusText, you have to use .then(response)

You need to create an httpInterceptor as outlined here during your module config.
// register the interceptor as a service
$provide.factory('myHttpInterceptor', function($q) {
return function(promise) {
return promise.then(function(response) {
// do something on success
}, function(response) {
//do something for your 417 error
if(response.status == 417) {
alert("We've got a 417!");
return $q.reject(response);
}
//some other error
return $q.reject(response);
});
}
});
$httpProvider.responseInterceptors.push('myHttpInterceptor');

The support for statusText was added in AngularJS 1.2.16.

Related

Why is the response with error code (500) treated as successful response while using interceptor

I am trying to use an Angular interceptor for handling my 500 or 403 error codes. For other codes I have custom business implementation. However it seems using interceptor makes Angular treat error responses as success responses and my success callback in .then is called. Isn't this strange, considering docs which says 200-299 codes are only treated as success response.
My code:
function appInterceptorFn(){
var interceptor = {
responseError: function (config) {
if (config && config.status === cramConfig.FORBIDDEN_ACCESS_CODE) {
$rootScope.$emit('ERROR_EVENT', config);
}
return config;
}
}
return interceptor;
}
Is there something that can be done to avoid it, I am using AngularJS v1.3.17
I have visited this link which shows a different implementation but I would like to use interceptor preferably.
Is this a known issue ?
by returning your object "normally", you tell angular to treat your error as a success.
You have to replace your
return config;
with
return $q.reject(config);
Explanation
If you look at the documentation here : https://docs.angularjs.org/api/ng/service/$http#interceptors , you will see the following:
// optional method
'responseError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
}
I know it's not a lot of documentation, but it tells you that if you return an object, or a promise, it will resolve your error as if there was no error. If you want your error to be re-thrown, you have to do it explicitly by using $q.reject()

Differentiate 404 from a Rest service from a normal Request in Angular interceptors

I'm codifying an interceptor in Angular to treat the 404 responses. When a request comes from a REST service the interceptor must not intercept the request. My main problem is differentiating between when the request comes from a page request or from a REST service request. Currently I'm using the URL in rejection.config.url and testing if my REST URL service matches that value. Although this approach is working, I think this is not a good solution. Is there another way or a better way to do that?
Interceptor code:
angular.module("mainApp").factory("notFoundInterceptor",['$q','$location','$rootScope', function ($q,$location,$rootScope) {
return {
responseError: function (rejection) {
function isURLFromRESTService(rejection) {
var url = /myresturl/; //pattern used to match REST URL
return url.test(rejection.config.url);
}
if (rejection.status === 404 and !isURLFromRESTService(rejection)) { // Not found
$rootScope.notFound = {
erro: 'Não possível encontrar: ' + $location.url()
}
$location.path('/notfound');
}
return $q.reject(rejection);
}
};}]);
Here is an alternative: can't say if its better or not :-| ... you decide :)
When you invoke your REST API, pass a config object with a flag like:
$http.get('REST API URL', { ignore404: true })
Now in your interceptor you can check like:
if (rejection.status === 404 && !rejection.config.ignore404) {
$rootScope.notFound = {
erro: 'Não possível encontrar: ' + $location.url()
}
$location.path('/notfound');
}
Of course you will need to pass this config object for each of your REST API calls.
For that you may want to write your own wrapper over $http that adds the config object (or just add the ignore404 property if config object is passed from outside) and use this service in place of $http while invoking your REST API.

Angular : intercept specific request with $resource

I'm new to Angular, and am working on an interceptor. I created an angular factory to get some data from an API like that :
app.factory('Connection',['$resource',function($resource) {
return $resource('url',{param1: '1',param2: '55'},);
}]);
I also created the interceptor which looks like that :
app.factory('connectionInterceptor', function($q,$location) {
var connectionInterceptor = {
response: // code here
responseError: // code here
};
return connectionInterceptor;
});
The interceptor works well. But it intercepts every http request I do, and I'd like to make it work for a specific $resource. I read in angular $resource doc that there is a way to make it by adding an interceptor action/param to $resource. So I tried :
app.factory('Connection',['$resource',function($resource) {
return $resource('http://localhost:8080/api/login',{user: '1',password: '55'}, {},
query: {
method : 'GET',
interceptor : 'connectionInterceptor'
}
});
}]);
which didn't work. The thrown error is : Error in resource configuration for action query. Expected response to contain an object but got an array.
What did I miss ?
As you said, interceptors are globally set. I had to add a test to my response to check the $resource URL and add some specific treatment.
module.factory('interceptor', function() {
var interceptor = {
response: function(response) {
if (response.config.url.startsWith('my url')) {
// some treatment
}
else
// other treatment
return response;
}
return connectionInterceptor;
});

Can a request interceptor create a http response in angularJS

I am in process of creating Offline mode in an app. Whenever a http request is send, I would want an interceptor to detect the network state. If the state is no connectivity, I would want to create a mock response and make it feel like as if the response is coming from a server.
You could check if you are online or not by reading the status of the response in your interceptor, if its a 401 / 501/ etc:
var interceptor = ['$rootScope', '$q', function ($rootScope, $q) {
function success(response) {
return response;
}
function error(response) {
var status = response.status; // error code
if ((status >= 400) && (status < 500)) {
$rootScope.broadcast("AuthError", status);
return;
}
if ((status >= 500) && (status < 600)) {
$rootScope.broadcast("ServerError", status);
return;
}
// otherwise
return $q.reject(response);
}
return function (promise) {
return promise.then(success, error);
}
}]
There's another way, using html5, but I guess will not work on some browsers. This is done using navigator.onLine property, like:
if (navigator.onLine) {
//I'm online
} else {
I'm not online
}
(https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine)
If you throw an object inside your request interceptor this will trigger a call to the responseError interceptor, passing the throwed object as its argument.
You have to find a way notify responseError interceptor that this is not a real error and that it should return a custom response.
The responseError interceptor can choose to recover error returning a response object o fail returning a rejected promise (check angular's $http interceptor docs)
EDIT: If you throw an object from request interceptor you can't avoid an error message in console. It's better to return a rejected promise passing config object as value and put extra information in it for responseError detect the special situation.

Add data to angular $http request that will be returned in the response

I need to add some data to each response I send via $http in angular that will be in the response. In other works I'm trying to add an 'id' to the request because when the response is returned I need to associate it with the correct object that sent it. Is this possible? If so how would I go about it?
use interceptors, I'm quoting from the documentation:
For purposes of global error handling, authentication, or any kind of
synchronous or asynchronous pre-processing of request or
postprocessing of responses, it is desirable to be able to intercept
requests before they are handed to the server and responses before
they are handed over to the application code that initiated these
requests.
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
return {
'request': function(config) {
config.id = generateId(); //or a timestamp maybe?
return config;
},
'response': function(response) {
// do something on success
return response;
}
};
});
then add your
$httpProvider.interceptors.push('myHttpInterceptor');

Resources