Angularjs interceptor

This article is referenced from: https://my.oschina.net/ilivebox/blog/290881

Foreword:

Today, I encountered an interceptor in a company project. As the name suggests, an interceptor is meant to intercept something. The interceptors in the project are all packaged by the team leader, and I don’t understand it, so I asked Du Niang and combined The online explanation, here is a summary

What is an interceptor?

As the name suggests, the interceptor is to intercept something~~~
$httpAngularJS's $http service allows us to communicate with the background by sending HTTP requests. In some cases, we want to capture all requests and perform operations before sending them to the server. There are also situations where we want to capture the response and process it before completing the call. A good example is handling global http exceptions (in our company's project, we also capture responses, handle global http exceptions, and post code later). Interceptors came into being. This article will introduce AngularJS interceptors and give some useful examples.

There is an array of interceptors in $httpProvider, and the so-called interceptor is simply a regular service factory registered in the array. The following example shows you how to create an interceptor:

module.factory('myInterceptor',['$log',function($log){   // 就是一个服务
	$log.debug('$log在这里向您展示这是一个常规的注入工厂');
	var myInterceptor={};
	...
	...
	return myInterceptor;
}])

Then add its name to the $httpProvider.interceptors array;

<!-- lang: js -->
module.config(['$httpProvider', function($httpProvider) { // config是配置服务的
    $httpProvider.interceptors.push('myInterceptor');
}]);

What are the operations that interceptors allow us to do and what are their functions

Intercept the request by implementing the request method: This method will be executed before $http sends the request to the background, so you can modify the configuration or do other operations. This method receives a request configuration object (request configuration object) as a parameter, and then must return a configuration object or promise. If an invalid configuration object or promise is returned, it will be rejected, causing the $http call to fail.

Intercept the response by implementing the response method: This method will be executed after $http receives the response from the background, so you can modify the response or do other operations. This method receives the response object as a parameter, and then must return the response object or promise. The response object includes request configuration, headers, status and data from the background. If an invalid response object is returned or the promise will be rejected, the $http call will fail.

Intercept request exceptions by implementing the requestError method: Sometimes a request fails to be sent or is rejected by the interceptor. The request exception interceptor will capture those requests that were interrupted by the previous request interceptor. It can be used to restore the request or sometimes to undo the configuration made before the request, such as closing the progress bar, activating buttons and input boxes and so on.

Intercept the response exception by implementing the responseError method: Sometimes our background call fails. It is also possible that it was rejected by a request interceptor, or interrupted by the previous response interceptor. In this case, the response exception interceptor can help us recover the background call.

Asynchronous operation (not thoroughly understood)

Sometimes it is necessary to do some asynchronous operations in the interceptor. Fortunately, AngularJS allows us to return a promise to defer processing. It will delay sending the request in the request interceptor or delay the response in the response interceptor.

<!-- lang: js -->
module.factory('myInterceptor', ['$q', 'someAsyncService', function($q, someAsyncService) {
    var requestInterceptor = {
        request: function(config) {
            var deferred = $q.defer();  // 创建一个异步对象;
            someAsyncService.doAsyncOperation().then(function() {
                // Asynchronous operation succeeded, modify config accordingly
                ...
                deferred.resolve(config);
            }, function() {
                // Asynchronous operation failed, modify config accordingly
                ...
                deferred.resolve(config);
            });
            return deferred.promise;
        }
    };
    return requestInterceptor;
}]);

In the above example, the request interceptor uses an asynchronous operation to update the configuration based on the result. Then it continues to execute the operation with the updated configuration. If deferred is rejected, the http request will fail.
The response interceptor example is the same as above.
Only when the deferred is parsed, the request is considered successful. If the deferred is rejected, the request will fail.

example

In this section, I will provide some examples of AngularJS interceptors so that you can better understand how they are used and show how they can help you. But please keep in mind that the solution I provide here is not necessarily the best or most accurate solution.

###Session interception, request interception

There are two ways to achieve server authentication. The first is traditional Cookie-Based authentication. Each requesting user is authenticated through cookies on the server side. Another way is Token-Based authentication. When the user logs in, he will get a sessionToken from the background. The sessionToken identifies each user on the server and will be included in every request sent to the server.

The following sessionInjector adds the x-session-token header to each captured request (if the current user is logged in):

<!-- lang: js -->
module.factory('sessionInjector', ['SessionService', function(SessionService) {
    var sessionInjector = {
        request: function(config) {
            if (!SessionService.isAnonymus) {
                config.headers['x-session-token'] = SessionService.token;
            }
            return config;
        }
    };
    return sessionInjector;
}]);
module.config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.push('sessionInjector'); //放到拦截器中
}]);

Then create a request

<!-- lang: js -->
$http.get('https://api.github.com/users/naorye/repos');

The configuration object before being intercepted by sessionInjector looks like this:

<!-- lang: js -->
{
    "transformRequest": [
        null
    ],
    "transformResponse": [
        null
    ],
    "method": "GET",
    "url": "https://api.github.com/users/naorye/repos",
    "headers": {
        "Accept": "application/json, text/plain, */*"
    }
}

The configuration object after being intercepted by sessionInjector looks like this: Token is added to the request header

<!-- lang: js -->
{
    "transformRequest": [
        null
    ],
    "transformResponse": [
        null
    ],
    "method": "GET",
    "url": "https://api.github.com/users/naorye/repos",
    "headers": {
        "Accept": "application/json, text/plain, */*",
        "x-session-token": 415954427904
    }
}

##Timestamp (request and response interceptor)

Let's use an interceptor to measure how long it takes to return a response from the background. This can be done by adding a time stamp to each request and response.

<!-- lang: js -->
module.factory('timestampMarker', [function() {
    var timestampMarker = {
        request: function(config) {
            config.requestTimestamp = new Date().getTime();
            return config;
        },
        response: function(response) {
            response.config.responseTimestamp = new Date().getTime();
            return response;
        }
    };
    return timestampMarker;
}]);
module.config(['$httpProvider', function($httpProvider) { // 注册拦截器
    $httpProvider.interceptors.push('timestampMarker');
}]);

Above, intercept the request and response, and assign the current time to config.requestTimestamp and config.responseTimestamp.

<!-- lang: js -->  调用接口看看从发送请求到响应需要多长时间
$http.get('https://api.github.com/users/naorye/repos').then(function(response) {
    var time = response.config.responseTimestamp - response.config.requestTimestamp;
    console.log('The request took ' + (time / 1000) + ' seconds.');
});

Below is the complete code for the timestamp (request and response interceptor)

<!doctype html>
<html>
	<head> 
		<title>Timestamp Marker Example</title>
        <script src="../../angularjs/angular-1.5.8/angular.js"></script>
	</head>

	<body ng-app="timestamp-marker-example">
		<div ng-controller="ExampleController">
			The request took <span ng-bind="requestTime"></span> seconds.
		</div>

		<script type="text/javascript">
		var module = angular.module('timestamp-marker-example', []);
		module.factory('timestampMarker', [function() {
		    var timestampMarker = {
		        request: function(config) {
		            config.requestTimestamp = new Date().getTime();
		            return config;
		        },
		        response: function(response) {
		            response.config.responseTimestamp = new Date().getTime();
		            return response;
		        }
		    };
		    return timestampMarker;
		}]);
		module.config(['$httpProvider', function($httpProvider) {
		    $httpProvider.interceptors.push('timestampMarker'); 
		}]);

		module.controller('ExampleController', ['$scope', '$http', function($scope, $http) {
			$scope.requestTime = '[waiting]';
			$http.get('https://api.github.com/users/naorye/repos').then(function(response) {
			    var time = response.config.responseTimestamp - response.config.requestTimestamp;
			    $scope.requestTime = (time / 1000);
			});
		}]);
		</script>
	</body>
</html>

##Request recovery (request exception interception)

In order to demonstrate request exception interception, we need to simulate the situation where the previous interceptor rejected the request. Our request exception interceptor will get the reason for rejection and the recovery request.

Let's create two interceptors: requestRejector and requestRecoverer.

<!-- lang: js -->
module.factory('requestRejector', ['$q', function($q) {
    var requestRejector = {
        request: function(config) {
            return $q.reject('requestRejector');
        }
    };
    return requestRejector;
}]);
module.factory('requestRecoverer', ['$q', function($q) {
    var requestRecoverer = {
        requestError: function(rejectReason) {
            if (rejectReason === 'requestRejector') {
                // Recover the request
                return {
                    transformRequest: [],
                    transformResponse: [],
                    method: 'GET',
                    url: 'https://api.github.com/users/naorye/repos',
                    headers: {
                        Accept: 'application/json, text/plain, */*'
                    }
                };
            } else {
                return $q.reject(rejectReason);
            }
        }
    };
    return requestRecoverer;
}]);
module.config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.push('requestRejector');
    // Removing 'requestRecoverer' will result to failed request
    $httpProvider.interceptors.push('requestRecoverer');
}]);

Then, if you request like the following, we will see success in the log, although requestRejector rejected the request.

<!-- lang: js -->
$http.get('https://api.github.com/users/naorye/repos').then(function() {
    console.log('success');
}, function(rejectReason) {
    console.log('failure');
});

Full code: http://www.webdeveasy.com/code/interceptors-in-angularjs-and-useful-examples/request-recover.html

##Session Recovery (Response to Abnormal Interceptor)

Sometimes, in our single-page application, session loss may occur. This situation may be due to the session expired or the server is abnormal. Let's create an interceptor to restore the session and then automatically resend the original request (assuming the session expires).
For demonstration purposes, let's assume that a session expires and the http status code 419 is returned.

<!-- lang: js -->
module.factory('sessionRecoverer', ['$q', '$injector', function($q, $injector) {
    var sessionRecoverer = {
        responseError: function(response) {
            // Session has expired
            if (response.status == 419){
                var SessionService = $injector.get('SessionService');
                var $http = $injector.get('$http'); // 获取注入
                var deferred = $q.defer();

                // Create a new session (recover the session)
                // We use login method that logs the user in using the current credentials and
                // returns a promise
                SessionService.login().then(deferred.resolve, deferred.reject);

                // When the session recovered, make the same backend call again and chain the request
                return deferred.promise.then(function() {
                    return $http(response.config);
                });
            }
            return $q.reject(response);
        }
    };
    return sessionRecoverer;
}]);
module.config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.push('sessionRecoverer');
}]);

In this way, if the background call fails and the session expires, sessionRecoverer will create a new session and then call the background again.

to sum up

In this article I explained the knowledge about AngularJS interceptors, I introduced request, response, requestError and responseError interceptors, and explained how/when to use them. I also provide some practical and useful examples that you can use in your development.

Finally, write the interceptor used in the project below

First create an interceptor (in appService.js, it will be written later when writing the composition of the project structure);

//发送请求统一带上token
app.factory('authInterceptor', ["$rootScope", "$cookies", "$injector", "$q", "$location", function ($rootScope, $cookies, $injector, $q, $location) {
    return {
        request: function (config) {
            config.headers = config.headers || {};
            var headerName = 'Authorization';
            var cookieName = 'XSRF-TOKEN';
            //检查cookie是否存在
            if ($cookies.get(cookieName) == undefined || $cookies.get(cookieName) == '') {
                $location.path('/Index');
            }
            else {
                config.headers[headerName] = 'Bearer ' + $cookies.get(cookieName);
            }

            //设置不缓存
            config.headers["X-Requested-With"] = 'XMLHttpRequest';
            config.headers["Cache-Control"] = "no-cache";
            config.headers['Pragma'] = 'no-cache';

            return config;
        },
        responseError: function (response) {
            abp.ui.clearBusy("#mianBody");
            abp.ui.clearBusy($('.modal-content'));//清除Modal遮罩层
            //401服务端返回授权失败
            if (response.status == 401) {
                var msg = "您访问的接口未授权,请联系管理员";// response.data.error.message || 
                abp.message.warn(msg, "提示");
            } else if (response.status == 400) {
                abp.message.warn("您输入的参数不符合规范,请重新核对", "提示");
            } else if (response.status == 403) {
                abp.message.warn("您没有接口访问权限,请联系管理员", "提示");
            } else if (response.status == 500 && response.data.error.code == 200)//code为200需要提示到界面的错误信息
            {
                abp.message.warn(response.data.error.message, "提示");
            }
            else {
                var errorData = "<div><p>" + response.status + ":" + response.statusText + "</p>" +
                    "<p>请求接口为:" + response.config.url + "</p>"
                    + "</div>"

                if (response.data.error && response.data.error.message) {
                    errorData += "<p>" + response.data.error.message + "<p>";
                }

                var topMenuId = $location.search().topMenuId;
                $location.path("/error").search({ topMenuId: topMenuId, errorData: errorData });
                return $q.reject(response);
            }

            //else
            //    if (response.status === 404) {
            //    $location.path('/Index');
            //    return $q.reject(response);
            //}
        },
        response: function (response) {
            return response;
        }
    };
}]);

Use the interceptor and
write it in app.js (app.js is the configuration service, which will be written later when writing the project architecture composition);

app.config([
	'$httpProvider',function($httpProvider){
		$httpProvider.interceptors.push('authInterceptor');
	}
])

In this way, the interface will be used before, after the request, and global exception handling in the interceptor. Greatly improve efficiency.

Guess you like

Origin blog.csdn.net/weixin_43996999/article/details/95478716