$ State (ui-router) 'ı $ http interceptor'a enjekte etmek döngüsel bağımlılığa neden olur


120

Ne elde etmeye çalışıyorum

Bir $ http isteğinin 401 hatası döndürmesi durumunda belirli bir duruma (oturum açma) geçiş yapmak istiyorum. Bu nedenle bir $ http engelleyici oluşturdum.

Sorun

Önleyiciye '$ state' eklemeye çalıştığımda döngüsel bir bağımlılık elde ediyorum. Neden ve nasıl düzeltirim?

kod

//Inside Config function

    var interceptor = ['$location', '$q', '$state', function($location, $q, $state) {
        function success(response) {
            return response;
        }

        function error(response) {

            if(response.status === 401) {
                $state.transitionTo('public.login');
                return $q.reject(response);
            }
            else {
                return $q.reject(response);
            }
        }

        return function(promise) {
            return promise.then(success, error);
        }
    }];

    $httpProvider.responseInterceptors.push(interceptor);

Yanıtlar:


213

Çözüm

Kullan $injectorbir başvuru almak için hizmet $statehizmet.

var interceptor = ['$location', '$q', '$injector', function($location, $q, $injector) {
    function success(response) {
        return response;
    }

    function error(response) {

        if(response.status === 401) {
            $injector.get('$state').transitionTo('public.login');
            return $q.reject(response);
        }
        else {
            return $q.reject(response);
        }
    }

    return function(promise) {
        return promise.then(success, error);
    }
}];

$httpProvider.responseInterceptors.push(interceptor);

Neden

açısal-ui-yönlendirici , $httphizmeti bir bağımlılık olarak enjekte eder ve $TemplateFactorydaha sonra önleyiciyi gönderdikten sonra kendi $httpiçinde dairesel bir referans oluşturur $httpProvider.

$httpHizmeti doğrudan böyle bir engelleyiciye enjekte etmeye çalışırsanız, aynı döngüsel bağımlılık istisnası atılır .

var interceptor = ['$location', '$q', '$http', function($location, $q, $http) {

Kaygıların Ayrılması

Döngüsel bağımlılık istisnaları, uygulamanızda kararlılık sorunlarına neden olabilecek çeşitli endişeler olduğunu gösterebilir. Kendinizi bu istisnayla bulursanız, kendilerine atıfta bulunan herhangi bir bağımlılıktan kaçınmak için mimarinize bakmak için zaman ayırmalısınız.

@Stephen Friedrich'in cevabı

$injectorİstenilen hizmete doğrudan bir referans almak için öğesini kullanmanın ideal olmadığı ve bir anti model olarak kabul edilebileceği konusunda aşağıdaki yanıta katılıyorum .

Bir olayı yaymak çok daha zarif ve aynı zamanda bağımsız bir çözümdür.


1
Önleyicilere 4 farklı işlevin geçtiği Angular 1.3 için bu nasıl ayarlanır?
Maciej Gurban

3
Kullanım aynı olacaktır, $injectorhizmeti önleyicinize enjekte $injector.get()edersiniz ve $statehizmeti almanız gereken yeri ararsınız .
Jonathan Palumbo

$ İnjectot.get ("$ state") 'i << tanımlanmadı >> olarak alıyorum, lütfen sorunun ne olabileceğini söyleyebilir misiniz?
sandeep kale

Basit bir durum olduğunda bu kesinlikle bir cankurtarandır, ancak bununla karşılaştığınızda bu, kodunuzun bir yerinde gerçekten döngüsel bir bağımlılık yarattığınızın bir işaretidir, bu, DI'sı ile AngularJS'de yapılması kolaydır. Misko Hevery bunu blogunda çok iyi açıklıyor ; kodunuzu geliştirmek için okumanızı şiddetle tavsiye ederiz.
JD Smith

2
$httpProvider.responseInterceptors.pushAngular'ın sonraki sürümlerini kullanıyorsanız artık çalışmaz. $httpProvider.interceptors.push()Bunun yerine kullanın . Siz de değiştirmeniz gerekir interceptor. Her neyse, harika cevap için teşekkürler! :)
shyam

25

Soru, AngularJS'nin bir kopyasıdır : Hizmeti bir HTTP engelleyicisine enjekte etme (Dairesel bağımlılık)

Cevabımı bu başlıktan burada yeniden gönderiyorum:

Daha İyi Bir Düzeltme

Doğrudan $ enjektörü kullanmanın bir anti-model olduğunu düşünüyorum.

Döngüsel bağımlılığı kırmanın bir yolu bir olay kullanmaktır: $ state enjekte etmek yerine $ rootScope enjekte edin. Doğrudan yeniden yönlendirmek yerine

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

artı

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

Bu şekilde endişeleri ayırmış olursunuz:

  1. 401 yanıtını algıla
  2. Girişe yönlendir

2
Aslında, bu soru bu sorunun bir kopyası çünkü bu soru ondan önce sorulmuştu. Yine de zarif düzeltmenizi sevin, bu yüzden bir olumlu oy alın. ;-)
Aaron Grey

1
Bunu nereye koyacağım konusunda kafa karıştırıyorum. $ RootScope. $ Emit ("yetkisiz");
alex

16

Ben mevcut durumu kurtarmaya çalışana kadar Jonathan'ın çözümü harikaydı. In ui-yönlendirici v0.2.10 mevcut durumu yakalayıcısı ilk sayfa yüklemesinde doldurulması görünmüyor.

Neyse, onun yerine $ stateChangeError olayını kullanarak çözdüm . $ StateChangeError olay hem size verir etmek ve gelen devletler, hem de hata. Oldukça şık.

$rootScope.$on('$stateChangeError',
    function(event, toState, toParams, fromState, fromParams, error){
        console.log('stateChangeError');
        console.log(toState, toParams, fromState, fromParams, error);

        if(error.status == 401){
            console.log("401 detected. Redirecting...");

            authService.deniedState = toState.name;
            $state.go("login");
        }
    });

Bununla ilgili bir sorun gönderdim .
Justin Wrobel

Her zaman eşzamansız olduğu için bunun ngResource ile mümkün olmadığını düşünüyorum. Bazı şeyleri denedim ama bir durum değişikliğini önlemek için kaynak çağrısı alamıyorum ...
Bastian

4
Durum değişikliğini tetiklemeyen bir isteği durdurmak isterseniz ne olur?
Shikloshi

@Stephen Friedrich'in yukarıda yaptığı gibi aynı yaklaşımı kullanabilirsiniz, bu aslında buna benziyor ancak özel bir olay kullanıyor.
saiyancoder
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.