I'm writing a response intercepter for an API call with ngResource like this:
var Thing = $resource('/api/things/:id', {id: '#id'}, {
queryAll: {
method: 'GET',
interceptor: {
response: function(response) {
var instance = response.resource;
doAsyncStuffHere(instance.data);
return instance;
}
}
}
});
The interceptor function should enrich the data from the response but returns the unmodified instance before the async stuff is done (as expected, since it's async...).
Is there a good way, to enrich the data asynchronously and make the return statement "wait" until it's done? Callback? Promise?
After searching the right question, i've found an article who solved my issue:
Sometimes there is a need to make some asynchronous operations inside the interceptor. Luckily AngularJS allows us to return a promise that will be resolved later. This will defer the request sending in case of request interceptor and will defer the response resolving in case of response interceptor.
From: http://www.webdeveasy.com/interceptors-in-angularjs-and-useful-examples/
And here is what my code now looks like and working like a charm.
var Thing = $resource('/api/things/:id', {id: '#id'}, {
queryAll: {
method: 'GET',
interceptor: {
response: function(response) {
var instance = response.resource,
_deferred = $q.defer();
doAsyncStuffHere(instance.data).then(function() {
_deferred.resolve(instance);
});
return deferred.promise;
}
}
}
});
Use o exemplo:
https://docs.angularjs.org/api/ng/service/http#$http-arguments;
A propriedade "transformRequest" é uma melhor opção;
Related
In my controller i want send a request using get method if $http, in that get method i want to send the sessionID in headers. Below am giving the code snippet please check.
this.surveyList = function () {
//return session;
return $http.get('http://op.1pt.mobi/V3.0/api/Survey/Surveys', {headers: { 'sessionID': $scope.sessionid}})
.then(function(response){
return response.data;
}, function(error){
return error;
});
}
but this is not working when i send this vale in backend they getting null.
So how to resolve this.
we have a issue where the api is getting called twice from angular , however it works only once when called with the POSTMAN. And here with the custom header passed to the api, the action is called twice. What could be the reason for it?
Try in this way,
$http({
method: 'GET',
url: 'http://op.1pt.mobi/V3.0/api/Survey/Surveys',
headers: {
'sessionId': $scope.sessionid
}
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs,
// or server returns response with an error status.
});
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;
});
I'm willing to retrieve the response header of a resource request, cause I've put pagination information and something else in it rather than the response body, to make the REST api clear.
Though we can get it from the success / error callback like below:
Object.get({type:'foo'}, function(value, responseHeaders){
var headers = responseHeaders();
});
Where 'Object' is my resource factory service.
Further, when I'm trying to make the route change after required resources resolved, I've tried this:
.when('/list', {
templateUrl: 'partials/list.html',
controller: 'ListCtrl',
// wait for the required promises to be resolved before controller is instantialized
resolve: {
objects: ['Object', '$route', function(Object, $route){
return Object.query($route.current.params).$promise;
}]
}
})
and in controller, just inject "objects" instead of Object service, because it's resolved and filled in with real data.
But I got problem when I try to get headers info from the "objects" in controller.
I tried objects.$promise.then(function(data, responseHeaders){}), but responseHeader was undefined.
How can I change the $resource service's behavior so that it throws the responseHeader getter into the $promise then() callback function?
My service "Object" for reference:
myServices.factory('Object', ['$resource',
function($resource){
return $resource('object/:id', {id: '#id'}, {
update: {method: 'PUT'},
});
}
]);
I had the exact same problem. I used an interceptor in the resource definition to inject the http headers in the resource.
$resource('/api/resource/:id', {
id: '#id'
}, {
index: {
method: 'GET',
isArray: true,
interceptor: {
response: function(response) {
response.resource.$httpHeaders = response.headers;
return response.resource;
}
}
}});
Then, in the then callback, the http headers are accesible through $httpHeaders:
promise.then(function(resource) {
resource.$httpHeaders('header-name');
});
I think I had a similar problem: After POSTing a new resource I needed to get the Location header of the response, since the Id of the new resource was set on the server and then returned via this header.
I solved this problem by introducing my own promise like this:
app.factory('Rating', ['$resource',
function ($resource) {
// Use the $resource service to declare a restful client -- restangular might be a better alternative
var Rating = $resource('http://localhost:8080/courserater/rest/ratings-cors/:id', {id: '#id'}, {
'update': { method: 'PUT'}
});
return Rating;
}]);
function RestController($scope, $q, Rating) {
var rating = new Rating();
var defer = $q.defer(); // introduce a promise that will be resolved in the success callback
rating.$save(function(data, headers){ // perform a POST
// The response of the POST contains the url of the newly created resource
var newId = headers('Location').split('/').pop();
defer.resolve(newId)
});
return defer.promise;
})
.then (function(newId) {
// Load the newly created resource
return Rating.get({id: newId}).$promise; // perform GET
})
.then(function(rating){
// update the newly created resource
rating.score = 55;
return rating.$update(); // perform PUT
});
}
We can't use .then for returning the header because the promise doesn't allow for multiple return values. (e.g., (res, err))
This was a requested feature, and was closed https://github.com/angular/angular.js/issues/11056
... the then "callbacks" can have only [one] argument. The reason for this is that those "callbacks" correspond to the return value / exception from synchronous programming and you can't return multiple results / throw multiple exceptions from a regular function.
I'm trying to figure out if it is possible to use a $http interceptor to cancel a request before it even happens.
There is a button that triggers a request but if the user double-clicks it I do not want the same request to get triggered twice.
Now, I realize that there's several ways to solve this, and we do already have a working solution where we wrap $http in a service that keeps track of requests that are currently pending and simply ignores new requests with the same method, url and data.
Basically this is the behaviour I am trying to do with an interceptor:
factory('httpService', ['$http', function($http) {
var pendingCalls = {};
var createKey = function(url, data, method) {
return method + url + JSON.stringify(data);
};
var send = function(url, data, method) {
var key = createKey(url, data, method);
if (pendingCalls[key]) {
return pendingCalls[key];
}
var promise = $http({
method: method,
url: url,
data: data
});
pendingCalls[key] = promise;
promise.finally(function() {
delete pendingCalls[key];
});
return promise;
};
return {
post: function(url, data) {
return send(url, data, 'POST');
}
}
}])
When I look at the API for $http interceptors it does not seem to be a way to achieve this. I have access to the config object but that's about it.
Am I attempting to step outside the boundaries of what interceptors can be used for here or is there a way to do it?
according to $http documentation, you can return your own config from request interceptor.
try something like this:
config(function($httpProvider) {
var cache = {};
$httpProvider.interceptors.push(function() {
return {
response : function(config) {
var key = createKey(config);
var cached = cache[key];
return cached ? cached : cached[key];
}
}
});
}
Very old question, but I'll give a shot to handle this situation.
If I understood correctly, you are trying to:
1 - Start a request and register something to refer back to it;
2 - If another request takes place, to the same endpoint, you want to retrieve that first reference and drop the request in it.
This might be handled by a request timeout in the $http config object. On the interceptor, you can verify it there's one registered on the current request, if not, you can setup one, keep a reference to it and handle if afterwards:
function DropoutInterceptor($injector) {
var $q = $q || $injector.get('$q');
var dropouts = {};
return {
'request': function(config) {
// I'm using the request's URL here to make
// this reference, but this can be bad for
// some situations.
if (dropouts.hasOwnProperty(config.url)) {
// Drop the request
dropouts[config.url].resolve();
}
dropouts[config.url] = $q.defer();
// If the request already have one timeout
// defined, keep it, othwerwise, set up ours.
config.timeout = config.timeout || dropouts[config.url];
return config;
},
'requestError': function(reason) {
delete dropouts[reason.config.url];
return $q.reject(reason);
},
'response': function(response) {
delete dropouts[response.config.url];
return response;
},
'responseError': function(reason) {
delete dropouts[reason.config.url];
return $q.reject(reason);
}
};
}
I have a service where I am pulling data from server. When I click the button to send out the request to server through this service, the window freezes until I receive a response from server. Is there anything I can do to make this request asynchronous ?
Here is my service.
app.factory('service', function($http) {
return {
getLogData : function(startTime,endTime){
return $http({
url: baseURL + 'getLogData',
method: 'GET',
async: true,
cache: false,
headers: {'Accept': 'application/json', 'Pragma': 'no-cache'},
params: {'startTime': startTime , 'endTime': endTime}
});
}
};
)};
HTML.
<button ng-click="getData()">Refresh</button>
<img src="pending.gif" ng-show="dataPending" />
Code
$scope.getData = function(){
service.getLogData().success(function(data){
//process data
}).error(function(e){
//show error message
});
}
While there is some argument about the pros and cons of your approach, I am thinking that the problem is answered here: AJAX call freezes browser for a bit while it gets response and executes success
To test if this in fact part of the problem, dummy up a response and serve it statically. I use Fiddler or WireShark to get the response and then save to a file like testService.json. XHR and all of it's various derivatives like $HTTP $.ajax see it as a service though the headers might be slightly different.
Use the success promise, and wrap up the log data in a set of objects that you can attach to a $scope.
So instead of having your service have a blocking method, have it maintain a list of "LogEntries".
// constructor function
var LogEntry = function() {
/*...*/
}
var logEntries = [];
// Non-blocking fetch log data
var getLogData = function() {
return $http({
url : baseURL + 'getLogData',
method : 'GET',
async : true,
cache : false,
headers : { 'Accept' : 'application/json' , 'Pragma':'no-cache'},
params : {'startTime' : startTime , 'endTime' : endTime}
}).success(function(data) {;
// for each log entry in data, populate logEntries
// push(new LogEntry( stuff from data ))...
};
}
Then in your controller, inject your service and reference this service's log data array so Angular will watch it and change the view correctly
$scope.logEntries = mySvc.logEntries;
Then in the HTML, simply do something over logEntries:
<p ng-repeat="logEntry in logEntries">
{{logEntry}}
</p>
use this code to config
$httpProvider.useApplyAsync(true);
var url = //Your URL;
var config = {
async:true
};
var promise= $http.get(url, config);
promise.then(
function (result)
{
return result.data;
},
function (error)
{
return error;
}
);