Как избежать ошибок $compile:tpload при ответе кода состояния 401

Мы разрабатываем одностраничное приложение с AngularJS и ASP.NET MVC Json Rest API.

Когда неаутентифицированный клиент пытается перейти к частному маршруту (например, / Foo / Home / Template), чтобы получить шаблон, он получает ответ 401 от Web API, и наше приложение AngularJS автоматически перенаправляет его на страницу входа.

Мы обрабатываем 401 с перехватчиком $ http примерно так:

if (response.status === 401) { 
        $location.path(routeToLogin);
        return $q.reject(response);
}

Ввод правильных учетных данных позволяет клиенту получить шаблон.

Все работает отлично, за исключением одной детали; консоль Javascript сообщает об этой ошибке:

Error: [$compile:tpload] http://errors.angularjs.org/1.3.0/$compile/tpload?p0=%Foo%2FHome%2FTemplate%2F

Документация AngularJs утверждает это:

Описание

Эта ошибка возникает, когда $ compile пытается извлечь шаблон с какого-либо URL-адреса, и запрос не выполняется.

В нашем приложении AngularJs запрос не выполняется, но он задуман, потому что ресурс есть, но к нему нельзя получить доступ (401).

Должен ли я пойти дальше и принять такую ​​ошибку на консоли, или есть возможность ее отключить или каким-то образом защитить?

РЕДАКТИРОВАТЬ:

Я немного отладил угловой источник и обнаружил, какая часть кода вызывает исключение.
Так как мы используем TemplateUrl чтобы объявить наши шаблоны, мы косвенно используем функцию compileTemplateUrl это делает этот вызов:

$templateRequest($sce.getTrustedResourceUrl(templateUrl))

это оставляет второй параметр (ignoreRequestError) из templateRequest не определено.

ignoreRequestError (необязательно) булево

Следует ли игнорировать исключение, когда запрос не выполняется или шаблон пуст

Когда наш http-перехватчик, обрабатывая код состояния 401, отклоняет обещание, $http.get внутри $TemplateRequestProvider не удается и вызывает эту функцию:

 function handleError() {
        self.totalPendingRequests--;
        if (!ignoreRequestError) {
          throw $compileMinErr('tpload', 'Failed to load template: {0}', tpl);
        }
        return $q.reject();
      }

Я считаю, что мы не можем ничего сделать, чтобы предотвратить ошибку на консоли, как TemplateUrl не позволяет установить ignoreRequestError флаг ложно.

Я пытался обойти отказ в случае кода состояния 401; это исправляет ошибку в консоли, но, к сожалению, у нее есть побочный эффект: пустой шаблон неправильно кэшируется в TemplateCache вызывая другие проблемы.

3 ответа

Решение

Подумав, что я вспомнил об украшении в Angular, он решил эту проблему отлично:

app.config(['$provide', function($provide) {
  $provide.decorator('$templateRequest', ['$delegate', function($delegate) {

    var fn = $delegate;

    $delegate = function(tpl) {

      for (var key in fn) {
        $delegate[key] = fn[key];
      }

      return fn.apply(this, [tpl, true]);
    };

    return $delegate;
  }]);
}]);

Вы должны быть в состоянии перехватить вызов шаблона по статусу и URL.

Plunker

app.config(function($httpProvider) {

  var interceptor = function($location, $log, $q) {

      function success(response) {
        // The response if complete
        $log.info(response);
        return response;
      }

      function error(response) {
        // The request if errors
        $log.error(response);
        return $q.reject(response);
      }

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

  $httpProvider.responseInterceptors.push(interceptor);

});

На мой взгляд, у вас есть два варианта:

Вариант А)

идти с перехватчиками. Однако, чтобы исключить компиляцию, вы должны вернуть код состояния успеха в сообщении об ошибке ответа (BAD) ИЛИ перенаправить на страницу входа в систему внутри перехватчика (хорошо):

app.factory('authInterceptorService', function () {

    var interceptor = {};

    interceptor.responseError = function (rejection) {

        if (rejection.status === 401 && rejection.config.url === "home template url") {

            //BAD IDEA
            //console.log("faking home template");
            //rejection.status = 200;
            //rejection.data = "<h1>should log in to the application first</h1>";

            //GOOD IDEA
            window.location = "/login.html";
        }

        return rejection;
    }


    return interceptor;
});

и в конфигурации приложения:

app.config(['$httpProvider', function ($httpProvider) {

   $httpProvider.interceptors.push('authInterceptorService');
}

Вариант б)

сделайте домашний шаблон общедоступным. В конце концов это должна быть только разметка HTML, без какой-либо толковой информации.

это решение чисто... и, возможно, также возможно.

Другие вопросы по тегам