Việc tiêm $ state (ui-router) vào bộ chặn $ http gây ra sự phụ thuộc vòng tròn


120

Điều tôi đang cố gắng đạt được

Tôi muốn chuyển sang một trạng thái nhất định (đăng nhập) trong trường hợp yêu cầu $ http trả về lỗi 401. Do đó, tôi đã tạo một trình chặn $ http.

Vấn đề

Khi tôi cố gắng chèn '$ state' vào bộ đánh chặn, tôi nhận được sự phụ thuộc vòng tròn. Tại sao và làm thế nào để tôi sửa chữa nó?

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

Câu trả lời:


213

Khắc phục

Sử dụng $injectordịch vụ để tham khảo $statedịch vụ.

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

Nguyên nhân

angle-ui-router đưa $httpdịch vụ vào như một phần phụ thuộc vào $TemplateFactoryđó, sau đó tạo ra một tham chiếu $httpvòng trong $httpProviderchính nó khi điều động bộ đánh chặn.

Ngoại lệ phụ thuộc vòng tương tự sẽ được ném ra nếu bạn cố gắng đưa $httpdịch vụ trực tiếp vào một bộ đánh chặn như vậy.

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

Tách các mối quan tâm

Các ngoại lệ phụ thuộc vòng tròn có thể chỉ ra rằng có sự pha trộn các mối quan tâm trong ứng dụng của bạn có thể gây ra các vấn đề về tính ổn định. Nếu bạn thấy mình có ngoại lệ này, bạn nên dành thời gian để xem xét kiến ​​trúc của mình để đảm bảo bạn tránh bất kỳ sự phụ thuộc nào mà cuối cùng lại tham chiếu đến chính chúng.

Câu trả lời của @Stephen Friedrich

Tôi đồng ý với câu trả lời bên dưới rằng việc sử dụng $injectorđể nhận trực tiếp tham chiếu đến dịch vụ mong muốn là không lý tưởng và có thể được coi là một hình thức phản đối.

Phát một sự kiện là một giải pháp tách biệt và thanh lịch hơn nhiều.


1
Điều này sẽ được điều chỉnh như thế nào cho Angular 1.3, nơi có 4 chức năng khác nhau được chuyển vào các bộ đánh chặn?
Maciej Gurban

3
Việc sử dụng sẽ giống nhau, bạn đưa $injectordịch vụ vào thiết bị chặn của mình và gọi đến $injector.get()nơi bạn cần để nhận $statedịch vụ.
Jonathan Palumbo

Tôi nhận được $ injectionot.get ("$ state") là << không được xác định >>, bạn có thể vui lòng cho biết vấn đề có thể là gì không?
sandeep kale,

Đây chắc chắn là một cứu cánh khi đó là một tình huống đơn giản, nhưng khi bạn gặp phải điều này, đó là một dấu hiệu cho thấy bạn đã thực sự tạo ra một sự phụ thuộc vòng tròn ở đâu đó trong mã của mình, điều này rất dễ thực hiện trong AngularJS với DI của nó. Misko Hevery giải thích nó rất tốt trong blog của mình ; rất khuyên bạn nên đọc nó để cải thiện mã của bạn.
JD Smith

2
$httpProvider.responseInterceptors.pushkhông hoạt động nữa nếu bạn đang sử dụng phiên bản Angular mới hơn. Sử dụng $httpProvider.interceptors.push()thay thế. Bạn cũng sẽ phải sửa đổi interceptor. Dù sao, cảm ơn vì câu trả lời tuyệt vời! :)
shyam

25

Câu hỏi là một bản sao của AngularJS: Đưa dịch vụ vào bộ đánh chặn HTTP (Phụ thuộc vòng)

Tôi đăng lại câu trả lời của mình từ chủ đề đó ở đây:

Một bản sửa lỗi tốt hơn

Tôi nghĩ rằng sử dụng trực tiếp bộ phun $ là một phản vật chất.

Một cách để phá vỡ sự phụ thuộc vòng tròn là sử dụng một sự kiện: Thay vì tiêm $ state, hãy tiêm $ rootScope. Thay vì chuyển hướng trực tiếp, hãy làm

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

thêm

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

Bằng cách đó, bạn đã tách các mối quan tâm:

  1. Phát hiện phản hồi 401
  2. Chuyển hướng để đăng nhập

2
Trên thực tế, câu hỏi đó là một bản sao của câu hỏi này, bởi vì câu hỏi này đã được hỏi trước đó. Mặc dù vậy, yêu thích bản sửa chữa thanh lịch của bạn, vì vậy hãy có một sự ủng hộ. ;-)
Aaron Grey

1
Tôi đang bối rối không biết đặt cái này ở đâu. $ RootScope. $ Release ("trái phép");
alex

16

Giải pháp của Jonathan rất tuyệt vời cho đến khi tôi cố gắng cứu trạng thái hiện tại. Trong ui-router v0.2.10 , trạng thái hiện tại dường như không được điền khi tải trang ban đầu trong bộ chặn.

Dù sao, tôi đã giải quyết nó bằng cách sử dụng sự kiện $ stateChangeError để thay thế. Sự kiện $ stateChangeError cung cấp cho bạn cả trạng thái đếnđi , cũng như lỗi. Nó khá tiện lợi.

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

Tôi đã gửi một vấn đề về điều này.
Justin Wrobel

Tôi nghĩ rằng điều này là không thể với ngResource vì nó luôn luôn không đồng bộ? Tôi đã thử một số công cụ nhưng tôi không nhận được lệnh gọi tài nguyên để ngăn thay đổi trạng thái ...
Bastian

4
Điều gì sẽ xảy ra nếu bạn muốn chặn một yêu cầu không kích hoạt thay đổi trạng thái?
Shikloshi

Bạn có thể sử dụng cách tiếp cận tương tự như @Stephen Friedrich đã làm ở trên, cách tiếp cận thực sự giống với cách này nhưng sử dụng sự kiện tùy chỉnh.
saiyancoder
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.