Confirm HTTP Request Sent - javascript

I am using the $http get from Angular, I want to confirm that a http request is sent to our API. I am aware of the .success method but this waits for a return, is there a way to just get the $http object to confirm the request has been sent?

I used something similar to track number of requests sent, which are waiting for response.
Add an http-interceptor on app level:
.config(['$httpProvider',
function($httpProvider) {
$httpProvider.interceptors.push('RequestCounterInterceptor');
}
])
The code of interceptor (you can modify it to handle just requests to your API's url):
.factory('RequestCounterInterceptor', ['RequestCounterService', '$q',
function(RequestCounterService, $q) {
return {
'request': function(request) {
RequestCounterService.increase();
return request;
},
'requestError': function(request) {
RequestCounterService.increase();
return $q.reject(request);
},
'response': function(response) {
RequestCounterService.decrease();
return response;
},
'responseError': function(response) {
RequestCounterService.decrease();
return $q.reject(response);
}
};
}
])
The service:
.service('RequestCounterService', ['$log',
function($log) {
var currentRequestCount = 0; // init
return {
increase: function() {
currentRequestCount++;
$log.log("currentRequestCount ", currentRequestCount);
},
decrease: function() {
currentRequestCount--;
$log.log("currentRequestCount ", currentRequestCount);
},
getCount: function() {
return currentRequestCount;
}
};
}
])
Then anywhere in you controller, directive... you can use RequestCounterService.getCount() to see how many requests have been sent and did not received response yet. You could display a message to user like There are currently XY requests being processed by the server or similar.
But in case the processing takes too much time, you should solve your issue server-side as James Gaunt proposed in comments.

Related

how to use HttpBackend for the $http

I'm having a hardtime to create a Test with the Controller that uses promise when doing initialization. here's my angularjs scirpt.
Javascript.js
var appModule = angular.module('appModule', []);
appModule.controller('c0001Controller', function($http, $window) {
var user = {};
this.c0001Data = user;
this.submitForm = function() {
if(!angular.isUndefined(this.c0001Data.user_id) && !angular.isUndefined(this.c0001Data.password))
{
var promise = $http.post('C0001Login', user);
promise.success(function(data, status, headers, config) {
if(data.message == 'error'){
alert('Invalid Username/Password');
} else {
$window.location.href = data.url + "#/c0003";
}
});
promise.error(function(data, status, headers, config) {
alert("Invalid Username/Password");
});
}
else {
alert ("Invalid/ Username/password");
}
};
});
Using $httpBackend is more like setting up a fake call to intercept the original call of your $http service in your test cases.
Say you in your controller/service you have an $http get that gets from the request url 'api/employees'. In your test you would like to do something like this before the actual call to your function that calls $http:
$httpBackend.expectGET('api/employees').and.return(200, [
{ id: 1, name: 'sample' },
{ id: 2, name: 'sample 2' }
]);
(JasmineJS) In this way the original $http get request to your url 'api/employees' will not be called instead, the $httpBackend's setup/expected call will be called, and a http status code of 200 along with the array data will be returned.
This would work well on expecting a POST with data parameters. You should always know the request url, the data and other configs used in your original $http calls.
P.S. Always return appropriate HTTP status codes when using $httpBackend. Say, returning an HTTP status code 400 will trigger your catch block in the code you're testing.

Creating a function around angular $http requests

First project in AngularJS and I started creating my services (factories) that I made modular like this
angular.module('app.services.public', [])
.factory('publicService', ['$http', function publicService($http) {
var results = {};
results.contact = function (name, email, message){
return $http.get();
};
return results;
}]);
That I then call in my main angular app by including it. When I call it, I need to listen for success or error
publicService.contact().success(callback).error(callback)
My question is, I'm going to be doing a lot of API requests through these services and seems to be bad code to listen to the error everytime since 90% of the time it will do the same thing.
How can I create a wrapper around the $http.get or around all factory calls?
So something like
apiCall = function (url, data, successCallback, errorCallback){
$http.get(url,data).success(function(){
successCallback()
}).error(function(){
if(errorCallback()){ errorCallback(); return; }
// or display general error message
})
}
I would recommend against converting promise-based into callback-based APIs. Angular adopted promises and it best to stay with them.
Also, stay away from $http-specific .success/.error and use promise .then/.catch APIs.
How wide do you need to cast your net to handle $http errors?
1) Say, it only applies to your publicService service, then you can "handle" it at the each function:
.factory("publicService", function($http, $q){
function handleError(){
// invokes error handlers
}
return {
onError: function(cb){
// register error handlers
},
doSomethingA: function(){
return $http.get("some/url/A")
.then(function(response){
return response.data;
})
.catch(function(error){
handleError(error);
return $q.reject(error); // still "rethrow" the error
}
},
doSomethingB: function(){
// similar to above
},
// etc...
};
})
Then you could separate request from error handling:
.controller("MainCtrl", function($scope, publicService){
publicService.onError(function(error){
$scope.showError = true; // or something like that
})
})
.controller("FunctionACtrl", function($scope, publicService){
publicService.doSomethingA()
.then(function(data){
$scope.data = data;
});
})
2) Of course, the above, would only apply to request made via publicService. If you want to catch all $http errors, you could implement an $http interceptors. I won't go into detail - there is enough info in documentation and elsewhere - but it would could work like below:
.factory("ErrorService", function(){
return {
onError: function(cb){
// register error handlers
},
broadcastError: function(error){
// invoke error handlers
}
};
})
Then in interceptor, use ErrorService as a dependency:
'responseError': function(rejection) {
ErrorService.broadcastError(rejection);
return $q.reject(rejection);
}
Then you could handle the errors globally:
.controller("MainCtrl", function($scope, ErrorService){
ErrorService.onError(function(error){
$scope.showError = true; // or something like that
})
})
You have the right idea. You can do it easily with a Factory.
myApp.factory(APIService, function(publicService, $http) {
return {
// create methods in here
...
contact: function(cb) {
$http.get(url,data).success(cb).error(function(err){
console.error('oh no!', err);
});
}
};
});
Then you can use it in your controllers.
APIService.contact(function(data){
console.log('response from the api!', data);
});
You can even move your error handler to its own factory as well.
I would suggest an implementation using angular's $q service.
angular.module('app.services.public', [])
.factory('publicService', ['$http', '$q', function publicService($http, $q) {
var results = {};
results.contact = function (name, email, message){
return $q.when($http.get());
};
return results;
}]);
Or rather than use the $q.when(...) method you can use $q.deferred like so:
angular.module('app.services.public', [])
.factory('publicService', ['$http', '$q', function publicService($http, $q) {
var deferred = $q.deferred();
var results = {};
results.contact = function (name, email, message){
$http.get().success(function(data){
deferred.resolve({
// assumes data retried from http request has a title and price attribute
title: data.title,
cost: data.price});
}).error(function(data){
deferred.reject(data);
});
};
return deferred.promise;
}]);

Is there a way I can give an error alert when my HTTP calls fail because of internet access?

My code makes many AngularJS $http requests. Often it is 3-4 at the same time.
Is there some way that I can intercept the http messages so that I get just one alert pop up if the internet connectivity is lost and there are multiple requests going on? I have seen other sites that do this but then if the action requires a number of http calls it seems that I could get more than one error popup coming.
If possible I would like to do this in just the one place in my code.
You need add responseInterceptors inside you $httpProvider in configuration phase of angular. This interceptor gets called after angular $httpProvider processing the response.
CODE
module.config(['$httpProvider', function($httpProvider) {
var interceptor = ['$rootScope', '$q', '$location', function(scope, $q, $location) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 500) {
alert("Internal Server Error")
return;
}
if (status == 404) {
alert("Page not found")
return;
}
// otherwise
return $q.reject(responseInterceptors);
}
return function(promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
}]);
Above code will provide you better control on error handling when any request fails.
For more details refer this anwser.
Update
For showing alert only once we could create a service,if error occurred then that will handle the set error variable
Service
module.service('errorService',function(){
//getter
this.getErrorFlag = function(){
return this.isError;
}
//setter
this.setErrorFlag = function(val){
this.isError = val;
}
});
Interceptor
module.config(['$httpProvider', function($httpProvider) {
var interceptor = ['$rootScope', '$q', '$location','errorService', function(scope, $q, $location,errorService) {
function success(response) {
return response;
}
function error(response) {
//setting error variable
errorService.setErrorFlag(true)
return $q.reject(responseInterceptors);
}
return function(promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
}]);
Inside you controller put all $http call promises inside $q.all, and when promises gets resolved then check for isError flag of errorService.
IsError flag will be true if error occurred at least one & by using it you can show error only once.
Controller
module.controller('appCtrl',['$scope','$q','errorService',function($scope,$q,errorService){
var ajax1 = $http.get('url').then(function(){
},
function(){
});
var ajax2 = $http.get('url').then(function(){
},
function(){
});
var ajax3 = $http.get('url').then(function(){
},
function(){
});
errorService.setErrorFlag(false);
$q.all(ajax1, ajax2, ajax3).then(function(data){
//check for isError flag from service
if(errorService.getErrorFlag())
alert('Error occurred while processing request.'); //this will show only once
else
alert('No error occurred while processing request.')
});
}]);
Hope this could help you.
Thanks.
If you don't want to use an interceptor, you could simply process the error callback of your $http calls:
$scope.httpError = null;
$scope.processHttpError = function() {
// If you don't already have got an http error
if (!$scope.httpError) {
$scope.httpError = "Cannot load stuff";
}
};
$http.get('/someUrl')
.success(function(data, status, headers, config) { ... })
.error($scope.processHttpError);

In AngularJS, how can I apply a check function on all the responses of `$http`?

$http({
method: 'POST',
url: '/getAllUsers'
}).success(function(data, status, headers, config) {
if (data.length === 0) {
$location.path('/admin/login');
}
// process data
if (data.ok == 1) {
// correct data
} else {
// error
}
})
I use $http to fetch server data, and the server will respond an empty string if the auth fails. What I want to do is, if data is empty, then the application should redirect to login page. But since there are many controllers, and each controller has several $http call, so I don't want to copy and paste the directing code everywhere.
Is there any way I can "inject" this function on every $http response without writing the same code everywhere?
And am I able to apply it only on $http calls in some specific controllers?
You can use a http intercetor. The intercetor's response method is called right after $http receives the response from the backend. You can modify the response or make other actions. The method get called with the http response object and needs to return the response object directly, or as a promise containing the response or a new response object.
$provide.factory('myHttpInterceptor', function() {
return {
'response': function(response) {
if (/* response failed */ ) {
// $rootScope broadcast
// or $location login
// or whatever you want
}
return response;
}
};
});
$httpProvider.interceptors.push('myHttpInterceptor');
Edit:
I'm not sure if this is correct usage of $http config (I'never done this..) but you could also conditionally add a transformResponse function to a single $http call like this:
function transformErrorResponse(data, headersGetter, status) {
if (/* failed */)
// handle failure
}
$http({ transformResponse : transformErrorResponse})
.get(/* */)
.then(/* */);
You can create a factory definition and push it in config phase as Interceptor (using $httpprovider)
Factory Definition
angular.module('myApp').factory('httpInterceptor', ['$location', function($location) {
var httpInterceptor = {
response: function(config){
if (config.data.trim() == "") {
$location.path('\login');
}
return config;
}
};
return httpInterceptor;
}]);
Config Phase
angular.module('myApp').config(['$httpProvider' ,function($httpProvider) {
$httpProvider.interceptors.push('httpInterceptor');
}]);
You can write a simple config for $http in your module config and write your error handling in the transformResponse property of the $http config.
Something like this:
angular.module("yourModule", []).config ($http) ->
$http({
transformResponse:(data, headersGetter, status)->
/**Handle your data empty stuff here. ***/
})

angular-spinner | Skipping $http.post error handling

I am using angular-spinner to intercept all http request from my application and show a loading spinner.
Relevant code:-
//spinner configuration START
myApp.factory('spinnerInterceptor', ['usSpinnerService', function(usSpinnerService) {
return {
request: function(config) {
usSpinnerService.spin('spinner-1');
return config;
},
response:function(config){
usSpinnerService.stop('spinner-1');
return config;
},
responseError:function(config){
usSpinnerService.stop('spinner-1');
return config;
}
};
}]);
myApp.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('spinnerInterceptor');
}]);
//spinner configuration END
Except starting/stopping the spinner , I am simply just returning the config object.
Problem:-
One of my POST RESTful endpoint, return 404 status with an error message and still success handler of the $http block gets executed? Why?
$http({
method : 'POST',
url : url,
params :paramsJson,
data : _data
}).success(function(data,status) {
// THIS GET EXECUTED AFTER SPINNER INTERCEPTOR WITH STATUS 404 !!!!
}).error(function(data,status) {
// THIS BLOCK I EXPECT TO RUN IN CASE OF 404 or any non 2XX response
});
Before success/error handler of the $http, the spinnerInterceptor do get executed, which is somewhere playing with the error handling of the promise that gets returned.
When running my code without the spinner interceptor, everything works as expected.
Please help me fix this.
The problem here is "responseError" interceptor. you need to reject request in this block. else error is already handled in you interceptor so what ever you return here will go to success block.
The corrected interceptor code is:
responseError:function(config){//here we get response rename it
usSpinnerService.stop('spinner-1');
return $q.reject(config);
}
You can refer to Error Handling in angular for more info.
I had to develop an "automatic" spinner recently in a project. I used $q promises to signal success / error on the interceptor methods and it worked fine. My code was:
(function () {
'use strict';
function httpBusyInterceptor($q) {
function _responseError (response) {
//Hide spinner code
return $q.reject(response);
}
function _response (response) {
//Hide spinner code
return response || $q.when(response);
}
function _request (config) {
//Show spinner code
return config || $q.when(config);
}
return {
request: _request,
response: _response,
responseError: _responseError
};
}
httpBusyInterceptor.$inject = ['$q'];
angular.module('common.ui').factory('httpBusyInterceptor', httpBusyInterceptor);
})();

Categories