关于研究Webservice问题总结与心得体会
一、问题描述分析
在前期我们调用edms系统中的webservice接口时,出现了问题。我们用了JDK Web服务API和第三方框架Axis1(Axis2)以及HttpClient等方式来连接webservice接口,最终均失败。在后台打印日志,错误描述如下:
java.io.IOException:Server returned HTTP response code: 401for URL: https://dms-test.sfchina.bmw.com.cn/FH/FileHold/UserRoleManager/WindowsLogin.asmx/Satsun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1626)
atsun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254) atClientTestTONet2.main(ClientTestTONet2.java:39)
在网上查阅,401错误是没有权限。这个问题困扰了我们很久,我们研究了webservice原理和尝试了多种访问Webservice的方式与写法,最终我们采用前端javascript中拼接SOAP消息的方式发送请求,用new ActiveXObject("Microsoft.XMLHTTP")这个异步对象传递消息;这样的话只要是域用户并已成功登录ARP系统,那么在edms系统中就会验证通过。最终解决了问题。
可是,我们用了两周的时间来解决这个问题,中间着实走了很多很多弯路。现在,回忆解决这一问题一路走来的历程,总结研究的思路。想一想我们研究的过程中那些走了弯路,哪些是当时没有考虑到的,哪些又是我们的想法的有偏差的,做一总结,以便以后遇到类似问题,或者其他的问题,吸取教训,总结经验,少走弯路,更能一步到位。
二、前期研究过程与结果
1.Axis1以及Axis2框架访问
首先,我们采用Axis框架访问webservice接口,关键调用实例如下:
Stringendpoint="https://dms-test.sfchina.bmw.com.cn/FH/FileHold/UserRoleManager/SessionManager.asmx/";
Stringns="http://filehold.com/userrolemanager/sessionmanager/";
StringoperationName = "IsSessionValid";
Serviceservice = new Service();
Callcall = (Call) service.createCall();
call.setTargetEndpointAddress(endpoint);
call.setOperationName(newQName(ns,operationName)); call.addParameter(newQName(ns,"sessionId"),org.apache.axis.encoding.XMLType.XSD_STRING,javax.xml.rpc.ParameterMode.IN);
call.addParameter(newQName(ns,"keepAlive"),org.apache.axis.encoding.XMLType.XSD_BOOLEAN,javax.xml.rpc.ParameterMode.IN);
call.setReturnClass(java.lang.String[].class);
call.setUseSOAPAction(true);
call.setSOAPActionURI(ns+"IsSessionValid");
res=(boolean)call.invoke(new Object[]{"200",true});
结果:出现问题401没有权限,不能访问。
分析:由于我们访问地址是由外网访问的,最终结果为401没有权限访问,可以看到我们访问的地址格式是https,当时我单纯的认为是由于访问https的地址,外网是没有权限。所以,我们又开始研究http和https访问的区别,现在回想起来,其实关键问题不在这里,在这里耽误了时间。不过,我们也很快证实了这一点。
2.Javaweb服务的API实现
使用基于底层java API的SOAP message的Web服务发送消息,关键代码如下:
URLurlTemp=new URL("http….");
URLConnectionconnection = urlTemp.openConnection();
connection.setDoOutput(true);
out =new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
StringBuffersb = new StringBuffer();
sb.append("sessionId=1111&");
sb.append("keepAlive=true");
out.write(sb.toString());
out.flush();
StringsCurrentLine;
sCurrentLine= "";
InputStreaml_urlStream;
l_urlStream= connection.getInputStream();// 请求
BufferedReaderl_reader=newBufferedReader(newInputStreamReader(l_urlStream));
while ((sCurrentLine = l_reader.readLine()) != null) {
sTotalString.append(sCurrentLine);
}
System.out.println(sTotalString);
}
结果:依然报同样的401错误。
分析:由1和2的访问我们得出结论:
1)401权限的问题,关键不在于访问https这种格式,因为我们证实了用这种访问其他https格式的webservie接口,依然能成功。当时我们问题定位有误。耽误了些许时间。
2)由于这两种访问方式一种是由第三方框架Axis,另一种是有JAVA JDK自带的底层访问方式,我们测试过这两种方式访问其他的接口(例如天气遇到接口)均没有问题,可是我们链接edms系统webservice接口,是连不上的,没有权限访问。当时我认为,这两种写法是没有问题的。只要我们解决访问权限问题,并把验证用户的身份信息附到访问访问程序中,一切问题就解决。所以接下来,我们把重点放到权限这里。
3.权限验证
接下来,我们想着如何将身份用户信息提交到服务端,首先我们获得一个域用户,并成功登录ARP系统。接下来,我们用Axis1来访问,并附上登录用户信息,调用call.serUserName()和call.getMessageContext().setUsername(username),可是依然报401错误,后经过沟通知道,在.Net中提供了一个NetworkCredential类,通过它我们可以在网络中提供一个凭证,只有获得该凭证的用户才能访问相应的服务的权限。之后我又到网上查,尝试java.net.Authentucator、org.apach.axis2.transport.http.HttpTransportProperties.Authenticator,这些我们都一一试过,可都无济于事。
其实,问题进展到这一步,我们能想到权限的问题,想到只有ARP的登录用户才能访问Edms的接口,经过查阅API文档我们获取的信息得知,edms系统会记录当前用户的sessionId这一关键信息,如果当初知识储备够丰富的话,我们就该考虑到用浏览器访问,完全用前端JavaScript访问webservice,这样才可以记录当前的用户信息,这样才能做到用户和edms中的联系,而非ARP的服务端与edms的联系。现在回想起来,我们在这里耗费了太多的时间。
4.HttpClient模拟浏览器访问接口。
不过,我们在想到用js访问webservice之前,也在网上了解到使用httpclient4.*的包,用httpClient的方式模拟浏览器访问webservice,并试着在其中附用户权限,关键代码如下:
DefaultHttpClienthttpClient =newDefaultHttpClient();
httpClient.getParams().setParameter(HttpProtocolParams.HTTP_CONTENT_CHARSET,"utf-8");
NTCredentials creds = newNTCredentials(username,password,myWorkStation,"");
我们采用拼soap协议的方式然后发送到webservice,
结果:在调用接口任无结果,报401无法访问。
分析:做到现在,我们已经确认两点:1)我们想要进入edms,必须在ARP中登录,该用户的信息才能通过验证。2)edms能记录用户的session,那么就说明我们用浏览器访问,这样的话,才能起到用户关联edms系统。
三、后期问题分析与结果
根据前期的分析,我们已确定以下两点:
1.根据Edms系统中要求访问者是已登录用户,我们必须在ARP系统中使用域用户单点登录成功,而且在edms系统中必须有该域用户的域信息,这样方可验证成功。(在前期我们联系IT部门已将axi1975用户设为域用户并拥有访问权限)。
2.根据接口API(FileHold)开发说明文档,edms会记录当前已用户的信息sessionid,那么我们只能采用浏览器的方式去请求webservice接口,这样,类似于通过浏览器自动记住当前已登录用户的身份信息,其中包括在后面通过调用webservice返回的sessionId值。
显然,在java类中我们很难将身份信息提交到服务端,即使我们验证通过,那么当前用户的session怎么保存,怎么去控制它的失效时间。所以我们需要在浏览器中,用前端javascript中拼接SOAP消息的方式发送请求,用new ActiveXObject("Microsoft.XMLHTTP")这个异步对象传递消息;这样的话只要是域用户并已成功登录ARP系统,那么在edms系统中就会验证通过。
实例代码:
<script type="text/javascript">
function RequestWebService() {
//这是我们在第一步中创建的Web服务的地址
var URL ="http://sccn0203/FH/FileHold/UserRoleManager/WindowsLogin.asmx";
vardata;
data = '<?xml version="1.0" encoding="utf-8"?>';
data=data+ '<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">';
data = data + '<soap12:Body>';
data = data + '<StartSessionxmlns="http://filehold.com/userrolemanager/windowslogin/">';
data = data + '<clientType>CustomClient</clientType>';
data = data + '</StartSession></soap12:Body>';
data = data + '</soap12:Envelope>';
//创建异步对象
var xmlhttp = newActiveXObject("Microsoft.XMLHTTP");
xmlhttp.Open("POST", URL, false);
xmlhttp.SetRequestHeader("Content-Type","application/soap+xml");
xmlhttp.Send(data);
document.getElementById("data").innerHTML= xmlhttp.responseText;
//window.open('"http://sccn0203/FH/FileHold/WebClient/LoginForm.aspx?sessionId="+xmlhttp.responseText',"_blank");
}
</script>
四、解决问题与学习总结
经过了两周的学习与研究,利用webservice连接edms接口,这一过程我们着实走了很多的弯路,也有一些认识上的错误和理解上的偏差:没有更仔细的研究开发API文档,所以没有能想到用浏览器前端访问webservice;没有更好的拓展自己的知识来解决问题,所以忽视了用JS来访问webservice的方式。这些疏忽和认识的错误,造成了很多时间和人力的浪费。所以,这是一次教训,总结经验,扩展知识面。如果以后遇到类似问题,我们能否少走弯路,甚至一步到位。
五、心得体会
在开发webservice接口这一过程中,由于前期对webservice不够了解,所以刚开始研究其原理,掌握其用法。研究了两周由于权限问题被卡住,几乎没有进展。这也暴露出我的知识储备不够,在研究webservice知识时走马观花,没有系统全面的学习,导致没有想到用jS访问webservice。虽然进展不是很顺利,但最终解决了问题,最重要的是在这一过程中学到了很多。
1.遇到问题,一定要冷静的思考,想到问题点的入口后,继续往下钻,如果有进展,那继续研究;否则,迅速换一思路,寻找另一突破口。
2.在研究新的技术,要将该技术涉及相关的点都要了解到,想想如果自己早了解用JS访问webservice的话,可能就会少走一些弯路。
3.有的时候,询问别人也是一种方法。与别人分享自己的问题,看看别人是怎么想的,说不定你的问题别人也遇到过,他们的建议或许对自己有启发,比自己在那里独自思考可能会更好更快。
4.我们在研究一个问题时,如果自己花费了很多时间与精力在这个事情上,仍然没有解决问题,那么试着换一种思路,拓展知识面寻求其他办法,换一种思维或许会引刃而解。想想如果webservice这个问题继续按照我之前的写法与思路,也许再研究两周也没有什么进展。所以,接下来的学习中开阔知识面也很重要,
4.在编程方面,如果要测试某一功能的写法,在网络资料中找到相关代码demo,先全部Copy到环境中,确定成功执行的情况下,然后再根据自己的情况修改,这样可以减少很多不必要的错误。
5.要仔细阅读开发文档,如果我们仔细的阅读那个API文档,从中早一点发现并推敲上面的信息,或许我们能早一点找到用这一种方式去访问webservice。
6.成功=坚持冲过起步的低谷 + 良好的学习方法+ 勤敲代码的习惯 + 处理问题分析问题解决问题的能力