在目前前后端分离的工作模式下,前端需要对接许多接口服务,对接的同时我们会发现很多时候会对请求做一些相同的处理,例如 阻止重复提交、根据服务的返回给前台一些相同的提示信息 或者 打印请求日志 等等。针对这种情况,我们可以通过简单的二次封装 http 请求方法,来简化我们的工作 并提高用户体验。
下面分别列举了 卡萨帝商城、海贝商城管理后台 和 数据中心 三个不同框架中对于 http 请求的实际应用,每个项目中封装的侧重点不同,但是不同框架处理问题的思路是基本一致的。
jQuery
需求: 部分服务需要校验用户登录,如未登录则应中止请求,跳转登陆页或给出提示
解决方法:设置拦截器
jQuery.ajaxSetup({ type: "post", dataType: "json", cache: false, box_obj: null, scroll: null, beforeSend: function(request) {// 创建一个拦截器,在请求发送出去之前做一次处理// 判断用户是否登录,未登录则中止请求 并跳转登陆页或给出提示 if (this.login && !istrsidssdssotoken()) { request.abort(); } }, success: function(data) {// 请求成功返回后可做统一处理,例如统一的弹层提示等 if (data.isSuccess) { }// 执行每个请求的 success_cb 方法,并传递请求的返回结果 if (this.success_cb) { this.success_cb(data); } }, error: function(jqXHR, textStatus, errorThrown) { }});
$.ajax({url: url,data: params,login: true, // 如果设置 login 为 true 则在请求前做登录校验success_cb: function(data) {// 当前请求的成功回调if (data.isSuccess) {}}});
AngularJS
需求:阻止重复提交,根据后台返回做某些统一处理
解决方法:记录每次发送的请求发送前校验,同一 url 请求是否在进行中 以及 请求参数是否一致
app.factory('trsGetData', function($http, $q, ngDialog, $state, $rootScope, $timeout, $httpParamSerializerJQLike,siteInfo) { $rootScope.repeatedlyClick = {}; //阻止重复提交 return function(url, params, funName, method) { if (!funName) { funName = "temporary"; }// 如果是下载,则走单独逻辑 if (method == "download") { url = url + '?'; for (var param in params) { if (params[param]) { url += param + '=' + params[param] + '&'; } } url = url.slice(0, -1); window.location.href = url; return false; } if (method != "get") { method = "post"; } var defer = $q.defer(); /** * 阻止重复提交 校验方法名和请求参数 */ if (!$rootScope.repeatedlyClick[funName] || $rootScope.repeatedlyClick[funName] !== JSON.stringify(params)) { $rootScope.repeatedlyClick[funName] = JSON.stringify(params); //记录方法funName提交的参数parms如果提交的参数相同则不会再次提交 $http({ method: method, url: url, headers: { 'formdata': "1", //用于wcm解析data 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'siteId':siteInfo.curSite.siteId }, data: method == 'get' ? "" : $httpParamSerializerJQLike(params), params: method == 'get' ? params : "", timeout: 60000 //超时时间30s }).success(function(data, status, headers, config) { $rootScope.repeatedlyClick[funName] = false; /** * wcm登陆失败则跳转登陆页 * open服务 wcm未登录,请登录后再操作 * here应用 access-denied * wcm服务 boolean */ if ((data.resultMsg === "wcm未登录,请登录后再操作" || data.resultMsg === "access-denied" || headers("TRSNotLogin"))) { $rootScope.$emit("userIntercepted", "notLogin", data); } if (!data.isSuccess || data.isSuccess == 'false') { if (data.resultMsg) { console.warn(data.resultMsg); } } defer.resolve(data); }).error(function(data, status, headers, config) { $rootScope.repeatedlyClick[funName] = false; /** * 请求失败 则给出提示 */ if (status == -1 || status == 408) { } ngDialog.open({ template: 'common/404/dialog404.html', className: 'ngdialog-theme-default', width: 572, showClose: false, closeByDocument: false, closeByEscape: false, controller: ['$scope', function($scope, $state) { $scope.dialog = { "dialogTitle": "提示", "dialogMessage": "", "dialogIcon": "icon-cuowu", "dialogConfirm": "确定", // "dialogCancel": "取消", "goHome": function() { ngDialog.close(); }, "goBack": function() { history.go(-1); ngDialog.close(); } }; }] }); defer.reject(data); }); } return defer.promise; }; });
trsGetData(envUrl + '/eshop/eshopcenter.do', params, "isSiteHaveRight", "post");
Vue.js
需求:由于列表服务返回很慢,用户如果切换不同检索条件检索的话,有可能出现后请求的数据先返回,先请求的数据后返回的情况,导致页面展示数据与用户检索条件不符的情况
解决方法:相同的请求发出前设置拦截器取消正在进行中的相同请求
var promiseArr = {}trsGetData: function () { let cancel // 创建 http 请求 const httpServer = axios.create({ responseType: 'json', cancelToken: new axios.CancelToken(function (c) { cancel = c }) }) httpServer.interceptors.request.use(function (config) { if (promiseArr[config.url]) { promiseArr[config.url]('操作取消') // 取消相同请求 promiseArr[config.url] = cancel } else { promiseArr[config.url] = cancel // 记录当前请求的 cancel 方法 } return config }, function (err) { // return Promise.reject (error) }) return httpServer }
gUtils.trsGetData().post(Vue.prototype.apiUrl + 'vipcenter/memberRights/query', data) .then(function (data) { if (data.data.isSuccess) { return data.data.data } else { return [] } }).catch(function (err) { console.warn(err) return catchMessage(err) }) }