最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to avoid $compile:tpload errors on 401 status code response - Stack Overflow

programmeradmin0浏览0评论

We are developing a Single Page Application with AngularJS and ASP.NET MVC Json Rest API.

When an unauthenticated client tries to navigate to a private route (Ex: /Foo/Home/Template) to get a template, it gets a 401 response from the Web API and our AngularJS app automatically redirects it to the login page.

We are handling the 401 with $http interceptor with something like this:

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

Entering the correct credentials allows the client to get the template.

Everything is working perfectly except for one detail; the Javascript console reports this error:

Error: [$compile:tpload] .3.0/$compile/tpload?p0=%Foo%2FHome%2FTemplate%2F

AngularJs documentation states this:

Description

This error occurs when $compile attempts to fetch a template from some URL, and the request fails.

In our AngularJs app the request fails but it is by design because the resource is there but it cannot be accessed (401).

Should I move on and accept this kind of error on console or is it possible to mute or shield it in some way?

EDIT:

I have debugged the angular source a little bit and I found what part of the code is raising the exception.
Since we are using TemplateUrl to declare our templates, we are indirectly using the function compileTemplateUrl that makes this call:

$templateRequest($sce.getTrustedResourceUrl(templateUrl))

this leaves the second parameter (ignoreRequestError) of templateRequest undefined.

ignoreRequestError(optional)boolean

Whether or not to ignore the exception when the request fails or the template is empty

When our http interceptor, handling the 401 status code, rejects the promise, the $http.get inside the $TemplateRequestProvider fails and calls this function:

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

I believe we can't do anything to prevent the error on console as TemplateUrl does not allow to set the ignoreRequestError flag to false.

I've tried to bypass the reject in case of 401 status code; this fixes the error on console but sadly it has a side effect: an empty template is wrongly cached into the TemplateCache causing othe problems.

We are developing a Single Page Application with AngularJS and ASP.NET MVC Json Rest API.

When an unauthenticated client tries to navigate to a private route (Ex: /Foo/Home/Template) to get a template, it gets a 401 response from the Web API and our AngularJS app automatically redirects it to the login page.

We are handling the 401 with $http interceptor with something like this:

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

Entering the correct credentials allows the client to get the template.

Everything is working perfectly except for one detail; the Javascript console reports this error:

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

AngularJs documentation states this:

Description

This error occurs when $compile attempts to fetch a template from some URL, and the request fails.

In our AngularJs app the request fails but it is by design because the resource is there but it cannot be accessed (401).

Should I move on and accept this kind of error on console or is it possible to mute or shield it in some way?

EDIT:

I have debugged the angular source a little bit and I found what part of the code is raising the exception.
Since we are using TemplateUrl to declare our templates, we are indirectly using the function compileTemplateUrl that makes this call:

$templateRequest($sce.getTrustedResourceUrl(templateUrl))

this leaves the second parameter (ignoreRequestError) of templateRequest undefined.

ignoreRequestError(optional)boolean

Whether or not to ignore the exception when the request fails or the template is empty

When our http interceptor, handling the 401 status code, rejects the promise, the $http.get inside the $TemplateRequestProvider fails and calls this function:

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

I believe we can't do anything to prevent the error on console as TemplateUrl does not allow to set the ignoreRequestError flag to false.

I've tried to bypass the reject in case of 401 status code; this fixes the error on console but sadly it has a side effect: an empty template is wrongly cached into the TemplateCache causing othe problems.

Share Improve this question edited Nov 13, 2014 at 8:14 systempuntoout asked Nov 7, 2014 at 22:32 systempuntooutsystempuntoout 74.1k47 gold badges175 silver badges246 bronze badges 13
  • Would modifying whatever is causing that template to load be an option? You could try to add the template to the $templateCache yourself and swallow the error accordingly. – Esteban Felix Commented Nov 9, 2014 at 22:43
  • It's the homepage template and it has to be loaded. – systempuntoout Commented Nov 10, 2014 at 0:12
  • Are you using ui-bootstrap ????? – vmontanheiro Commented Nov 10, 2014 at 0:43
  • @vcrzy yes, we are using angular ui-bootstrap. – systempuntoout Commented Nov 10, 2014 at 8:13
  • 1 Is downgrading angular an option? before it would have the ignoreErrors set to true, they changed it somewhere in 1.3 iirc. – PiniH Commented Nov 13, 2014 at 21:48
 |  Show 8 more comments

3 Answers 3

Reset to default 14 +150

After some thinking I remembered about decorating in Angular, it solved this problem perfectly:

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

You should be able to intercept the call for the template by status and 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);

});

As I see it, you have two options:

Option A)

go with the interceptors. However, to eliminate the compile you need to return success status code inside response error (BAD) OR redirect to the login page inside the interceptor (Good):

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

and on app config:

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

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

Option b)

make the home template public. After all it should be just html mark-up, without any sensible information.

this solution is clean...and perhaps is also possible.

发布评论

评论列表(0)

  1. 暂无评论