Memasukkan $ state (ui-router) ke interceptor $ http menyebabkan ketergantungan melingkar


120

Apa yang saya coba capai

Saya ingin beralih ke keadaan tertentu (masuk) jika permintaan $ http mengembalikan kesalahan 401. Karena itu saya telah membuat pencegat $ http.

Masalah

Ketika saya mencoba untuk memasukkan '$ state' ke interseptor saya mendapatkan ketergantungan melingkar. Mengapa dan bagaimana cara memperbaikinya?

Kode

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

Jawaban:


213

Perbaiki

Gunakan $injectorlayanan untuk mendapatkan referensi ke $statelayanan tersebut.

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

Penyebab

angular-ui-router menyuntikkan $httplayanan sebagai dependensi $TemplateFactoryyang kemudian membuat referensi melingkar ke $httpdalam $httpProviderdirinya sendiri saat mengirimkan pencegat.

Pengecualian dependensi melingkar yang sama akan diterapkan jika Anda mencoba untuk memasukkan $httplayanan secara langsung ke interseptor seperti itu.

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

Pemisahan Masalah

Pengecualian dependensi melingkar dapat menunjukkan bahwa ada campuran masalah dalam aplikasi Anda yang dapat menyebabkan masalah stabilitas. Jika Anda menemukan diri Anda dengan pengecualian ini, Anda harus meluangkan waktu untuk melihat arsitektur Anda untuk memastikan Anda menghindari dependensi yang akhirnya mereferensikan dirinya sendiri.

@ Jawaban Stephen Friedrich

Saya setuju dengan jawaban di bawah ini bahwa menggunakan $injectoruntuk langsung mendapatkan referensi ke layanan yang diinginkan tidak ideal dan dapat dianggap sebagai anti pola.

Memancarkan acara adalah solusi yang jauh lebih elegan dan juga terpisah.


1
Bagaimana ini akan disesuaikan untuk Angular 1.3, di mana ada 4 fungsi berbeda yang diteruskan ke interseptor?
Maciej Gurban

3
Penggunaannya akan sama, Anda menyuntikkan $injectorlayanan ke interseptor Anda dan menelepon di $injector.get()mana Anda perlu mendapatkan $statelayanan tersebut.
Jonathan Palumbo

Saya mendapatkan $ injectot.get ("$ state") sebagai << tidak ditentukan >>, dapatkah Anda memberi tahu apa masalahnya?
sandeep kale

Ini pasti penyelamat ketika ini situasi sederhana, tetapi ketika Anda mengalami ini, itu adalah tanda bahwa Anda memang telah membuat ketergantungan melingkar di suatu tempat dalam kode Anda, yang mudah dilakukan di AngularJS dengan DI-nya. Misko Hevery menjelaskannya dengan sangat baik di blognya ; sangat menyarankan Anda membacanya untuk meningkatkan kode Anda.
JD Smith

2
$httpProvider.responseInterceptors.pushtidak berfungsi lagi jika Anda menggunakan versi Angular yang lebih baru. Gunakan $httpProvider.interceptors.push()sebagai gantinya. Anda harus memodifikasi interceptorjuga. Bagaimanapun, terima kasih atas jawaban yang luar biasa! :)
shyam

25

Pertanyaannya adalah duplikat dari AngularJS: Layanan injeksi ke interseptor HTTP (ketergantungan melingkar)

Saya memposting ulang jawaban saya dari utas itu di sini:

Perbaikan yang Lebih Baik

Saya pikir menggunakan $ injektor secara langsung adalah antipattern.

Cara untuk memutus dependensi melingkar adalah dengan menggunakan peristiwa: Daripada memasukkan $ state, injeksikan $ rootScope. Alih-alih mengarahkan langsung, lakukan

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

plus

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

Dengan cara itu Anda telah memisahkan kekhawatiran:

  1. Deteksi respons 401
  2. Alihkan untuk masuk

2
Sebenarnya, pertanyaan itu adalah duplikat dari pertanyaan ini, karena pertanyaan ini telah ditanyakan sebelumnya. Cintai perbaikan elegan Anda, jadi dapatkan suara positif. ;-)
Aaron Grey

1
Saya bingung di mana harus meletakkan ini. $ RootScope. $ Emit ("unauthorized");
alex

16

Solusi Jonathan sangat bagus sampai saya mencoba menyelamatkan keadaan saat ini. Di ui-router v0.2.10 , status saat ini tampaknya tidak diisi pada pemuatan halaman awal di interseptor.

Bagaimanapun, saya menyelesaikannya dengan menggunakan acara $ stateChangeError sebagai gantinya. Peristiwa $ stateChangeError memberi Anda status ke dan dari , serta kesalahan. Cukup bagus.

$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");
        }
    });

Saya telah mengirimkan masalah tentang ini.
Justin Wrobel

Saya rasa ini tidak mungkin dengan ngResource karena selalu asynchron? Saya mencoba beberapa hal tetapi saya tidak mendapatkan panggilan sumber daya untuk mencegah perubahan status ...
Bastian

4
Bagaimana jika Anda ingin menghalangi permintaan yang tidak memicu perubahan status?
Shikloshi

Anda dapat menggunakan pendekatan yang sama seperti yang dilakukan @Stephen Friedrich di atas, yang sebenarnya mirip dengan yang ini tetapi menggunakan peristiwa khusus.
saiyancoder
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.