0


java webservice 根据wsdl文件生成客户端代码;webservice可视化测试工具SOAPUI;乱码。

前言

本文主要介绍通过wsdl文件生成java客户端代码以及webservice的可视化测试工具SoapUI;以及解决遇到的乱码问题。

背景

最近要对接HIS系统,对方提供的接口是webservice(这种技术,有点古老),提供了wsdl文件,我方需要根据wsdl文件生成java代码,intellij idea生成webservice客户端代码支持的不是很好,研究得知,可通过wsimport命令来生成,

  • wsimport 根据wsdl文件生成java代码;
  • wsgen 根据endpoint implementation class生成必要的文件,包括但不限于wsdl文件。

这两个命令在

  1. %JAVA_HOME%\bin

下。

根据wsdl生成java代码

  1. wsimport -encoding utf8 -p 包名 -wsdllocation http://somedomain/some/path/some.wsdl wsdl文件路径
  • -encoding,指定生成的java文件的编码
  • -p,指定生成的java包名
  • -wsdllocation,指定生成的java中wsdl的路径
  • wsdl文件路径,通常会将wsdl保存为本地文件

注意的点

@WebServiceClient的类

生成后的文件,先找到一个注解有javax.xml.ws.WebServiceClient的类,这个类对应着wsdl文件中的service元素;

  1. <definitions name=""
  2. xmlns="http://schemas.xmlsoap.org/wsdl/">
  3. <service name="service_name">
  4. </service>

上述xml使用了默认namespace,如果写全,则是

  1. <wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" >
  2. <wsdl:service name="service_name">
  3. </wsdl:service>

以下同理,不再赘述。
这个类通常指定了wsdl的位置及namespaceURI,

  1. import javax.xml.ws.Service;
  2. /**
  3. *
  4. * 通常会有如下注释以表明是自动生成
  5. * This class was generated by the JAX-WS RI.
  6. * JAX-WS RI 2.2.9-b130926.1035
  7. * Generated source version: 2.2
  8. *
  9. */
  10. @WebServiceClient(name = "SomeService", targetNamespace = "http://some.domain", wsdlLocation = "http://domain.com/path/some.wsdl")
  11. public class SomeService
  12. extends Service
  13. {
  14. private final static URL PREFIX_WSDL_LOCATION;
  15. static {
  16. URL url = null;
  17. WebServiceException e = null;
  18. try {
  19. //这里是WSDL文件的地址,真正的通信地址在soap:address元素的location属性中
  20. url = new URL("http://domain.com/path/some.wsdl");
  21. } catch (MalformedURLException ex) {
  22. e = new WebServiceException(ex);
  23. }
  24. PREFIX_WSDL_LOCATION= url;
  25. }
  26. }

@WebService的类

找到注解有 javax.jws.WebService的类,这个类对应着wsdl文件中的portType元素。

  1. <portType name="SomePortType">
  2. <operation name="operation_name">
  3. <documentation>Service definition of function ns__operation_name</documentation>
  4. <input message="输入参数"/>
  5. <output message="输出参数"/>
  6. </operation>
  7. </portType>

这个类中通常会定义了很多方法,

  1. @WebService(name = "SomePortType", targetNamespace = "http://some.domain")
  2. @XmlSeeAlso({
  3. com.package.ObjectFactory.class
  4. })
  5. public interface SomePortType {
  6. @WebMethod(operationName = "someOperation")
  7. @WebResult(name = "someResult", targetNamespace = "")
  8. //省略
  9. public String someOperation(
  10. @WebParam(name = "输入参数1", targetNamespace = "")
  11. String someInput,
  12. @WebParam(name = "输入参数2", targetNamespace = "")
  13. String someInput2);

ObjectFactory

还有一个ObjectFactory类,但

  1. import javax.xml.bind.annotation.XmlRegistry;
  2. @XmlRegistry
  3. public class ObjectFactory {}

基本用法

  1. 注解有WebServiceClient的类 someService = new 注解有WebServiceClient的类;
  2. final SomePortType somePortType = someService.getSome();
  3. final String 输出参数 = somePortType.目标方法(输入参数)

好用的SOAP ui测试工具

我们先直观的感受下wsdl文件,wsdl文件用来描述一个webservice提供了哪些服务;比如天气webservice,

  1. http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?wsdl

,浏览器中打开如下
在这里插入图片描述
最重要的是

  1. wsdl:service

节点,提供了4个端口,2个webservice(SoapUI中创建SOAP project可以看到)、2个http的(SoapUI中创建rest project可以看到)。

  1. soap:address

元素中的location属性才是真正的服务地址。

  1. <wsdl:service name="WeatherWebService">
  2. <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">.....</wsdl:documentation>
  3. <wsdl:port name="WeatherWebServiceSoap" binding="tns:WeatherWebServiceSoap">
  4. <soap:address location="http://www.webxml.com.cn/WebServices/WeatherWebService.asmx"/>
  5. </wsdl:port>
  6. <wsdl:port name="WeatherWebServiceSoap12" binding="tns:WeatherWebServiceSoap12">
  7. <soap12:address location="http://www.webxml.com.cn/WebServices/WeatherWebService.asmx"/>
  8. </wsdl:port>
  9. <wsdl:port name="WeatherWebServiceHttpGet" binding="tns:WeatherWebServiceHttpGet">
  10. <http:address location="http://www.webxml.com.cn/WebServices/WeatherWebService.asmx"/>
  11. </wsdl:port>
  12. <wsdl:port name="WeatherWebServiceHttpPost" binding="tns:WeatherWebServiceHttpPost">
  13. <http:address location="http://www.webxml.com.cn/WebServices/WeatherWebService.asmx"/>
  14. </wsdl:port>
  15. </wsdl:service>

就像postman是http的ui客户端,SoapUI是web service的ui客户端,用来测试非常方便,注意下载开源版。
在这里插入图片描述
新建SOAP project,输入wsdl地址,

  1. http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?wsdl


在这里插入图片描述
点击绿色左右箭头,能看到定义服务的URI和SOAP版本等信息,WeatherWebServiceSoap是SOAP 1.1版本,WeatherWebServiceSoap12是SOAP 1.2版本。
在这里插入图片描述

双击Request 1,点击绿色箭头就可以执行了,右侧是执行结果。
在这里插入图片描述

webservice仍然是使用的http来通信的,只是发送的内容遵循SOAP格式要求,这一点可通过http log查看。
在这里插入图片描述
http的请求体,请求体都封装在Envelope元素内,包含header和body两部分;

  1. <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://WebXml.com.cn/">
  2. <soapenv:Header/>
  3. <soapenv:Body>
  4. <web:getSupportCity>
  5. <!--Optional:-->
  6. <web:byProvinceName>?</web:byProvinceName>
  7. </web:getSupportCity>
  8. </soapenv:Body>
  9. </soapenv:Envelope>

http响应体,响应也是封装在Envelope元素内,包含header和body两部分;

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <soap:envelope
  3. xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  6. <soap:body>
  7. <getsupportcityresponse
  8. xmlns="http://WebXml.com.cn/">
  9. <getsupportcityresult>
  10. <string>........</string>
  11. </getsupportcityresult>
  12. </getsupportcityresponse>
  13. </soap:body>
  14. </soap:envelope>

问题

在用java调用ASP.Net提供的webservice时,遇到了中文全部乱码、英文字母和数字正常的问题,可能是我方在处理响应时,使用的

  1. ISO_8859_1

解码的字节流,因为

  1. ISO_8859_1

只有字母、数字,没有汉字,符合中文全部乱码、英文字母和数字正常这个现象;将已经乱码的字符串使用

  1. ISO_8859_1

编码为乱码前的字节流,然后再使用

  1. UTF_8

解码就好了。从中可以推断,接收到的字节流是utf8编码的,只是在字节流转字符串时使用了错误的编码解码导致了乱码。

  1. import java.nio.charset.StandardCharsets;
  2. /**
  3. * webservice 返回的编码是iso-8859-1,需要转换成utf-8
  4. * @param str
  5. * @return
  6. */
  7. private static String convertEncode(String str){
  8. return new String(str.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
  9. }

在解决上述问题时,看到了汉字奇数位乱码相关的帖子,经参考最后一个奇数汉字出现乱码解决方案、关于Java奇数最后一个字符输出乱码问题,懂了问题产生的原因

  1. final byte[] utf8s = "你好啊".getBytes("utf8");
  2. final String gbk = new String(utf8s, "gbk");
  3. System.out.println(gbk);
  4. final byte[] gbks = gbk.getBytes("gbk");
  5. for (byte utf8 : utf8s) {
  6. //输出十六进制
  7. System.out.printf("%02X ", utf8);
  8. }
  9. System.out.println();
  10. for (byte gbkEle : gbks) {
  11. System.out.printf("%02X ", gbkEle);
  12. }
  13. System.out.println()
  14. System.out.print(new String(gbks, "utf8"));

最后一个字节发生变化,变为3F

  1. 浣犲ソ鍟�
  2. E4 BD A0 E5 A5 BD E5 95 8A
  3. E4 BD A0 E5 A5 BD E5 95 3F
  4. 你好�?

当“你好啊”替换为“一二三”时,结果如下,第3个字节变为3F

  1. 涓�浜屼笁
  2. E4 B8 80 E4 BA 8C E4 B8 89
  3. E4 B8 3F E4 BA 8C E4 B8 89
  4. �?二三

也就是说,utf8字节流以gbk(通常将2个字节作为一个汉字处理)解码为字符串时,因为编解码差异会导致有些字符找不到,然后就会被替换为问号(ascii字符集的3F),字节流已经发生变化,再以utf8解码时(utf8中,通常3个字节当作1个汉字处理),就产生了乱码。

总结

参考过java对接webservice接口的4种方式总结,个人觉得还是使用wsimport生成java客户端的方式比较简单,不需要添加依赖,而且是面向java编程;使用httpclient发送SOAP消息体倒是简单直接,就是需要拼接xml有点繁琐。

标签: java 开发语言

本文转载自: https://blog.csdn.net/wangjun5159/article/details/140183648
版权归原作者 wangjun5159 所有, 如有侵权,请联系我们删除。

“java webservice 根据wsdl文件生成客户端代码;webservice可视化测试工具SOAPUI;乱码。”的评论:

还没有评论