前段时间做一个用户接口,对方提供WebService方式的接口供调用,记录遇到的几个问题
1、Soap1.1 、 Soap1.2 与SoapAction
错误描述:用saaj访问axis2构建的WS,返回错误消息:
com.sun.xml.internal.messaging.saaj.soap.MessageImpl identifyContentType
严重: SAAJ0537: Invalid Content-Type. Could be an error message instead of a SOAP message
com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: java.security.PrivilegedActionException: com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Invalid Content-Type:application/xml. Is this an error message instead of a SOAP response?
at com.sun.xml.internal.messaging.saaj.client.p2p.HttpSOAPConnection.call(HttpSOAPConnection.java:146)
错误分析:返回的Soap消息的Content-Type是application/xml,saaj不识别此
Content-Type,认为返回的不是Soap响应
原因:1) 客服端saaj能识别的Content-Type为text/xml和application/soap+xml
2) axis2返回的为application/xml,之所以返回此类型,是因为axis2根据请求类型返回不同Content-Type的内容,请求类型有soap1.1、soap1.2、REST
3) 客户端发送请求时,发送soap1.1但是代码里MIME头未加SoapAction设置,axis2是通过SoapAction的指定区分Soap1.1与REST请求,这样请求被识别为REST,返回的content-tyee被设置为application/xml
解决方法:
1) 使用soap1.2
MessageFactory mf = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL)
2) 使用soap1.1并指定SoapAction
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage msg = mf.createMessage();
msg.getMimeHeaders().addHeader("SOAPAction", Qns);
* REST(Representational State Transfer): 表现性状态传输,与soap区别在于,提交请求直接用url+参数,返回结果为xml文档,不使用soap消息作为往返的信息载体,说是不只将http协议作为单纯的传输协议什么的,第一次听说这个概念,但实际上以前做Domino的时候经常用到,类似?readviewentries这样的扩展Url命令
* 不单是axis2,只要使用soap1.1,则Soap请求消息的SoapAction都是必须指定的,否则会出错,SoapAction的值可以从服务的wsdl文档中查
2、部署碰到的问题
错误描述:本地测试通过,部署到服务器上出错
1) 报不认SOAPConstants.SOAP_1_2_PROTOCOL常量,改成MF.newInstance()通过,可能和2)问题的原因一样
2) 我想用的是Javax.xml.soap包下的类,但是似乎在服务器上执行都被识别指向成了org.apache.axis下的一些类而报异常,发现服务器以前有部署axis,把相关的包从服务器上删掉,并拷入sun的saaj相关的几个包,问题解决,网络上说jdk1.5以上直接支持saaj,是不是不要拷sun的saaj包可以直接用?
3、返回Soap消息处理
错误描述:测试时发现,返回的Soap消息,业务标签的尖括号都被转义,类似于:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetUserByUnitResponse xmlns="http://aaaa/aa">
<GetUserByUnitResult>
<Users>
<User>
<Id>00001</Id>
<Name>admin</Name>
</User>
……
<Users>
</GetUserByUnitResult>
</GetUserByUnitResponse>
</soap:Body>
</soap:Envelope>
这样我无法解析xml,业务数据都被作为一整个文本内容了,交涉很久,接口厂家称他那头没有问题,我也找不出这样的原因所在(我还是认为问题出在他那头(.net),那位高手指点下?),只能认命,先处理标签再用dom解析
处理代码
/**
* 转换Soap消息,因返回的Soap内部业务标签都被转义,需进行处理
* @param smsg SoapMessage对象
* @param tempfilepath 临时文件名称
* @return Document 处理后的Soap消息的Document对象
* @return
*/
public static Document TransformSoapmsg(SOAPMessage smsg,
String tempfilepath) {
Document document = null;
try {
File file = new File(tempfilepath);
OutputStream out = new FileOutputStream(file);
smsg.writeTo(out);
out.flush();
out.close();
FileInputStream reader = new FileInputStream(file);
InputStreamReader inr = new InputStreamReader(reader, "utf8");
BufferedReader br = new BufferedReader(inr);
String s1 = null;
//逐行替换
String regEx = "<";
String regEx2 = ">";
Pattern p = Pattern.compile(regEx);
Pattern p2 = Pattern.compile(regEx2);
String Strall = "";
while ((s1 = br.readLine()) != null) {
Matcher m = p.matcher(s1);
String s = m.replaceAll("<");
Matcher m2 = p2.matcher(s);
String s2 = m2.replaceAll(">");
Strall += s2 + "/r/n";
}
br.close();
reader.close();
// 打开源文件,将修改后的字符串写回去
OutputStreamWriter outr = new OutputStreamWriter(
new FileOutputStream(tempfilepath), "utf8");
BufferedWriter bw = new BufferedWriter(outr);
bw.write(Strall);
bw.flush();
bw.close();
DocumentBuilderFactory factory = DocumentBuilderFactory
.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.parse(new File(tempfilepath));
} catch (Exception e) {
e.printStackTrace();
}
return document;
}
4、编码处理
问题描述:本地用户接口部分用户无法注册成功,经检查,只支持GB2312字符集,需要剔除用户名、部门名中的不支持字符。
/**
* 删除字符串中非GB2312字符
* @param sourceStr 源字符串
* @return 仅由GB2312字符组成的字符串
*/
public static String delUnsupporGBKStr(String sourceStr) {
String ret = "";
String tmp = "";
int k = 0;
try {
for (int i = 0; i < sourceStr.length(); i++) {
//
tmp = bytesToHexString(sourceStr.substring(i, i + 1).getBytes("gbk"));
k = Integer.parseInt(tmp,16);
if (k < Integer.parseInt("7E", 16)) {
ret += sourceStr.substring(i, i + 1);
} else {
int c1 = Integer.parseInt(tmp.substring(0,2),16);
int c2 = Integer.parseInt(tmp.substring(2),16);
if (c1 >= Integer.parseInt("A1", 16)
&& c1 < Integer.parseInt("F7", 16)
&& c2 >= Integer.parseInt("A0", 16)
&& c2 < Integer.parseInt("FF", 16)) {
ret += sourceStr.substring(i, i + 1);
} else {
System.out.println(sourceStr.substring(i, i + 1));
}
}
}
} catch (Exception e) {
System.out.println(e);
}
return ret;
}
/**
* 字节数组转化为16进制数文本
* @param bArray 字节数组
* @return 16进制数文本 eg:“F0F2”
*/
public static final String bytesToHexString(byte[] bArray) {
StringBuffer sb = new StringBuffer(bArray.length);
String sTemp;
for (int i = 0; i < bArray.length; i++) {
sTemp = Integer.toHexString(0xFF & bArray[i]);
if (sTemp.length() < 2)
sb.append(0);
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}