问题
In order to better consume a hateoas enabled rest api I got the idea to intercept http calls and add some methods to my resource objects before returning them.
The idea is that if the resource has an array of links I'll add methods to easy further http requests.
The below code is push to the interceptors array of a $httpProvider and does pretty much what I want.
define(['angular'], function (angular) {
var $http = angular.injector(['ng']).get('$http');
function flattenLinks(links) {
for (var key in links) {
var rel = links[key].rel;
var href = links[key].href;
links[rel] = href;
}
}
return {
'response': function responseHandler(response) {
var data = response.data ? response.data : response;
if(typeof data === 'string' || !data.hasOwnProperty('links')) {
return response;
}
if(data.links instanceof Array) {
flattenLinks(data.links);
}
if(data instanceof Array) {
for(var key in data) {
responseHandler(data[key]);
}
}
data.hasLink = function(link) {
return link in data.links;
};
data.get = function(rel) {
return $http.get(data.links[rel]);
};
data.delete = function(rel) {
return $http.delete(data.links[rel]);
};
data.post = function(rel) {
return $http.post(data.links[rel], data);
};
data.put = function(rel) {
return $http.put(data.links[rel], data);
};
return response;
}
};
});
The problem is that when I use, as seen below, my added methods to do requests the response isn't handled by my interceptor. The $http.get, $http.delete, etc. done from within my interceptor isn't intercepted (naturally!).
vm.someResourceObj.get('self').then(function(response) {
console.log(response);
});
So the question is. How do I get the internal calls to $http handled?
回答1:
First of all - this is not an answer to you question =)
But it's a kind of advice: man, you are really try to develop a bicycle.
According to developers guide you should use $resource
instead of $http
to interact with REST api. Especially with RESTful.
$resource
service will provide you functionality to make exactly what you want - to make requests like User.get()
and after this User.save()
, User.update()
, etc.
My suggest is to create a factory
for each entity(like "user", "account", etc), or for each workflow(like "sign up").
'use strict';
angular.module('app.factories.user', ['ngResource'])
.factory('UserFactory', function ($resource) {
var _userApi = {
user: $resource('/user', {}, {})
};
return {
getCurrentUser: function () {
return _userApi.user.get(function (data) {
// here we got data.user;
}, function (response) {
console.error(response.status);
}).$promise;
}
};
})
;
And use it in a controller:
.controller('UserPageCtrl', function ($scope, UserFactory) {
var currentUser;
$scope.getCurrentUser = function () {
return UserFactory.getCurrentUser().success(function (user) {
//here we got user
currentUser = user;
//now we able do stuf like
//currentUser.save()
//etc.
}).error(function (err) {
console.error(err);
});
};
});
So, don't mind if examples above little bit... you know... let's say if you don't like it ))
Just take a look at $resource
documentation (and this)
If you don't like resource, you can use third-party restanguar (or perhaps something else). But restangular is much more strict, so choose it only if you know for sure that your backend developers aren't idiots.
UPD1: About interceptors (I'll put here my working code for error intercepting, the idea of this code is to logout user when any request return 403 error
status):
You are able to add all facctory to global $httpProvider
(at module config section).
Like this:
app.config(function ($urlRouterProvider, $httpProvider) {
//Process backend errors
$httpProvider.interceptors.push('errorsFactory');
})
And here is errorsFactory
:
'use strict';
angular.module('app.factories.errors', [])
.factory('errorsFactory', function ($q) {
var HTTP_STATUS = {
FORBIDDEN: 403
};
return {
response: function (response) {
if (response.status === HTTP_STATUS.FORBIDDEN) {
//logout here
}
return response || $q.when(response);
},
responseError: function (rejection) {
if (rejection.status === HTTP_STATUS.FORBIDDEN) {
//logout here
}
return $q.reject(rejection);
}
};
})
;
回答2:
Use $injector
to get $http
instead of accessing it via angular.
.
Detailed Answer:
The problem is how you inject $http
. It seems that you get some $http
that is outside of your app (I can't exactly say why, though). var $http = angular.injector(['ng']).get('$http');
doesn't do what you want (or what you think it does).
You could do var $http = angular.element(document).injector().get('$http');
(or probably better use $document
there), but imo you should use the $injector
-service normally: you can just add $injector
to your dependencies/inject statement and inside your interceptor use that to retrieve the service you want like: var $http = $injector.get("$http");
With that your interceptor will intercept the $http
-calls it makes.
来源:https://stackoverflow.com/questions/36221715/recursively-intercepting-http