如何在401状态代码响应中避免$ compile:tpload错误

时间:2014-11-07 22:32:16

标签: javascript angularjs single-page-application angularjs-routing

我们正在使用AngularJS和ASP.NET MVC Json Rest API开发单页应用程序。

当未经身份验证的客户端尝试导航到私有路由(例如: / Foo / Home / Template )以获取模板时,它会自动从Web API和我们的AngularJS应用程序获得401响应将其重定向到登录页面。

我们正在使用$http interceptor处理401,其中包含以下内容:

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 documentation声明:

  

描述

     

当$ compile尝试从某些模板获取模板时,会发生此错误   URL,请求失败。

在我们的AngularJs应用程序中,请求失败,但它是设计的,因为资源存在但无法访问(401)。

我应该继续在控制台上接受这种错误,还是可以以某种方式静音或屏蔽它?

修改

我稍微调试了角度源,我发现代码的哪一部分引发了异常 由于我们使用TemplateUrl来声明模板,因此我们间接使用调用此函数的函数compileTemplateUrl

$templateRequest($sce.getTrustedResourceUrl(templateUrl))

这使得ignoreRequestError的第二个参数(templateRequest)未定义。

  

ignoreRequestError(可选)boolean

     

请求失败时是否忽略异常   模板是空的

当我们的http拦截器处理401状态代码拒绝承诺时,$TemplateRequestProvider内的$ http.get失败并调用此函数:

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

我相信我们无法阻止控制台上的错误,因为TemplateUrl不允许将ignoreRequestError标记设置为false。

我已尝试绕过401状态码的拒绝;这修复了控制台上的错误,但遗憾的是它有副作用:空模板被错误地缓存到TemplateCache导致其他问题。

3 个答案:

答案 0 :(得分:13)

经过一番思考,我记得在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;
  }]);
}]);

答案 1 :(得分:0)

您应该能够通过状态和网址拦截模板的调用。

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

});

答案 2 :(得分:0)

在我看来,你有两个选择:

选项A)

跟拦截器一起去。但是,要消除编译,您需要在响应错误(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上:

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

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

选项b)

将主页模板设为公开。毕竟它应该只是html标记,没有任何明智的信息。

这个解决方案很干净......也许也是可能的。