使用AngularJS的拦截器
拦截器
使用拦截器可以对请求进行预处理,在将请求传递给服务器之前,也可以对应用程序代码进行处理,并且还可以定义全局的错误处理。
https://docs.angularjs.org/api/ng/service/$http #拦截器
我尝试使用拦截器来测量从请求到响应的时间,并处理响应中的错误。
├── angular
│ ├── angular-cookies.js
│ ├── angular-resource.js
│ ├── angular-route.js
│ ├── angular-sanitize.js
│ └── angular.js
├── app
│ ├── controllers.js
│ ├── services.js
│ └── configurations.js ← 新規追加
└── index.html
从AngularJS的开发者指南中借用了示例源代码(用于将通过Yahoo Finance API获取的各国货币汇率应用于输入的数量和金额,并以列表形式显示)。参考链接:https://docs.angularjs.org/guide/concepts
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Try Interceptor</title>
<script src="angular/angular.js"></script>
<script src="angular/angular-resource.js"></script>
<script src="angular/angular-cookies.js"></script>
<script src="angular/angular-sanitize.js"></script>
<script src="angular/angular-route.js"></script>
<script src="app/app.js"></script>
<script src="app/controllers.js"></script>
<script src="app/services.js"></script>
</head>
<body ng-app="myapp.controllers">
<h1>AngularJs Sample</h1>
<div class="container" ng-controller="SampleController">
<b>Invoice:</b>
<div>
Quantity: <input type="number" ng-model="qty" required />
</div>
<div>
Costs: <input type="number" ng-model="cost" required /><br />
<select ng-model="inCurr">
<option ng-repeat="c in currencies">{{c}}</option>
</select>
</div>
<div>
<b>Total:</b>
<span ng-repeat="c in currencies">
{{total(c) | currency:c }}
</span><br/>
<button class="btn" ng-click="pay()">Pay</button>
</div>
</div>
</body>
</html>
angular.module('myapp.controllers', ['myapp.services'])
.controller('SampleController', function($scope, SampleService) {
$scope.qty = 1;
$scope.cost = 2;
$scope.inCurr = 'JPY';
$scope.currencies = SampleService.currencies;
$scope.total = function (outCurr) {
return SampleService.convert($scope.qty * $scope.cost, $scope.inCurr, outCurr);
};
$scope.pay = function () {
SampleService.pay($scope.qty, $scope.incurr);
};
});
angular.module('myapp.services', [])
.factory('SampleService', function ($http) {
var YAHOO_FINANCE_URL_PATTERN =
'http://query.yahooapis.com/v1/public/yql?q=select * from '+
'yahoo.finance.xchange where pair in ("PAIRS")&format=json&'+
'env=store://datatables.org/alltableswithkeys&callback=JSON_CALLBACK';
var currencies = ['USD', 'EUR', 'CNY', 'JPY'];
var usdToForeignRates = {};
refresh();
return {
currencies: currencies,
convert: convert,
refresh: refresh
};
function convert(amount, inCurr, outCurr) {
return amount * usdToForeignRates[outCurr] * 1 / usdToForeignRates[inCurr];
};
function refresh() {
var url = YAHOO_FINANCE_URL_PATTERN.replace('PAIRS', 'USD' + currencies.join('","USD'));
return $http.jsonp(url).success(
function(data, status, headers, config) {
var time = config.responseTimestamp - config.requestTimestamp;
console.log('The request took ' + (time / 1000) + ' seconds.');
var newUsdToForeignRates = {};
angular.forEach(data.query.results.rate, function(rate) {
var currency = rate.id.substring(3,6);
newUsdToForeignRates[currency] = window.parseFloat(rate.Rate);
});
usdToForeignRates = newUsdToForeignRates;
}
);
};
function pay(qty, incurr) {
$http.post("/test", {qty: qty, incurr: incurr});
}
});
在configurations.js中进行了Interceptor的处理。
angular.module('myapp.configurations', ['myapp.controllers'])
.config(function($httpProvider) {
$httpProvider.interceptors.push(
function ($q, $rootScope) {
return {
request: function(config) {
config.requestTimestamp = new Date().getTime();
return config;
},
response: function(response) {
response.config.responseTimestamp = new Date().getTime();
return response;
},
responseError: function(rejection) {
if (500 == rejection.status) {
alert('System Error!');
}
return $q.reject(rejection);
}
};
}
);
});
只需将工厂函数推送到$httpProvider拥有的interceptors数组中,即可定义Interceptor。
可以在拦截器Push的工厂中定义以下四个函数。
request : サーバに引き渡される前のリクエストの前処理を定義
response : リクエストを行ったアプリケーションコードに引き渡される前のレスポンスの前処理
requestError : リクエストでエラーが起きた際の処理を定義
responseError : レスポンスでエラーが起きた際の処理を定義
请参考AngularJS文档中的”https://docs.angularjs.org/api/ng/service/$http#Interceptors”部分。
为了实现测量请求到响应时间和处理响应错误,本文定义了request、response和responseError。
在请求和回复中计算处理时间,当响应错误为HTTP状态码500时执行处理。
试着去做

进行错误处理的测试
虽然可以在服务器端编写错误处理来进行确认,但这样比较麻烦,所以我打算通过测试来进行确认。
请参考以下内容来了解基本的测试编写方法。
使用Jasmine进行AngularJS测试。
AngularJS在它的$httpBackend中提供了模拟后端行为的功能,我们利用这个功能对SampleService的pay方法中的$http.post请求的服务器端进行了模拟。
※由于在加载SampleService时会使用$http.jsonp访问yahooapi,所以我们也对这个请求进行了模拟。
'use strict';
describe('Service: SampleService', function () {
beforeEach(module('myapp.services'));
var $httpBackend,
SampleService ;
var YAHOO_FINANCE_URL_PATTERN =
'http://query.yahooapis.com/v1/public/yql?q=select * from '+
'yahoo.finance.xchange where pair in ("PAIRS")&format=json&'+
'env=store://datatables.org/alltableswithkeys&callback=JSON_CALLBACK';
var alert_msg;
beforeEach(inject(function($injector) {
// Set up the mock http service responses
$httpBackend = $injector.get('$httpBackend');
var currencies = ['USD', 'EUR', 'CNY', 'JPY'];
var url = YAHOO_FINANCE_URL_PATTERN.replace('PAIRS', 'USD' + currencies.join('","USD'));
$httpBackend.when('JSONP', url).respond(
{query: {results: {rate: [
{id: 'HOGE', rate: 1.0}
]}}}
);
alert_msg = '_defalut_';
spyOn(window, 'alert').and.callFake(function(msg) {
alert_msg = msg;
});
SampleService = $injector.get('SampleService');
}));
it('should success, when pay() is called', function() {
expect(alert_msg).toEqual('_defalut_');
var data = {qty: 1, cost: 1000};
$httpBackend.when('POST', '/test', data).respond(200, '');
SampleService.pay(1, 1000).success(function() {
expect(true).toBe(true);
}).error(function() {
expect(false).toBe(true);
});
$httpBackend.flush();
expect(alert_msg).toEqual('_defalut_');
});
it('should fail, when pay() is called', function() {
expect(alert_msg).toEqual('_defalut_');
var data = {qty: 1, cost: 1000};
$httpBackend.when('POST', '/test', data).respond(400, '');
SampleService.pay(1, 1000).success(function() {
expect(false).toBe(true);
}).error(function() {
expect(true).toBe(true);
});
$httpBackend.flush();
expect(alert_msg).toEqual('_defalut_');
});
it('should fail and emit alert message for status code 500, when pay() is called', function() {
expect(alert_msg).toEqual('_defalut_');
var data = {qty: 1, cost: 1000};
$httpBackend.when('POST', '/test', data).respond(500, '');
SampleService.pay(1, 1000).success(function() {
expect(false).toBe(true);
}).error(function() {
expect(true).toBe(true);
});
$httpBackend.flush();
expect(alert_msg).toEqual('System Error!');
});
});
这个测试有三个案例。
-
- 200状态码的正常情况
-
- 400状态码的异常情况
- 500状态码的异常情况
我正在检查警告消息的内容。
请参考
– AngularJS 拦截器及其有用示例
– 为了更好地使用 AngularJS 的提示和技巧