Я пытаюсь написать HTTP-перехватчик для моего приложения AngularJS для обработки аутентификации.
Этот код работает, но я беспокоюсь о том, чтобы вручную внедрить службу, поскольку я думал, что Angular должен обрабатывать это автоматически:
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(function ($location, $injector) {
return {
'request': function (config) {
//injected manually to get around circular dependency problem.
var AuthService = $injector.get('AuthService');
console.log(AuthService);
console.log('in request interceptor');
if (!AuthService.isAuthenticated() && $location.path != '/login') {
console.log('user is not logged in.');
$location.path('/login');
}
return config;
}
};
})
}]);
То, что я начал делать, но столкнулся с проблемами циклической зависимости:
app.config(function ($provide, $httpProvider) {
$provide.factory('HttpInterceptor', function ($q, $location, AuthService) {
return {
'request': function (config) {
console.log('in request interceptor.');
if (!AuthService.isAuthenticated() && $location.path != '/login') {
console.log('user is not logged in.');
$location.path('/login');
}
return config;
}
};
});
$httpProvider.interceptors.push('HttpInterceptor');
});
Еще одна причина, по которой я обеспокоен, заключается в том, что раздел о $ http в Angular Docs, кажется, показывает способ «обычным способом» вводить зависимости в перехватчик Http. См. Их фрагмент кода в разделе «Перехватчики»:
// register the interceptor as a service
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
return {
// optional method
'request': function(config) {
// do something on success
return config || $q.when(config);
},
// optional method
'requestError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
},
// optional method
'response': function(response) {
// do something on success
return response || $q.when(response);
},
// optional method
'responseError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise
}
return $q.reject(rejection);
};
}
});
$httpProvider.interceptors.push('myHttpInterceptor');
Куда должен идти приведенный выше код?
Думаю, мой вопрос в том, как правильно это сделать?
Спасибо, и я надеюсь, что мой вопрос был достаточно ясным.
javascript
angularjs
shaunlim
источник
источник
$http
. Я нашел единственный способ обойти это - использовать$injector.get
, но было бы здорово узнать, есть ли хороший способ структурировать код, чтобы этого избежать.Ответы:
У вас есть циклическая зависимость между $ http и вашим AuthService.
То, что вы делаете с помощью
$injector
службы, решает проблему курицы и яйца, откладывая зависимость $ http от AuthService.Я считаю, что то, что вы сделали, на самом деле является самым простым способом сделать это.
Вы также можете сделать это:
run()
блоке, а не вconfig()
блоке, это уже может помочь). Но можете ли вы гарантировать, что $ http еще не был вызван?AuthService.setHttp()
или что-то в этом роде.источник
run()
блоке, потому что вы не можете внедрить $ httpProvider в блок выполнения. Это можно сделать только на этапе настройки.Это то, чем я закончил
Примечание.
$injector.get
Вызовы должны выполняться в методах перехватчика. Если вы попытаетесь использовать их в другом месте, вы по-прежнему будете получать ошибку циклической зависимости в JS.источник
Я думаю, что использование инжектора $ напрямую - это антипаттерн.
Способ разорвать круговую зависимость - использовать событие: вместо добавления $ state введите $ rootScope. Вместо прямого перенаправления сделайте
плюс
источник
Плохая логика дала такие результаты
На самом деле нет смысла искать, создан ли пользователь в Http Interceptor или нет. Я бы рекомендовал обернуть все ваши HTTP-запросы в один .service (или .factory, или .provider) и использовать его для ВСЕХ запросов. Каждый раз, когда вы вызываете функцию, вы можете проверить, вошел ли пользователь в систему или нет. Если все в порядке, разрешите отправить запрос.
В вашем случае приложение Angular в любом случае отправит запрос, вы просто проверяете там авторизацию, а после этого JavaScript отправит запрос.
Суть вашей проблемы
myHttpInterceptor
вызывается под$httpProvider
экземпляром. ВашеAuthService
использование$http
, или$resource
, и здесь у вас есть рекурсия зависимостей или циклическая зависимость. Если вы удалите эту зависимость изAuthService
, то вы не увидите эту ошибку.Также, как указал @Pieter Herroelen, вы можете поместить этот перехватчик в свой модуль
module.run
, но это будет больше похоже на взлом, а не на решение.Если вы хотите создать чистый и информативный код, вы должны придерживаться некоторых принципов SOLID.
Как минимум принцип единственной ответственности вам очень поможет в таких ситуациях.
источник
Если вы просто проверяете состояние Auth (isAuthorized ()), я бы рекомендовал поместить это состояние в отдельный модуль, скажем «Auth», который просто хранит состояние и не использует сам $ http.
Модуль аутентификации:
(я использовал localStorage для хранения здесь sessionId на стороне клиента, но вы также можете установить это внутри своего AuthService, например, после вызова $ http)
источник