X5內核 請求超時后會自動阻止請求返回並由代理服務器將原參數重新發送請求到服務層代碼。但由於第一次請求已經請求到服務器,會導致出現重復下單、支付等重大問題。
該問題由於騰訊x5瀏覽器會自動阻止第一次請求返回到頁面,屆時將拋出io異常。最開始打算通過攔截器來進行攔截第二次請求,但這樣將使頁面無法接受到返回信息報錯。
初步解決思路 當第二次請求訪問進服務層時暫停該線程,並定時循環查詢第一次請求是否返回成功,將第一次返回成功的結果賦值給第二次。
由於我們系統中所有頁面請求都會過一個通用controller 所以也會有很多正常請求通過 為了不影響整體使用
這里我用到了頁面驗簽的方法,用於驗證請求是否為同一次。這里先將參數進行排序,再用md5加密,添加一個“sign”的參數到后台,后台再排序加密進行比對,進行驗簽,注意這里參數加入了
時間戳,用於防止重復提交
KGF.getMD5 = function(params){ params["key"] = "ouE6yWvy"; var sParams = Object.keys(params).sort(); var oriString = ""; for(key in sParams){ oriString += sParams[key]+"="+params[sParams[key]]+"&"; } oriString = oriString.substr(0,oriString.length-1); delete params.key; return KGF.md5(oriString); };
驗簽成功后,由於需要將第一次返回成功的結果賦值給第二次。所以需要將調用接口成功的結果存入seesion中,但此時需要及時去清理,否則容易造成服務器內存溢出。
默認調用接口成功后就將結果集放入session,驗簽一通過就去取,這里的關鍵在於何時去刪除session中的值。由於所有結果訪問均會通過該控制器,為了區分不同的請求,
故用sign作為鍵,返回結果為值。
下面貼出代碼
@RequestMapping(value={"/common/ajax.do"}, method={RequestMethod.POST}, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public List<AjaxResponse> execute2(@RequestHeader(value="Accept", required=false) String accept, HttpServletRequest request){ XMLResults results = new XMLResults(); try{ AjaxObject object = (AjaxObject)request.getAttribute("ParamName"); String openid = (String) request.getSession().getAttribute("openid"); Class[] cArg = new Class[2]; cArg[0] = HttpServletRequest.class; cArg[1] = Map.class; DataResult dataResult = new DataResult(); Map<String, Object> params = object.getParams(); String signFromAjax = (String) params.get("sign"); long startTime = 0; long endTime = 0; if(!checkSign(params)){ log.error("接口請求驗簽失敗"); dataResult.setRetInfo("-1", "接口請求驗簽失敗", CommonUtil.parseDoubleList(new HashMap())); }else{ String signFromSession = (String) request.getSession().getAttribute(signFromAjax); request.getSession().setAttribute(signFromAjax,signFromAjax); //處理騰訊安卓微信瀏覽器,10s重發請求的問題 if(signFromSession != null){ for(int i = 0; i <10; i++){ try { Thread.currentThread().sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("====================重復請求讀取接口返回信息:第"+(i+1)+"次===================="); DataResult dataResultFromSession = (DataResult) request.getSession().getAttribute(signFromAjax+"interfaceResult"); if(dataResultFromSession != null){ request.getSession().removeAttribute(signFromAjax); request.getSession().removeAttribute(signFromAjax+"interfaceResult"); results = this.baseService.convertResult(dataResult); dataResult = dataResultFromSession; break; } if(i == 9){ dataResult.setRetInfo("-1", "接口請求超時,請聯系管理員", CommonUtil.parseDoubleList(new HashMap())); } } }else{ startTime = new Date().getTime(); params.put("requestIp", CommonUtil.getIpAddr(request)); params.put("openid", openid); params.put("deviceType", CommonUtil.getDeviceType(request)); params.put("browserType", CommonUtil.getBrowserType(request)); params.put("g_stationaddr", CommonUtil.getIpAddr(request)); Method method = commonDataModel.getClass().getDeclaredMethod(object.getCode(), cArg); dataResult = (DataResult) method.invoke(commonDataModel, request, params); endTime = new Date().getTime(); request.getSession().setAttribute(signFromAjax+"interfaceResult", dataResult); } } if(endTime - startTime <= 10000){ request.getSession().removeAttribute(signFromAjax); request.getSession().removeAttribute(signFromAjax+"interfaceResult"); } results = this.baseService.convertResult(dataResult); }catch (Exception e){ log.error("數據請求失敗,錯誤信息:" + e.getMessage()); results.setRetInfo("-1", "數據請求失敗"); } return webHelper.convertAjaxResponse(results); }