zoukankan      html  css  js  c++  java
  • angular 拦截器

    介绍:$http service在Angular中用于简化与后台的交互过程,其本质上使用XMLHttpRequestJSONP进行与后台的数据交互。在与后台的交互过程中,可能会对每条请求发送到Server之前进行预处理(如加入token),或者是在Server返回数据到达客户端还未被处理之前进行预处理(如将非JSON格式数据进行转换);当然还有可能对在请求和响应过程过发生的问题进行捕获处理。所有这些需求在开发中都非常常见,所以Angular为我们提供了$http拦截器,用来实现上述需求。

    Angular的$http拦截器是通过$httpProvider.interceptors数组定义的一组拦截器,每个拦截器都是实现了某些特定方法的Factory: 
    http拦截器

    实现:

    http拦截器一般通过定义factory的方式实现:

    myApp.factory('MyInterceptor', function($q) {return { // 可选,拦截成功的请求 request: function(config) {// 进行预处理// ...return config || $q.when(config); }, // 可选,拦截失败的请求 requestError: function(rejection) {// 对失败的请求进行处理// ...if (canRecover(rejection)) { return responseOrNewPromise } return $q.reject(rejection); }, // 可选,拦截成功的响应 response: function(response) {// 进行预处理// ....return response || $q.when(reponse); }, // 可选,拦截失败的响应 responseError: function(rejection) {// 对失败的响应进行处理// ...if (canRecover(rejection)) { return responseOrNewPromise } return $q.reject(rejection); } }; });

    随后,我们需要将实现的拦截器加入到$httpProvider.interceptors数组中,此操作一般在config方法中进行:

    myApp.config(function($httpProvider) { $httpProvider.interceptors.push(MyInterceptor); });

    当然,我们也可以通过匿名factroy的方式实现:

    $httpProvider.interceptors.push(function($q) {return { request: function(config) {// bala }, response: function(response) {// bala }, // bala }; });

    可以看到,每个拦截器都可以实现4个可选的处理函数,分别对应请求(成功/失败)和响应(成功/失败)的拦截:

    • request:此函数在$http向Server发送请求之前被调用,在此函数中可以对成功的http请求进行处理,其包含一个http config对象作为参数,这里对config对象具有完全的处理权限,甚至可以重新构造,然后直接返回此对象或返回包含此对象的promise即可。如果返回有误,会造成$http请求失败。如开发中经常需要在请求头中加入token以便验证身份,我们可以作如下处理:
    request: function(config) {
        config.headers = config.headers || {};
        if ($window.sessionStorage.token) {
            config.headers['X-Access-Token'] = $window.sessionStorage.token;
        }
        return config || $q.when(config);
    }
    • requestError:此方法会在前一个拦截器抛出异常或进行了reject操作时被调用,在这里可以进行恢复请求的操作,或者进行一些对于请求时发起动作的处理(如取消loading等);
    • response:此函数在$http从Server接收到响应时被调用,在此函数中可以对成功的http响应进行处理,这里具有对响应的完全处理权限,甚至可以重新构造,然后直接返回响应或返回包含响应的promise即可。如果返回有误,会造成$http接收响应失败
    • responseError:此方法会在前一个拦截器抛出异常或进行了reject操作时被调用,在这里可以进行恢复响应的操作,进行一些针对错误的处理

    使用用例

    利用request拦截器模拟实现Angular的XSRF(即CSRF)防御

    CSRF,即“跨站请求伪造”,不过不知道为什么Angular将其称为XSRF。当处理与后台交互时,Angular的$http会尝试从客户端cookie中读取一个token,其默认的key为XSRF-TOKEN,并构造一个名为X-XSRF-TOKEN的http头部,与http请求一起发送到后台。Server端就可以根据此token识别出请求来源于同域,当然跨域的请求$http不会加入X-XSRF-TOKEN头部。那我们可以利用request拦截器通过如下方式在同域请求头部中加入此头部以达到模拟Angular的XSRF(即CSRF)防御机制的实现效果:

    /**
    * 正式开发中Angular会主动进行XSRF防御(只要cookie中存在key为`XSRF-TOKEN`的token),
    * 一般不需要手动进行,除非cookie中不存在key为`XSRF-TOKEN`的token,这里只是模拟实现
    */
    request: function(config) {
      if(config.url.indexOf('SAME_DOMAIN_API_URL') > -1) {
        config.headers['X-XSRF-TOKEN'] = $cookies.get('XSRF-TOKEN');
      }
      return config;
    }

    如果初始http请求头部类似于:

    "headers": {
        "Accept": "application/json, text/plain, */*"
    }

    那么经过上述的拦截器后,其http请求头部就变成了:

    "headers": {
        "Accept": "application/json, text/plain, */*",
        "X-XSRF-TOKEN": X-XSRF-TOKEN-VALUE
    }

    利用response拦截器模拟实现Angular JSON易损性(JSON vulnerability)防御

    Angular在$http请求安全性方面不仅为我们设计了XSRF(CSRF)防御,而且针对请求JSON数据的Url可能通过类似于<script>标签加载的方式被恶意网站获取到我们的JSON数据的情况,设计了Angular JSON易损性(JSON vulnerability)防御,即Server端返回的JSON数据头部可以添加")]}', "字符串,得到包含此前缀的响应数据后,Angular会将此前缀删去,将响应还原成正式的JSON数据。此时我们就可以通过response拦截器模拟此过程:

    response: function(response) {
        var data = examineJSONResponse(response); // 假设存在这样一个方法
        if(!data) {
            response = validateJSONResponse(response); // 假设存在这样一个方法
        }
        return response || $q.when(reponse);
    }

    利用request拦截器response拦截器计算http请求耗时

    这个需求可能在开发中并不常用,这里只是作为同时使用request拦截器response拦截器的例子,我们可以在request拦截器response拦截器中分别计时,然后求得其差值即可:

    myApp.factory('timestampMarker', [function() {
        return {
            request: function(config) {
                config.requestTimestamp = new Date().getTime();
                return config;
            },
            response: function(response) {
                response.config.responseTimestamp = new Date().getTime();
                return response;
            }
        };
    }]);
    myApp.config(['$httpProvider', function($httpProvider) {
        $httpProvider.interceptors.push('timestampMarker');
    }]);

    这样我们在每次请求后台时,就能够计算出相应请求的耗时了,如:

    $http.get('https://api.github.com/users/liuwenzhuang/repos').then(function(response) {
        var time = response.config.responseTimestamp - response.config.requestTimestamp;
        console.log('The request took ' + (time / 1000) + ' seconds.');
    });

    http拦截器一般通过定义factory的方式实现:

    myApp.factory('MyInterceptor', function($q) {
      return {
        // 可选,拦截成功的请求
        request: function(config) {
          // 进行预处理
          // ...
          return config || $q.when(config);
        },
    
        // 可选,拦截失败的请求
       requestError: function(rejection) {
          // 对失败的请求进行处理
          // ...
          if (canRecover(rejection)) {
            return responseOrNewPromise
          }
          return $q.reject(rejection);
        },
    
    
    
        // 可选,拦截成功的响应
        response: function(response) {
          // 进行预处理
          // ....
          return response || $q.when(reponse);
        },
    
        // 可选,拦截失败的响应
       responseError: function(rejection) {
          // 对失败的响应进行处理
          // ...
          if (canRecover(rejection)) {
            return responseOrNewPromise
          }
          return $q.reject(rejection);
        }
      };
    });
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    随后,我们需要将实现的拦截器加入到$httpProvider.interceptors数组中,此操作一般在config方法中进行:

    myApp.config(function($httpProvider) {
        $httpProvider.interceptors.push(MyInterceptor);
    });
    • 1
    • 2
    • 3
    • 1
    • 2
    • 3

    当然,我们也可以通过匿名factroy的方式实现:

    $httpProvider.interceptors.push(function($q) {
      return {
       request: function(config) {
           // bala
        },
    
        response: function(response) {
           // bala
        },
    
        // bala
      };
    });
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可以看到,每个拦截器都可以实现4个可选的处理函数,分别对应请求(成功/失败)和响应(成功/失败)的拦截:

    • request:此函数在$http向Server发送请求之前被调用,在此函数中可以对成功的http请求进行处理,其包含一个http config对象作为参数,这里对config对象具有完全的处理权限,甚至可以重新构造,然后直接返回此对象或返回包含此对象的promise即可。如果返回有误,会造成$http请求失败。如开发中经常需要在请求头中加入token以便验证身份,我们可以作如下处理:
    request: function(config) {
        config.headers = config.headers || {};
        if ($window.sessionStorage.token) {
            config.headers['X-Access-Token'] = $window.sessionStorage.token;
        }
        return config || $q.when(config);
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • requestError:此方法会在前一个拦截器抛出异常或进行了reject操作时被调用,在这里可以进行恢复请求的操作,或者进行一些对于请求时发起动作的处理(如取消loading等);
    • response:此函数在$http从Server接收到响应时被调用,在此函数中可以对成功的http响应进行处理,这里具有对响应的完全处理权限,甚至可以重新构造,然后直接返回响应或返回包含响应的promise即可。如果返回有误,会造成$http接收响应失败
    • responseError:此方法会在前一个拦截器抛出异常或进行了reject操作时被调用,在这里可以进行恢复响应的操作,进行一些针对错误的处理。

    使用用例

    为演示Angular $http拦截器的使用方法,下面通过几个常用的用例来说明:

    利用request拦截器模拟实现Angular的XSRF(即CSRF)防御

    CSRF,即“跨站请求伪造”,不过不知道为什么Angular将其称为XSRF。当处理与后台交互时,Angular的$http会尝试从客户端cookie中读取一个token,其默认的key为XSRF-TOKEN,并构造一个名为X-XSRF-TOKEN的http头部,与http请求一起发送到后台。Server端就可以根据此token识别出请求来源于同域,当然跨域的请求$http不会加入X-XSRF-TOKEN头部。那我们可以利用request拦截器通过如下方式在同域请求头部中加入此头部以达到模拟Angular的XSRF(即CSRF)防御机制的实现效果:

    /**
    * 正式开发中Angular会主动进行XSRF防御(只要cookie中存在key为`XSRF-TOKEN`的token),
    * 一般不需要手动进行,除非cookie中不存在key为`XSRF-TOKEN`的token,这里只是模拟实现
    */
    request: function(config) {
      if(config.url.indexOf('SAME_DOMAIN_API_URL') > -1) {
        config.headers['X-XSRF-TOKEN'] = $cookies.get('XSRF-TOKEN');
      }
      return config;
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    如果初始http请求头部类似于:

    "headers": {
        "Accept": "application/json, text/plain, */*"
    }
    • 1
    • 2
    • 3
    • 1
    • 2
    • 3

    那么经过上述的拦截器后,其http请求头部就变成了:

    "headers": {
        "Accept": "application/json, text/plain, */*",
        "X-XSRF-TOKEN": X-XSRF-TOKEN-VALUE
    }
    • 1
    • 2
    • 3
    • 4
    • 1
    • 2
    • 3
    • 4

    利用response拦截器模拟实现Angular JSON易损性(JSON vulnerability)防御

    Angular在$http请求安全性方面不仅为我们设计了XSRF(CSRF)防御,而且针对请求JSON数据的Url可能通过类似于<script>标签加载的方式被恶意网站获取到我们的JSON数据的情况,设计了Angular JSON易损性(JSON vulnerability)防御,即Server端返回的JSON数据头部可以添加")]}', "字符串,得到包含此前缀的响应数据后,Angular会将此前缀删去,将响应还原成正式的JSON数据。此时我们就可以通过response拦截器模拟此过程:

    response: function(response) {
        var data = examineJSONResponse(response); // 假设存在这样一个方法
        if(!data) {
            response = validateJSONResponse(response); // 假设存在这样一个方法
        }
        return response || $q.when(reponse);
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    利用request拦截器response拦截器计算http请求耗时

    这个需求可能在开发中并不常用,这里只是作为同时使用request拦截器response拦截器的例子,我们可以在request拦截器response拦截器中分别计时,然后求得其差值即可:

    myApp.factory('timestampMarker', [function() {
        return {
            request: function(config) {
                config.requestTimestamp = new Date().getTime();
                return config;
            },
            response: function(response) {
                response.config.responseTimestamp = new Date().getTime();
                return response;
            }
        };
    }]);
    myApp.config(['$httpProvider', function($httpProvider) {
        $httpProvider.interceptors.push('timestampMarker');
    }]);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这样我们在每次请求后台时,就能够计算出相应请求的耗时了,如:

    $http.get('https://api.github.com/users/liuwenzhuang/repos').then(function(response) {
        var time = response.config.responseTimestamp - response.config.requestTimestamp;
        console.log('The request took ' + (time / 1000) + ' seconds.');
    });
  • 相关阅读:
    CF1305 Ozon Tech Challenge 2020 游戏存档
    CF1310A Recommendations 题解
    CF755G PolandBall and Many Other Balls 题解
    关于后缀自动机
    具体数学学习笔记
    Flask-SQLAlchemy中解决1366报错
    常用的SQLalchemy 字段类型
    flask_model防止循环引用
    navicate远程访问ubuntu上的mysql数据库
    flask运行环境搭建(nginx+gunicorn)
  • 原文地址:https://www.cnblogs.com/jessical626/p/6822436.html
Copyright © 2011-2022 走看看