Stop request in angularjs interceptor - angularjs

How can I stop a request in Angularjs interceptor.
Is there any way to do that?
I tried using promises and sending reject instead of resolve !
.factory('connectionInterceptor', ['$q', '$timeout',
function($q, $timeout) {
var connectionInterceptor = {
request: function(config) {
var q = $q.defer();
$timeout(function() {
q.reject();
}, 2000)
return q.promise;
// return config;
}
}
return connectionInterceptor;
}
])
.config(function($httpProvider) {
$httpProvider.interceptors.push('connectionInterceptor');
});

I ended up bypassing angular XHR call with the following angular Interceptor:
function HttpSessionExpiredInterceptor(sessionService) {
return {
request: function(config) {
if (sessionService.hasExpired()) {
/* Avoid any other XHR call. Trick angular into thinking it's a GET request.
* This way the caching mechanism can kick in and bypass the XHR call.
* We return an empty response because, at this point, we do not care about the
* behaviour of the app. */
if (_.startsWith(config.url, '/your-app-base-path/')) {
config.method = 'GET';
config.cache = {
get: function() {
return null;
}
};
}
}
return config;
}
};
}
This way, any request, POST, PUT, ... is transformed as a GET so that the caching mechanism can be
used by angular. At this point, you can use your own caching mechanism, in my case, when session
expires, I do not care anymore about what to return.

The $http service has an options
timeout to do the job.
you can do like:
angular.module('myApp')
.factory('httpInterceptor', ['$q', '$location',function ($q, $location) {
var canceller = $q.defer();
return {
'request': function(config) {
// promise that should abort the request when resolved.
config.timeout = canceller.promise;
return config;
},
'response': function(response) {
return response;
},
'responseError': function(rejection) {
if (rejection.status === 401) {
canceller.resolve('Unauthorized');
$location.url('/user/signin');
}
if (rejection.status === 403) {
canceller.resolve('Forbidden');
$location.url('/');
}
return $q.reject(rejection);
}
};
}
])
//Http Intercpetor to check auth failures for xhr requests
.config(['$httpProvider',function($httpProvider) {
$httpProvider.interceptors.push('httpInterceptor');
}]);

Not sure if it is possible in general. But you can start a $http request with a "canceler".
Here is an example from this answer:
var canceler = $q.defer();
$http.get('/someUrl', {timeout: canceler.promise}).success(successCallback);
// later...
canceler.resolve(); // Aborts the $http request if it isn't finished.
So if you have control over the way that you start your request, this might be an option.

I just ended up in returning as an empty object
'request': function request(config) {
if(shouldCancelThisRequest){
return {};
}
return config;
}

Here is what works for me, especially for the purposes of stopping the outgoing request, and mocking the data:
app
.factory("connectionInterceptor", [
"$q",
function ($q) {
return {
request: function (config) {
// you can intercept a url here with (config.url == 'https://etc...') or regex or use other conditions
if ("conditions met") {
config.method = "GET";
// this is simulating a cache object, or alternatively, you can use a real cache object and pre-register key-value pairs,
// you can then remove the if block above and rely on the cache (but your cache key has to be the exact url string with parameters)
config.cache = {
get: function (key) {
// because of how angularjs $http works, especially older versions, you need a wrapping array to get the data
// back properly to your methods (if your result data happens to be an array). Otherwise, if the result data is an object
// you can pass back that object here without any return codes, status, or headers.
return [200, mockDataResults, {}, "OK"];
},
};
}
return config;
},
};
},
])
.config(function ($httpProvider) {
$httpProvider.interceptors.push("connectionInterceptor");
});
If you are trying to mock a result like
[42, 122, 466]
you need to send an array with some http params back, its just how the ng sendReq() function is written unfortunately. (see line 1414 of https://github.com/angular/angular.js/blob/e41f018959934bfbf982ba996cd654b1fce88d43/src/ng/http.js#L1414 or snippet below)
// from AngularJS http.js
// serving from cache
if (isArray(cachedResp)) {
resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3], cachedResp[4]);
} else {
resolvePromise(cachedResp, 200, {}, 'OK', 'complete');
}

Related

Circular dependency while pushing http interceptor

I am using http Interceptor to intercept each http request in my application.
But I am getting Circular dependency found: $http <- APIInterceptor <- $http <- $templateRequest <- $compile
here is my Service code:
mPosServices.factory('mosServiceFactory', function ($http, $rootScope, $cookies, $q) {
return{
refresh_token: function () {
var refreshToken = $http({
method: "get",
url: "myservice/oauth/token?grant_type=refresh_token&client_id=restapp&client_secret=restapp&refresh_token=" + $cookies.get('refresh_token'),
})
return refreshToken;
}
});
mPosServices.service('APIInterceptor', ['mosServiceFactory','$cookies',function (mosServiceFactory,$cookies) {
var service = this;
service.request = function (config) {
if (!$cookies.get('access_token')) { //if access_token cookie does not exist
mosServiceFactory.refresh_token().then(function (response) {
var date = new Date();
date.setTime(date.getTime() + (response.data.expiresIn * 1000));
$cookies.remove('access_token');
$cookies.put('access_token', response.data.value, {expires: date});
$cookies.put('refresh_token', response.data.refreshToken.value);
}); //call the refresh_token function first
}
return config;
};
service.responseError = function (response) {
return response;
};
}]);
and pushing it as:
$httpProvider.interceptors.push('APIInterceptor');
in config function.
Am I doing something wrong here?
Even I tried to inject $http manually using
$injector
, but getting same error.
Kindly Help me.
You indeed need to add use $injector to get mosServiceFactory instance inside of interceptor. But this is not all you need to do. You also need to make sure you don't fall into infinite request loop because interceptor also makes a request. What you can do is to check if current request is the one for token refresh and if so don't fire one more request, I'm checking the URL for this.
One more important thing to mention. You need to return promise object from interceptor which resolves to original request config. This way it guaranties that intercepted request will be reissued after token is retrieved.
All together will look like this:
mPosServices.service('APIInterceptor', ['$injector', '$cookies', function($injector, $cookies) {
var service = this;
service.request = function(config) {
if (!$cookies.get('access_token') && config.url.indexOf('myservice/oauth/token?grant_type=') === -1) {
return $injector.get('mosServiceFactory').refresh_token().then(function(response) {
var date = new Date();
date.setTime(date.getTime() + (response.data.expiresIn * 1000));
$cookies.remove('access_token');
$cookies.put('access_token', response.data.value, {
expires: date
});
$cookies.put('refresh_token', response.data.refreshToken.value);
}).then(function() {
return config; // <-- token is refreshed, reissue original request
});
}
return config;
};
service.responseError = function(response) {
return response;
};
}]);
Check the demo I was testing solution on to see how it recovers original request after token is loaded.
Demo: http://plnkr.co/edit/1Aey2PThZQ4Y8IRZuVOl?p=preview

how to custom set angularjs jsonp callback name?

I have to fetch json data from some website through angularjs. i have done everything correctly according to the link below.
My problem is the api does not allow callback parameter to have any character except letters, numbers and _. And since angular replaces the JSON_CALLBACK with something like 'angular.callbacks._0', its not being allowed.
How can i custom set this value for angularjs?
parsing JSONP $http.jsonp() response in angular.js
thanks
The callback names are hard-coded here httpBackend.js#L55, so you can't config it.
But, you could write a HTTP interceptor to workaround it like this:
.factory('jsonpInterceptor', function($timeout, $window, $q) {
return {
'request': function(config) {
if (config.method === 'JSONP') {
var callbackId = angular.callbacks.counter.toString(36);
config.callbackName = 'angular_callbacks_' + callbackId;
config.url = config.url.replace('JSON_CALLBACK', config.callbackName);
$timeout(function() {
$window[config.callbackName] = angular.callbacks['_' + callbackId];
}, 0, false);
}
return config;
},
'response': function(response) {
var config = response.config;
if (config.method === 'JSONP') {
delete $window[config.callbackName]; // cleanup
}
return response;
},
'responseError': function(rejection) {
var config = rejection.config;
if (config.method === 'JSONP') {
delete $window[config.callbackName]; // cleanup
}
return $q.reject(rejection);
}
};
})
Example Plunker: http://plnkr.co/edit/S5K46izpIxHat3gLqvu7?p=preview
Hope this helps.

AngularJS global $http state ajax loader

I have an AngularJS app, and need an ajax loader for every request done by the $http - is there a simple way to do this.
My solution now is to set $rootScope.loading = 1 everytime i call a $http, and on the success set $rootScope.loading = 0..
What is the "best practice" for this?
My code now looks like:
$rootScope.loading = 1;
$http({method : "POST", url:url, data: utils.params(data), headers: {'Content-Type': 'application/x-www-form-urlencoded'}}).success(function() {
$rootScope.loading = 0;
});
In this case will be better to use interceptor
Anytime that we want to provide global functionality on all of our requests, such as authentication,
error handling, etc., it’s useful to be able to provide the ability to intercept all requests before they
pass to the server and back from the server.
angular.module('myApp')
.factory('myInterceptor',
function ($q,$rootScope) {
var interceptor = {
'request': function (config) {
$rootScope.loading = 1;
// Successful request method
return config; // or $q.when(config);
},
'response': function (response) {
$rootScope.loading = 0;
// successful response
return response; // or $q.when(config);
},
'requestError': function (rejection) {
// an error happened on the request
// if we can recover from the error
// we can return a new request
// or promise
return response; // or new promise
// Otherwise, we can reject the next
// by returning a rejection
// return $q.reject(rejection);
},
'responseError': function (rejection) {
// an error happened on the request
// if we can recover from the error
// we can return a new response
// or promise
return rejection; // or new promise
// Otherwise, we can reject the next
// by returning a rejection
// return $q.reject(rejection);
}
};
return interceptor;
});
and register it to the config
angular.module('myApp')
.config(function($httpProvider) {
$httpProvider.interceptors.push('myInterceptor');
});
example from ng-book
Use an http interceptor in order to intercept all your $http requests\responses and perform logic in them.
Here's an example of creating a custom one.
Here's an example of a ready module.

Angular - abort ajax request when using Restangular

I have a method that calls an angular service and consequently makes an ajax request via the service. I need to make sure that if this is called several times, the previous request in aborted (if it hasn't already been resolved that is).
This method can get called multiple times. This method is actually from ngTable on ngTableParams:
getData = function($defer, params) {
myService.getRecord(params).then(function(res){
...
$defer.resolve(res.Records);
});
}
Here's the method on the service:
this.getRecords = function(params) {
...
return Restangular
.all('/api/records')
.post(filters);
};
If ngTable makes 3 calls I want the first 2 to be aborted (unless of course they returned so fast that it got resolved)
You can abort $http calls via the timeout config property, which can be a Promise, that aborts the request when resolved.
So in restangular, you can do this like
var abort = $q.defer();
Restangular.one('foos', 12345).withHttpConfig({timeout: abort.promise}).get();
abort.resolve();
To integrate it with your usecase, for example, you could have this in your service:
var abortGet;
this.getRecords = function(params) {
...
if (abortGet) abortGet.resolve();
abortGet = $q.defer();
return Restangular
.all('/api/records')
.withHttpConfig({timeout: abortGet.promise})
.post(filters);
}
This way calling getRecords always aborts the previous call if has not been resolved yet!
This is another approach if you want to abort all http requests when change the state for UI-router:
angular
.run(function(HttpHandlerSrv) {
HttpHandlerSrv.abortAllRequestsOn('$stateChangeSuccess');
HttpHandlerSrv.R.setFullRequestInterceptor(function(element, operation, route, url, headers, params, httpConfig) {
httpConfig = httpConfig || {};
if(httpConfig.timeout === undefined) {
httpConfig.timeout = HttpHandlerSrv.newTimeout();
}
return { element: element, params: params, headers: headers, httpConfig: httpConfig };
});
})
.factory('HttpHandlerSrv', HttpHandlerSrv);
/** ngInject */
function HttpHandlerSrv($q, $rootScope, Restangular) {
var requests = [];
return {
R: Restangular,
newTimeout: newTimeout,
abortAllRequests: abortAllRequests,
abortAllRequestsOn: abortAllRequestsOn
};
function newTimeout() {
var request = $q.defer();
requests.push(request);
return request.promise;
}
function abortAllRequests() {
angular.forEach(requests, function(request) {
request.resolve();
});
requests = [];
}
function abortAllRequestsOn(event) {
$rootScope.$on(event, function(event, newUrl, oldUrl) {
if(newUrl !== oldUrl) { abortAllRequests(); }
});
}
}

AngularJS intercept all $http requests

I can't seem to get the $httpProvider.interceptors to actually intercept. I created a sample on JSFiddle that logs when the interceptor is run and when the $http response is successful. The request interceptor is run after the response is already returned as successful. This seems a bit backwards.
I can't use transformRequest because I need to alter the params in the config. That part isn't shown in the sample.
I'm using AngularJS 1.1.5
http://jsfiddle.net/skeemer/K7DCN/1/
Javascript
var myApp = angular.module('myApp', []);
myApp.factory('httpInterceptor', function ($q) {
return {
request: function (config) {
logIt('- Modify request');
return config || $q.when(config);
}
};
});
myApp.config(function ($httpProvider) {
$httpProvider.interceptors.push('httpInterceptor');
});
function MyCtrl($scope, $http) {
// Hit a server that allows cross domain XHR
$http.get('http://server.cors-api.appspot.com/server?id=8057313&enable=true&status=200&credentials=false')
.success(function (data) {
//logIt(data[0].request.url);
logIt('- GET Successful');
});
$scope.name = 'Superhero';
}
// Just the logging
var logs = document.getElementById('logs');
function logIt(msg) {
var e = document.createElement('div');
e.innerHTML = msg;
logs.insertBefore(e, logs.firstChild);
}
HTML
<div ng-controller="MyCtrl">Hello, {{name}}!</div>
<br/>
<div id="logs"></div>
If you want the option to accept/reject a request at interception time you should be using $httpProvider.responseInterceptors, see example below:
$httpProvider.responseInterceptors.push(function($q) {
return function(promise){
var deferred = $q.defer();
promise.then(
function(response){ deferred.reject("I suddenly decided I didn't like that response"); },
function(error){ deferred.reject(error); }
);
return deferred.promise;
};
});
EDIT Didn't read your comment, indeed responseInterceptors is now obsolete an that's how you do it instead:
$httpProvider.interceptors.push(function($q) {
return {
request: function(config){ return config; },
response: function(response) { return $q.reject(response); }
};
});
I learned something useful, thanks
The request interceptor isn't running after the data is returned. It's running before. Your logIt function inserts the newest message at the top. If you change your code to use the $log service, you'll see that the interceptor runs first.

Resources