Web服务协议的发展已从支持具有简单参数的非常简单的请求发展为完全支持现代的面向对象的语言。 XML-RPC可以说是Web服务的最早形式之一,它仅支持简单类型-字符串,整数,布尔值等。 SOAP通过其对象编码规则进一步迈出了这一步。 最后一步-改进二进制文件- 带有附件的SOAP来了。
带有附件的SOAP最初是作为SOAP 1.1的扩展引入的,并且主要的SOAP套件都支持它。 尽管SOAP 1.2(W3C官方发行版)尚不支持附件,但仍在(不久的将来)将它们包括在内的工作正在进行中。
Web服务和二进制数据
我毫不怀疑XML在应用程序集成方面的成功来自对文本编码的依赖(这与二进制协议(例如CORBA,面向对象的RPC标准或RMI,Java特定的RPC标准)相对)。 文本编码是可取的,这有几个原因,其中最关键的可能是它易于调试,并且在需要时更容易汇总特殊的实现。
尽管如此,对文本编码的依赖仍然是阴暗的一面,XML无法提供包含二进制数据的有效解决方案。 根据W3C XML Schema规范,二进制数据应以64或16进制编码。 不幸的是,64位编码的数据比未编码的数据大50%。 十六进制编码使大小加倍。 对于少量的二进制数据,此开销是可以接受的,但是对于较大的数据集,显然是个问题。
二进制数据在许多应用程序中很有用。 例如:
- 安全应用程序需要密钥,哈希,证书和加密数据本身。
- 多媒体应用程序可处理照片,音乐和电影。
- 在某些应用程序中,数据的XML表示被认为效率太低-想到了CAD / CAM。
- 成千上万种文件格式早于XML:文字处理,电子表格,字体,矢量图形,家谱等。
尽管可以创建这些文件格式的XML版本(类似于矢量图形的SVG),但是二进制数据已经存在了很长时间,并且可能会继续流行。
最后,还有XML本身的问题! 在另一个XML文档中包含一个XML文档并非易事(语法正确的解决方案依赖于CDATA部分和字符转义)。
为了满足所有这些应用程序的需求,Web服务必须有效地支持二进制数据。 所提出的解决方案是带有附件的SOAP,概括地说,该附件从XML有效负载中除去二进制信息,并将其作为multipart/related
MIME内容直接存储在HTTP请求中。
在设计适用于二进制数据的Web服务时,您的选择是:
- 如果数据集很小,则可以考虑在XML有效载荷内使用base 64编码; 对于小型数据集,开销较少。
- 如果数据集更大,则附件是唯一可行的选择。
清单1是带有基本64编码参数的SOAP请求。 注意address
元素。
清单1.基本的64编码参数
POST /ws/retrieve HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml multipart/related, text/*
Host: localhost:8080
SOAPAction: ""
Content-Length: 540
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ps:retrieve
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ps="http://psol.com/2004/ws/retrieve">
<address xsi:type="xsd:base64Binary">d3d3Lm1hcmNoYWwuY29t</address>
</ps:retrieve>
</soapenv:Body>
</soapenv:Envelope>
实施附件
Java开发人员可以通过JAX-RPC(基于XML的RPC的Java API)和SAAJ(带有Java的带有附件API的SOAP)获得附件。 不要让SAAJ的首字母缩写欺骗您:JAX-RPC支持附件( 有关示例,请参阅参考资料 )。 JAX-RPC和SAAJ之间的区别在于抽象级别,而不是功能。
JAX-RPC是比SAAJ更抽象的高级API。 它在RMI层后面隐藏了SOAP的大多数面向协议的方面。 开发人员使用Java对象,预处理器将它们转换为SOAP节点。 JAX-RPC使用java.awt.Image
和javax.activation.DataHandler
类来表示附件。
SAAJ更接近该协议。 使用SAAJ创建SOAP消息比使用JAX-RPC花费更多的工作(此外,它不提供到WSDL的自动链接),因此在大多数情况下,您将需要使用JAX-RPC。 SAAJ的低级方面仍然使其更适合说明附件的实际工作方式。 清单2是带有附件的SOAP请求。 该请求要求服务器调整照片大小; 因为照片文件很大,所以附件的效率更高。
清单2.附件参数
POST /ws/resize HTTP/1.0
Content-Type: multipart/related; type="text/xml";
start="<EB6FC7EDE9EF4E510F641C481A9FF1F3>";
boundary="----=_Part_0_7145370.1075485514903"
Accept: application/soap+xml, multipart/related, text/*
Host: localhost:8080
SOAPAction: ""
Content-Length: 1506005
------=_Part_0_7145370.1075485514903
Content-Type: text/xml; charset=UTF-8
Content-Transfer-Encoding: binary
Content-Id: <EB6FC7EDE9EF4E510F641C481A9FF1F3>
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ps:resize
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ps="http://psol.com/2004/ws/resize"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<source href="cid:E1A97E9D40359F85CA19D1B8A7C52AA3"/>
<percent>20</percent>
</ps:resize>
</soapenv:Body>
</soapenv:Envelope>
------=_Part_0_7145370.1075485514903
Content-Type: image/jpeg
Content-Transfer-Encoding: binary
Content-Id: <E1A97E9D40359F85CA19D1B8A7C52AA3>
note: binary data deleted...
------=_Part_0_7145370.1075485514903--
清单3展示了SOAP请求的创建。 该请求要求服务器调整图像大小。 步骤如下:
- 通过工厂创建SOAP连接和SOAP消息对象。
- 从消息对象检索消息主体(中间步骤:检索SOAP部分和信封)。
- 创建一个新的XML元素来表示请求并设置编码样式。
- 创建附件并使用
DataHandler
对象对其进行初始化。 - 创建更多的元素来表示两个参数(
source
和percent
)。 - 通过添加
href
属性,将附件与第一个参数相关联。 附件通过cid
(内容ID)URL引用。 - 将第二个参数的值直接设置为文本,然后调用服务。
该服务再次将调整后的图像作为附件进行回复。 要检索它,您可以测试SOAP错误(指示错误)。 如果没有故障,请以文件形式检索附件并进行处理。
清单3.使用SAAJ
public File resize(String endPoint,File file)
{
SOAPConnection connection =
SOAPConnectionFactory.newInstance().createConnection();
SOAPMessage message = MessageFactory.newInstance().createMessage();
SOAPPart part = message.getSOAPPart();
SOAPEnvelope envelope = part.getEnvelope();
SOAPBody body = envelope.getBody();
SOAPBodyElement operation =
body.addBodyElement(
envelope.createName("resize",
"ps",
"http://psol.com/2004/ws/resize"));
operation.setEncodingStyle("http://schemas.xmlsoap.org/soap/encoding/");
DataHandler dh = new DataHandler(new FileDataSource(file));
AttachmentPart attachment = message.createAttachmentPart(dh);
SOAPElement source = operation.addChildElement("source",""),
percent = operation.addChildElement("percent","");
message.addAttachmentPart(attachment);
source.addAttribute(envelope.createName("href"),
"cid:" + attachment.getContentId());
width.addTextNode("20");
SOAPMessage result = connection.call(message,endPoint);
part = result.getSOAPPart();
envelope = part.getEnvelope();
body = envelope.getBody();
if(!body.hasFault())
{
Iterator iterator = result.getAttachments();
if(iterator.hasNext())
{
dh = ((AttachmentPart)iterator.next()).getDataHandler();
String fname = dh.getName();
if(null != fname)
return new File(fname);
}
}
return null;
}
请注意, 清单3清楚地表明附件是XML消息之外 ! 这对于提高效率是必要的。
谈到效率,看一看清单4,其示出比较常见(且显着更短)的JAX-RPC版本清单3 。 JAX-RPC预编译器生成一个存根,大大简化了编码。 您将DataHandler
对象作为方法参数传递,并且JAX-RPC自动生成附件。
清单4.更有效的JAX-RPC
public File resize(File file)
throws ServiceException, RemoteException
{
AttachmentService service = new AttachmentServiceLocator();
AttachmentTip port = service.getAttachmentTip(); // get stub
DataHandler dh = new DataHandler(new FileDataSource(file));
DataHandler result = port.resize(dh,20);
return new File(result.getName());
}
结论
选择是好的,而SOAP在处理二进制数据时为您提供了选择:您可以将其编码为XML有效载荷内的base 64,这对于小型数据集而言是好的,或者可以将较大的二进制文件(未编码)附加到请求中。
翻译自: https://www.ibm.com/developerworks/xml/library/x-tippass/index.html