example common javascript angularjs

javascript - common - AngularJS: servicio de inyección en un interceptor HTTP(dependencia circular)



httpclient angular 5 example (5)

Creo que usar $ inyector directamente es un antipatrón.

Una forma de romper la dependencia circular es usar un evento: en lugar de inyectar $ state, inyecte $ rootScope. En lugar de redireccionar directamente, haga

this.$rootScope.$emit("unauthorized");

más

angular .module(''foo'') .run(function($rootScope, $state) { $rootScope.$on(''unauthorized'', () => { $state.transitionTo(''login''); }); });

Intento escribir un interceptor HTTP para mi aplicación AngularJS para manejar la autenticación.

Este código funciona, pero me preocupa inyectar manualmente un servicio ya que pensé que se suponía que Angular manejaba esto automáticamente:

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; } }; }) }]);

Lo que empecé haciendo, pero encontré problemas circulares de dependencia:

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''); });

Otra razón por la que estoy preocupado es porque la sección en $ http en Angular Docs parece mostrar una manera de hacer que las dependencias se inyecten de la "manera habitual" en un interceptor Http. Ver su fragmento de código en "Interceptores":

// 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'');

¿A dónde debería ir el código anterior?

Supongo que mi pregunta es ¿cuál es la forma correcta de hacer esto?

Gracias, y espero que mi pregunta sea lo suficientemente clara.


Esto es lo que terminé haciendo

.config([''$httpProvider'', function ($httpProvider) { //enable cors $httpProvider.defaults.useXDomain = true; $httpProvider.interceptors.push([''$location'', ''$injector'', ''$q'', function ($location, $injector, $q) { return { ''request'': function (config) { //injected manually to get around circular dependency problem. var AuthService = $injector.get(''Auth''); if (!AuthService.isAuthenticated()) { $location.path(''/login''); } else { //add session_id as a bearer token in header of all outgoing HTTP requests. var currentUser = AuthService.getCurrentUser(); if (currentUser !== null) { var sessionId = AuthService.getCurrentUser().sessionId; if (sessionId) { config.headers.Authorization = ''Bearer '' + sessionId; } } } //add headers return config; }, ''responseError'': function (rejection) { if (rejection.status === 401) { //injected manually to get around circular dependency problem. var AuthService = $injector.get(''Auth''); //if server returns 401 despite user being authenticated on app side, it means session timed out on server if (AuthService.isAuthenticated()) { AuthService.appLogOut(); } $location.path(''/login''); return $q.reject(rejection); } } }; }]); }]);

Nota: Las llamadas de $injector.get deben estar dentro de los métodos del interceptor. Si intenta utilizarlas en otro lugar, continuará obteniendo un error de dependencia circular en JS.


Si solo está buscando el estado Auth (isAuthorized ()), le recomendaría poner ese estado en un módulo aparte, diga "Auth", que solo contiene el estado y no usa $ http.

app.config([''$httpProvider'', function ($httpProvider) { $httpProvider.interceptors.push(function ($location, Auth) { return { ''request'': function (config) { if (!Auth.isAuthenticated() && $location.path != ''/login'') { console.log(''user is not logged in.''); $location.path(''/login''); } return config; } } }) }])

Módulo Auth:

angular .module(''app'') .factory(''Auth'', Auth) function Auth() { var $scope = {} $scope.sessionId = localStorage.getItem(''sessionId'') $scope.authorized = $scope.sessionId !== null //... other auth relevant data $scope.isAuthorized = function() { return $scope.authorized } return $scope }

(utilicé localStorage para almacenar el sessionId en el lado del cliente aquí, pero también puedes configurarlo dentro de tu AuthService después de una llamada de $ http, por ejemplo)


Tiene una dependencia circular entre $ http y su AuthService.

Lo que está haciendo al usar el servicio $injector es resolver el problema del huevo y la gallina al retrasar la dependencia de $ http en el servicio AuthService.

Creo que lo que hiciste es en realidad la forma más sencilla de hacerlo.

También puedes hacer esto por:

  • Registrar el interceptor más tarde (hacerlo en un bloque run() lugar de en un bloque config() ya puede ser el truco). ¿Pero puede garantizar que no se haya llamado $ http ya?
  • "Inyectar" $ http manualmente en el AuthService cuando está registrando el interceptor llamando a AuthService.setHttp() o algo así.
  • ...

La mala lógica hizo tales resultados

En realidad, no tiene sentido buscar es escrito por el usuario o no en Http Interceptor. Yo recomendaría envolver todas sus solicitudes HTTP en .service (o .factory, o en .provider), y usarlo para TODAS las solicitudes. Cada vez que llame a la función, puede verificar si el usuario inició sesión o no. Si todo está bien, permite enviar solicitud.

En su caso, la aplicación Angular enviará una solicitud en cualquier caso, solo debe verificar la autorización allí, y luego de eso, JavaScript enviará una solicitud.

El núcleo de tu problema

myHttpInterceptor se llama en la instancia $httpProvider . Su AuthService usa $http , o $resource , y aquí tiene recursividad de dependencia, o dependencia circular. Si eliminas esa dependencia de AuthService , entonces no verás ese error.

También como señaló @Pieter Herroelen, podrías colocar este interceptor en tu módulo module.run , pero esto será más como un hack, no como una solución.

Si desea hacer un código limpio y autodescriptivo, debe ir con algunos de los principios SÓLIDOS.

Al menos el principio de Responsabilidad Individual le ayudará mucho en tales situaciones.