前言
过完年来我们老大安排我做的一个小需求,就是往现有的.net客服查询系统增加一个二级菜单,点击菜单在对话框中输入账号信息即可查询到该用户的信息。当时做这个的时候遇到了一些问题,具体可查看解决了异常java.util.zip.ZipException: invalid CEN header (bad signature)心路历程这个博客。本来以为已经搞定了,谁知道客服用他们工作常用的浏览器(电脑配置老,所以用的都是低版本的浏览器,主要是遨游,偶尔用360)去使用这个功能的时候,出现了无法查询的异常。解决这个异常其实并不难,但是花了我好多的时间,所以我就把这个情况写到博客中,也给大家一个参考。
问题分析
当时我发现我和其他开发同事用谷歌浏览器来使用这个功能的时候,都是正常的,然后客服不可以,当时根据自己写的js提示,问题出现在ajax发送请求的那部分。所以我的定位是ajax的写法并不兼容低版本的浏览器。
所以我先上全部的代码,这样显得更好理解。
这是写在.net部分的代码,点击事件,操作dom。
<script type="text/javascript">
var btn = document.getElementById('vnet');
var manZone = document.getElementById('man_zone');
btn.onclick = function () {
manZone.innerHTML = '<iframe frameborder="0" width="772" height="492" src="<% =ResolveUrl("~/DataQuery/hnvnet.html")%>"></iframe>';
}
</script>
hnvnet.html这个是我往.net系统这个DataQuery目录添加的html文件,.net系统部署的是在内网http://192.168.5.215:6088/这个ip下,然后在hnvnet.html这个HTML文件里触发点击事件之后,就会往我部署在http://192.168.5.214:8081这个服务器tomcat下的java项目发送请求并返回数据库查询数据。
问题一:跨域问题
其实我遇到的第一个问题是跨域问题,浏览器会报异常。具体异常我是找不到了。不过这个很容易解决,直接在后台代码中加多一行代码就行。
@RequestMapping(value = "/queryBill", method = { RequestMethod.GET, RequestMethod.POST }, produces = "text/html;charset=UTF-8")
@ResponseBody
public String queryBill(HttpServletRequest request,HttpServletResponse response) throws Exception {
response.addHeader("Access-Control-Allow-Origin", "*");
String account = Tools.GetString(request.getParameter("account"), "");
List<QueryInfo> queryInfo = new ArrayList<QueryInfo>();
if(account != ""){
queryInfo = userService.queryBill(account);
}
return JSON.toJSONString(queryInfo);
}
response.addHeader("Access-Control-Allow-Origin", "*");
String account = Tools.GetString(request.getParameter("account"), "");
List<QueryInfo> queryInfo = new ArrayList<QueryInfo>();
if(account != ""){
queryInfo = userService.queryBill(account);
}
return JSON.toJSONString(queryInfo);
}
问题二:ajax post低版本浏览器报错
解决了问题一,出现了问题二,下面是我发送请求的js代码。使用这个代码使用高版本浏览器比如说谷歌,ie10以上这些都是正常的,但是客服的工作浏览器遨游浏览器(并不是最新的)不可以,直接报了服务器异常,请重试的错误,也就是执行下面这个函数到了error 这部分才会报错,我一看tomcat日志,正常。所以我就在想估计是我的jq ajax写法并不能兼容低版本的浏览器。
function sub() {
$("#alternatecolor").hide();
if(checkAccount()){
$(".metion").text("正在查询,请稍候~");
$(".submit").attr('disabled',true);
var account = $("#account").val();
var param = new Object();
param.account = account;
remindAjax.ajaxPost("http://192.168.5.214:8081/hnQuery/query/queryBill.html",param,
function(){
if(param == null)
return;
},
function (data) {
$(".metion").text("");
$(".submit").attr('disabled',false);
if (data.length == 0) {
$(".metion").text("该用户不存计费!");
return;
}
var businessType = "其它";
var billFlag = "有计费";
var html = "";
for(var i = 0; i < data.length; i++){
if(data[i].price == 10.00){
businessType = "业务1";
}else if (data[i].price == 3.00) {
businessType = "业务2";
}
if(data[i].ifExtend == 0){
billFlag = "无计费";
}
html += "<tr>";
html += "<td>" + data[i].userId + "</td>";
html += "<td>" + data[i].payAccount + "</td>";
html += "<td>" + businessType + "</td>";
html += "<td>" + data[i].price + "</td>";
html += "<td>" +billFlag + "</td>";
html += "</tr>";
}
$("#J_TbData").html(html);
$("#alternatecolor").show();
altRows('alternatecolor');
},
function()
{
$(".submit").attr('disabled',false);
$(".metion").text("服务器异常,请重试!");
}
);
}
}
失败的解决方案
然后我就使用了下面这个比较原生的写法,写法是参考兼容ie7到ie11,edge,chrome,firefox的ajax发送接收post数据代码。使用ie浏览器通过切换ie 7 8 9 10 11来慢慢查看效果。
function sub()
{
var xhr = getxhr();
if (!xhr)
{
alert("您的浏览器不支持AJAX!");
return false;
}
//设置ajax数据
var url = "http://192.168.5.214:8081/hnQuery/query/queryBill.html";
//set post
var formData = getCookieData();
if (false == formData)
{
return false;
}
var postData = JSON.stringify([formData]); //将数据封装成[{"xx":"xx"}]这种格式
//var postData = formData;
console.log(postData);
//开始ajax
/********ie 8,9兼容***********/
try
{
if( xhr instanceof XDomainRequest)
{
xhr.open("post",url);
xhr.timeout = 10000;
xhr.onprogress = function() { };
xhr.onerror = function () { };
xhr.ontimeout = function () {};
xhr.onload = function() {
try
{
console.log(xhr.responseText);
var data = JSON.parse(xhr.responseText);
console.log(data);
if (data.length == 0) {
$(".metion").text("该用户不存在计费!");
return;
}
var businessType = "其它";
var billFlag = "有计费";
var html = "";
for(var i = 0; i < data.length; i++){
if(data[i].price == 10.00){
businessType = "业务1";
}else if (data[i].price == 3.00) {
businessType = "业务2";
}
if(data[i].ifExtend == 0){
billFlag = "无计费";
}
html += "<tr>";
html += "<td>" + data[i].userId + "</td>";
html += "<td>" + data[i].payAccount + "</td>";
html += "<td>" + businessType + "</td>";
html += "<td>" + data[i].price + "</td>";
html += "<td>" +billFlag + "</td>";
html += "</tr>";
}
$("#J_TbData").html(html);
$("#alternatecolor").show();
altRows('alternatecolor');
}
catch (e)
{
return false;
alert("服务器出错");
}
}
xhr.send(postData);
return false;
}
}
catch(e)
{
alert("出异常了");
}
/********ie 8,9兼容结束***********/
xhr.open("POST",url,true);
xhr.setRequestHeader("Content-Type","application/json;charset=utf-8");
xhr.open("POST",url,true);
xhr.setRequestHeader("Content-Type","application/json;charset=utf-8");
xhr.onreadystatechange = function(){
if (xhr.readyState == 4)
{
if (xhr.status == 200)
{
//显示错误信息
try
{
var data = JSON.parse(xhr.responseText);
if (data.length == 0) {
$(".metion").text("该用户不存在计费!");
return;
}
var businessType = "其它";
var billFlag = "有计费";
var html = "";
for(var i = 0; i < data.length; i++){
if(data[i].price == 10.00){
businessType = "业务1";
}else if (data[i].price == 3.00) {
businessType = "业务2";
}
if(data[i].ifExtend == 0){
billFlag = "无计费";
}
html += "<tr>";
html += "<td>" + data[i].userId + "</td>";
html += "<td>" + data[i].payAccount + "</td>";
html += "<td>" + businessType + "</td>";
html += "<td>" + data[i].price + "</td>";
html += "<td>" +billFlag + "</td>";
html += "</tr>";
}
$("#J_TbData").html(html);
$("#alternatecolor").show();
altRows('alternatecolor');
}
catch (e)
{
alert("服务器出错");
}
}
else
{
//alert("网络错误");
}
}
}
xhr.send(postData);
return false;
}
var postData = JSON.stringify([formData]); //将数据封装成[{"xx":"xx"}]这种格式
//var postData = formData;
console.log(postData);
//开始ajax
/********ie 8,9兼容***********/
try
{
if( xhr instanceof XDomainRequest)
{
xhr.open("post",url);
xhr.timeout = 10000;
xhr.onprogress = function() { };
xhr.onerror = function () { };
xhr.ontimeout = function () {};
xhr.onload = function() {
try
{
console.log(xhr.responseText);
var data = JSON.parse(xhr.responseText);
console.log(data);
if (data.length == 0) {
$(".metion").text("该用户不存在计费!");
return;
}
var businessType = "其它";
var billFlag = "有计费";
var html = "";
for(var i = 0; i < data.length; i++){
if(data[i].price == 10.00){
businessType = "业务1";
}else if (data[i].price == 3.00) {
businessType = "业务2";
}
if(data[i].ifExtend == 0){
billFlag = "无计费";
}
html += "<tr>";
html += "<td>" + data[i].userId + "</td>";
html += "<td>" + data[i].payAccount + "</td>";
html += "<td>" + businessType + "</td>";
html += "<td>" + data[i].price + "</td>";
html += "<td>" +billFlag + "</td>";
html += "</tr>";
}
$("#J_TbData").html(html);
$("#alternatecolor").show();
altRows('alternatecolor');
}
catch (e)
{
return false;
alert("服务器出错");
}
}
xhr.send(postData);
return false;
}
}
catch(e)
{
alert("出异常了");
}
/********ie 8,9兼容结束***********/
xhr.open("POST",url,true);
xhr.setRequestHeader("Content-Type","application/json;charset=utf-8");
xhr.open("POST",url,true);
xhr.setRequestHeader("Content-Type","application/json;charset=utf-8");
xhr.onreadystatechange = function(){
if (xhr.readyState == 4)
{
if (xhr.status == 200)
{
//显示错误信息
try
{
var data = JSON.parse(xhr.responseText);
if (data.length == 0) {
$(".metion").text("该用户不存在计费!");
return;
}
var businessType = "其它";
var billFlag = "有计费";
var html = "";
for(var i = 0; i < data.length; i++){
if(data[i].price == 10.00){
businessType = "业务1";
}else if (data[i].price == 3.00) {
businessType = "业务2";
}
if(data[i].ifExtend == 0){
billFlag = "无计费";
}
html += "<tr>";
html += "<td>" + data[i].userId + "</td>";
html += "<td>" + data[i].payAccount + "</td>";
html += "<td>" + businessType + "</td>";
html += "<td>" + data[i].price + "</td>";
html += "<td>" +billFlag + "</td>";
html += "</tr>";
}
$("#J_TbData").html(html);
$("#alternatecolor").show();
altRows('alternatecolor');
}
catch (e)
{
alert("服务器出错");
}
}
else
{
//alert("网络错误");
}
}
}
xhr.send(postData);
return false;
}
function getxhr()
{
//获取ajax对象
var xhr = null;
try
{
xhr = new XDomainRequest();
}
catch(e)
{
try
{
xhr = new XMLHttpRequest();
}
catch(e)
{
try
{
xhr = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e)
{
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
}
}
return xhr;
}
function getCookieData()
{
var account = $("#account").val();
var postData = {
'account':account
};
return postData;
}
当时使用ie8 和 ie9访问都没报错,但是就是没接收到数据,后面才发现是后台接收的方式不对,前端发送json数据,那么后台如果还是使用request.getParameter("account")这种写法是接收不到的。当时我使用的架构包含了springMVC,网上有人说使用SpringMVC @RequestBody 接收Json数组对象 这种方法可以接收,但是我使用之后浏览器给我响应了415的响应码,我又去百度,他们说是没设置内容类型contenType导致后台认不出数据格式,但是你看我前面原生的代码,我不知道怎么设置啊,我去查的时候好像也没这个属性。最后我使用了比较原生的写法。
package com.aotain.project.controller;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import com.aotain.project.bean.QueryInfo;
import com.aotain.project.service.UserService;
import com.aotain.project.util.Tools;
@Controller
@RequestMapping(value = "/query")
public class QuryBillController {
private static Logger Log = Logger.getLogger("sysLog");
@Resource(name = "UserService")
private UserService userService;
@RequestMapping(value = "/queryBill", method = { RequestMethod.GET, RequestMethod.POST }, produces = "text/html;charset=UTF-8")
@ResponseBody
public String queryBill(HttpServletRequest request,HttpServletResponse response) throws Exception{
response.addHeader("Access-Control-Allow-Origin", "*");
//String account = Tools.GetString(request.getParameter("account"), "");
ServletInputStream inputStream = request.getInputStream();
BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer sb = new StringBuffer();
String sTempOneLine = new String("");
while((sTempOneLine = bf.readLine()) != null) {
sb.append(sTempOneLine);
}
String JsonString = sb.toString();
JSONArray array = JSONArray.fromObject(JsonString);
JSONObject jsonObject = array.getJSONObject(0);
String account = jsonObject.getString("account");
List<QueryInfo> queryInfo = new ArrayList<QueryInfo>();
if(account != ""){
queryInfo = userService.queryBill(account);
}
return JSON.toJSONString(queryInfo);
}
}
然后使用ie7-11分别去访问,除了7不可以之外(浏览器报了JSON没定义这个异常),其他的都正常,我就觉得这回应该是大功告成了吧,然后我就开开心心的去部署,叫客服用他们的浏览器测试,但是结果却大失所望,还是不行。他们的傲游浏览器报了下面这个错误
听我前端的同事说,应该是没设置请求头。又陷入郁闷之中。
后来参考了这个系统本来的做法,自己做链接,点击链接再跳去java做的jsp页面
<ul>
<li class="title"><b><a href="<% =ResolveUrl("~/DataQuery/List.aspx?t=yxdata")%>">记录查询</a></b></li>
</ul>
<%@ Page Language="C#" MasterPageFile="~/Master.Master" AutoEventWireup="true" CodeBehind="List.aspx.cs" Inherits="WebProject.DataQuery.List" %>
<asp:Content ID="Head1" ContentPlaceHolderID="head" runat="server">
<link href="<% =ResolveUrl("~/Css/ui-dialog.css") %>" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="<% =ResolveUrl("~/JavaScript/jquery-1.7.2.min.js") %>"></script>
<script type="text/javascript" src="<% =ResolveUrl("~/JavaScript/dialog-plus.js") %>"></script>
</asp:Content>
<asp:Content ID="Title1" ContentPlaceHolderID="Title" runat="server"><asp:Literal ID="ltdirection" runat="server"></asp:Literal></asp:Content>
<asp:Content ID="content1" ContentPlaceHolderID="Content1" runat="server">
<div id="man_zone" >
<span id="yxdata" runat="server" class="servicecodequery"></span>
</div>
<script type="text/javascript">
$(document).ready(function () {
var d = dialog({
width: 880,
height: 530,
title: '记录查询',
url: 'http://192.168.5.214:8080/querymanager/index.htmls'
});
d.show();
});
</script>
</asp:Content>
然后我把代码改成下面的形式,跳转到我java应用的jsp页面,最后成功。
<script type="text/javascript">
var btn = document.getElementById('vnet');
var manZone = document.getElementById('man_zone');
btn.onclick = function () {
manZone.innerHTML = '<iframe frameborder="0" width="772" height="492" src="http://192.168.5.214:8081/hnQuery/query/queryPage.html"></iframe>';
}
</script>
function sub() {
$("#alternatecolor").hide();
if(checkAccount()){
$(".metion").text("正在查询,请稍候~");
$(".submit").attr('disabled',true);
var account = $("#account").val();
var param = new Object();
param.account = account;
remindAjax.ajaxPost("http://192.168.5.214:8081/hnQuery/query/queryBill.html",param,
function(){
if(param == null)
return;
},
function (data) {
$(".metion").text("");
$(".submit").attr('disabled',false);
if (data.length == 0) {
$(".metion").text("该用户不存计费!");
return;
}
var businessType = "其它";
var billFlag = "有计费";
var html = "";
for(var i = 0; i < data.length; i++){
if(data[i].price == 10.00){
businessType = "业务1";
}else if (data[i].price == 3.00) {
businessType = "业务2";
}
if(data[i].ifExtend == 0){
billFlag = "无计费";
}
html += "<tr>";
html += "<td>" + data[i].userId + "</td>";
html += "<td>" + data[i].payAccount + "</td>";
html += "<td>" + businessType + "</td>";
html += "<td>" + data[i].price + "</td>";
html += "<td>" +billFlag + "</td>";
html += "</tr>";
}
$("#J_TbData").html(html);
$("#alternatecolor").show();
altRows('alternatecolor');
},
function()
{
$(".submit").attr('disabled',false);
$(".metion").text("服务器异常,请重试!");
}
);
}
}
@RequestMapping(value = "/queryPage", method = { RequestMethod.GET, RequestMethod.POST })
public ModelAndView queryPage() throws Exception {
ModelAndView mav = new ModelAndView("main/main");
return mav;
}
反思
其实吧,通过这个例子我们可以发现,如果发送请求的js跟接口不在同一个应用中,或许会存在或多或少的问题。我就在想,如果真遇到这种情况,可以这么做:
这个就像我之前做的调用淘宝接口查询号码归属地一样:
1、首先用java写个发送请求的源文件,类似于下面这种,给别人的接口发送请求获取数据:
public static JSONObject calcMobileCity(String urlString,String encodeType) throws Exception {
String jsonString = null;
JSONArray array = null;
JSONObject jsonObject = null;
StringBuffer sb = new StringBuffer();
BufferedReader buffer = null;
InputStream in = null;
HttpURLConnection urlcon = null;
try {
URL url = new URL(urlString);
urlcon = (HttpURLConnection) url.openConnection();
urlcon.setConnectTimeout(Integer.valueOf(ConnectTimeout)); //超时时间 5000 ms
urlcon.setReadTimeout(Integer.valueOf(ReadTimeout)); //读取时间 5000ms
in = urlcon.getInputStream();
buffer = new BufferedReader(new InputStreamReader(in, encodeType)); // 解决乱码问题
String line = null;
while ((line = buffer.readLine()) != null) {
sb.append(line);
}
jsonString = sb.toString();
jsonString = jsonString.replaceAll("^[__]\\w{14}+[_ = ]+", "[");// 替换掉“__GetZoneResult_ = ",让它能转换为JSONArray对象
String jsonString2 = jsonString + "]";
array = JSONArray.fromObject(jsonString2); // 把STRING转化为json对象
jsonObject = array.getJSONObject(0); // 获取JSONArray的JSONObject对象,便于读取array里的键值对
} catch (Exception e) {
Log.error("查询号码归属地异常,请求地址:" +urlString+",异常信息:"+ e.toString());
} finally {
if (buffer != null) {
buffer.close();
}
if (in != null) {
in.close();
}
if(urlcon != null){
urlcon.disconnect();
}
}
return jsonObject;
}
2、然后再写个Controller负责接收参数和请求过来的数据
3、最后再写个ajax请求给你自己的接口来获取数据。这样是有点曲线救国的意思,如果使用ajax发送请求到别人的接口出现问题可以考虑这种方法,至少实现了要求。
结语
我发现这次做这个小功能,很多时候并不是一些比较大的或者难的技术问题,反而是一些小细节,也往往是这些细节,可能让你不能按时完成任务,消耗你的耐心。所以说有时候参考系统本来的做法或者换个角度也许就可以解决问题。这篇博客文字写的有点多,我只是想把在解决问题过程中的想法(不管成功还是失败)都记录下来,因为下次出现这种情况可能是另外一种原因,记录下来可以起到一个抛砖引玉的作用。