การใส่ $ state (ui-router) ลงใน $ http interceptor ทำให้เกิดการพึ่งพาแบบวงกลม


120

สิ่งที่ฉันพยายามบรรลุ

ฉันต้องการเปลี่ยนเป็นสถานะบางสถานะ (เข้าสู่ระบบ) ในกรณีที่คำขอ $ http ส่งกลับข้อผิดพลาด 401 ฉันจึงสร้างตัวดักจับ $ http

ปัญหา

เมื่อฉันพยายามแทรก '$ state' ลงใน interceptor ฉันจะได้รับการพึ่งพาแบบวงกลม ทำไมและฉันจะแก้ไขได้อย่างไร?

รหัส

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

คำตอบ:


213

การแก้ไข

ใช้$injectorบริการเพื่อรับข้อมูลอ้างอิงถึง$stateบริการ

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

สาเหตุ

angular-ui-routerจะฉีด$httpบริการเป็นการพึ่งพา$TemplateFactoryซึ่งจะสร้างการอ้างอิงแบบวงกลม$httpภายใน$httpProviderตัวมันเองเมื่อส่งตัวสกัดกั้น

ข้อยกเว้นการพึ่งพาแบบวงกลมเดียวกันจะถูกโยนทิ้งหากคุณพยายามที่จะฉีด$httpบริการเข้าไปในตัวสกัดกั้นโดยตรงเช่นนั้น

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

การแยกความกังวล

ข้อยกเว้นการพึ่งพาแบบวงกลมสามารถบ่งชี้ว่ามีความกังวลหลายอย่างภายในแอปพลิเคชันของคุณซึ่งอาจทำให้เกิดปัญหาด้านความเสถียร หากคุณพบว่าตัวเองมีข้อยกเว้นนี้คุณควรใช้เวลาดูสถาปัตยกรรมของคุณเพื่อให้แน่ใจว่าคุณจะหลีกเลี่ยงการอ้างอิงใด ๆ ที่อ้างอิงตัวเอง

คำตอบของ @Stephen Friedrich

ฉันเห็นด้วยกับคำตอบด้านล่างว่าการใช้$injectorเพื่อรับการอ้างอิงถึงบริการที่ต้องการโดยตรงนั้นไม่เหมาะและอาจถือเป็นการต่อต้านรูปแบบ

การเปล่งเหตุการณ์เป็นวิธีการแก้ปัญหาที่หรูหราและแยกออกจากกันมาก


1
สิ่งนี้จะถูกปรับอย่างไรสำหรับ Angular 1.3 โดยมี 4 ฟังก์ชั่นที่แตกต่างกันส่งผ่านไปยังตัวสกัดกั้น
Maciej Gurban

3
การใช้งานจะเหมือนกันคุณฉีด$injectorบริการเข้าไปในเครื่องสกัดกั้นของคุณและโทรไป$injector.get()ที่ที่คุณต้องการรับ$stateบริการ
Jonathan Palumbo

ฉันได้รับ $ injectot.get ("$ state") เป็น << ไม่ได้กำหนด >> คุณช่วยบอกได้ไหมว่ามีปัญหาอะไร
sandeep kale

นี่เป็นเครื่องช่วยชีวิตอย่างแน่นอนเมื่อมันเป็นสถานการณ์ง่ายๆ แต่เมื่อคุณพบปัญหานี้มันเป็นสัญญาณว่าคุณได้สร้างการพึ่งพาแบบวงกลมที่ใดที่หนึ่งในโค้ดของคุณซึ่งทำได้ง่ายใน AngularJS ด้วย DI Misko Hevery อธิบายได้เป็นอย่างดีในบล็อกของเขา ; ขอแนะนำให้คุณอ่านเพื่อปรับปรุงโค้ดของคุณ
JD Smith

2
$httpProvider.responseInterceptors.pushไม่ทำงานอีกต่อไปหากคุณใช้ Angular เวอร์ชันที่ใหม่กว่า ใช้$httpProvider.interceptors.push()แทน คุณจะต้องแก้ไขinterceptorเช่นกัน อย่างไรก็ตามขอบคุณสำหรับคำตอบที่ยอดเยี่ยม! :)
shyam

25

คำถามซ้ำกับAngularJS: การแทรกบริการลงในตัวสกัดกั้น HTTP (การพึ่งพาแบบวงกลม)

ฉันโพสต์คำตอบอีกครั้งจากหัวข้อนั้นที่นี่:

การแก้ไขที่ดีขึ้น

ฉันคิดว่าการใช้ $ injector โดยตรงคือ antipattern

วิธีทำลายการพึ่งพาแบบวงกลมคือการใช้เหตุการณ์: แทนที่จะฉีด $ state ให้ฉีด $ rootScope แทนที่จะเปลี่ยนเส้นทางโดยตรงให้ทำ

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

บวก

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

ด้วยวิธีนี้คุณได้แยกความกังวล:

  1. ตรวจจับการตอบสนอง 401
  2. เปลี่ยนเส้นทางไปยังล็อกอิน

2
อันที่จริงคำถามนั้นซ้ำกับคำถามนี้เนื่องจากคำถามนี้ถูกถามก่อนหน้านั้น รักการแก้ไขที่สง่างามของคุณดังนั้นจงโหวตให้คะแนน ;-)
Aaron Grey

1
ฉันสับสนว่าจะวางไว้ที่ไหน $ rootScope. $ emit ("ไม่ได้รับอนุญาต");
alex

16

วิธีแก้ปัญหาของโจนาธานนั้นยอดเยี่ยมมากจนกระทั่งฉันพยายามกอบกู้สถานะปัจจุบัน ในui-router v0.2.10สถานะปัจจุบันดูเหมือนจะไม่ถูกเติมในการโหลดหน้าเริ่มต้นใน interceptor

อย่างไรก็ตามฉันแก้ไขโดยใช้เหตุการณ์$ stateChangeErrorแทน $ stateChangeErrorเหตุการณ์จะช่วยให้คุณทั้งสองไปและจากรัฐเช่นเดียวกับข้อผิดพลาด มันค่อนข้างดี

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

ฉันได้ส่งปัญหาเกี่ยวกับเรื่องนี้แล้ว
Justin Wrobel

ฉันคิดว่านี่เป็นไปไม่ได้กับ ngResource เนื่องจากเป็นแบบอะซิงโครไนซ์เสมอ? ฉันลองทำบางอย่าง แต่ไม่ได้รับการเรียกทรัพยากรเพื่อป้องกันการเปลี่ยนแปลงสถานะ ...
Bastian

4
จะเกิดอะไรขึ้นหากคุณต้องการสกัดกั้นคำขอที่ไม่ทำให้เกิดการเปลี่ยนแปลงสถานะ
Shikloshi

คุณสามารถใช้แนวทางเดียวกับที่ @Stephen Friedrich ทำข้างต้นซึ่งคล้ายกับเหตุการณ์นี้ แต่ใช้เหตุการณ์ที่กำหนดเอง
saiyancoder
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.