Próbuję napisać przechwytywacz HTTP dla mojej aplikacji AngularJS do obsługi uwierzytelniania.
Ten kod działa, ale obawiam się ręcznego wstrzykiwania usługi, ponieważ pomyślałem, że Angular powinien obsługiwać to automatycznie:
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;
}
};
})
}]);
Co zacząłem robić, ale napotkałem problemy z zależnością cykliczną:
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');
});
Innym powodem, dla którego jestem zaniepokojony, jest to, że sekcja dotycząca $ http w Angular Docs wydaje się pokazywać sposób na wprowadzenie zależności w „zwykły sposób” do przechwytywacza HTTP. Zobacz ich fragment kodu w sekcji „Interceptory”:
// 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');
Gdzie powinien iść powyższy kod?
Myślę, że moje pytanie brzmi: jaki jest właściwy sposób, aby to zrobić?
Dziękuję i mam nadzieję, że moje pytanie było wystarczająco jasne.
javascript
angularjs
shaunlim
źródło
źródło
$http
. Jedynym sposobem obejścia tego problemu jest użycie$injector.get
, ale byłoby wspaniale wiedzieć, czy istnieje dobry sposób na uporządkowanie kodu, aby tego uniknąć.Odpowiedzi:
Masz cykliczną zależność między $ http a usługą AuthService.
To, co robisz, korzystając z
$injector
usługi, polega na rozwiązaniu problemu z kura i jajkiem przez opóźnienie zależności $ http od usługi AuthService.Uważam, że to, co zrobiłeś, jest właściwie najprostszym sposobem na zrobienie tego.
Możesz to również zrobić poprzez:
run()
bloku zamiast wconfig()
bloku może już załatwić sprawę). Ale czy możesz zagwarantować, że $ http nie zostało jeszcze wywołane?AuthService.setHttp()
lub coś w tym stylu.źródło
run()
bloku, ponieważ nie możesz wstrzyknąć $ httpProvider do bloku uruchamiania. Możesz to zrobić tylko w fazie konfiguracji.Oto, co ostatecznie zrobiłem
Uwaga:
$injector.get
wywołania powinny znajdować się w metodach przechwytywacza, jeśli spróbujesz ich użyć w innym miejscu, nadal będziesz otrzymywać cykliczny błąd zależności w JS.źródło
Myślę, że użycie wtryskiwacza $ bezpośrednio jest przeciwieństwem.
Sposobem na przerwanie zależności cyklicznej jest użycie zdarzenia: zamiast wstrzykiwać $ state, wstrzyknij $ rootScope. Zamiast przekierowywać bezpośrednio, zrób
plus
źródło
Zła logika doprowadziła do takich wyników
Właściwie nie ma sensu szukać, czy autorstwo użytkownika jest czy nie w Http Interceptor. Zalecałbym zawinięcie wszystkich żądań HTTP w pojedynczy plik .service (lub .factory lub .provider) i użycie go dla WSZYSTKICH żądań. Za każdym razem, gdy wywołujesz funkcję, możesz sprawdzić, czy użytkownik jest zalogowany, czy nie. Jeśli wszystko jest w porządku, zezwól na wysyłanie żądania.
W Twoim przypadku aplikacja Angular wyśle żądanie w każdym przypadku, po prostu sprawdzasz tam autoryzację, a następnie JavaScript wyśle żądanie.
Sedno twojego problemu
myHttpInterceptor
jest wywoływana pod$httpProvider
instancją. TwojeAuthService
zastosowania$http
lub$resource
, a tutaj masz rekursję zależności lub zależność cykliczną. Jeśli usuniesz tę zależność zAuthService
, nie zobaczysz tego błędu.Jak zauważył @Pieter Herroelen, możesz umieścić ten przechwytywacz w swoim module
module.run
, ale będzie to bardziej jak włamanie, a nie rozwiązanie.Jeśli jesteś w stanie zrobić czysty i samoopisowy kod, musisz przestrzegać niektórych zasad SOLID.
Przynajmniej zasada pojedynczej odpowiedzialności bardzo Ci w takich sytuacjach pomoże.
źródło
Jeśli sprawdzasz tylko stan Auth (isAuthorized ()), zalecałbym umieszczenie tego stanu w osobnym module, powiedz „Auth”, który po prostu przechowuje stan i nie używa samego $ http.
Moduł autoryzacji:
(Użyłem localStorage do przechowywania sessionId po stronie klienta tutaj, ale możesz również ustawić to w swoim AuthService na przykład po wywołaniu $ http)
źródło