学习.NET的开发人员,在WCF的光芒照耀下,Web Service 似乎快要被人遗忘了。因为身边做技术的人一开口就是WCF多么的牛逼!废话不多,本人很久不写博客,今天总结一下最近几日遇到的几个关于WebService 跨语言的调用问题。

1 WebService 简介

Web Service也叫XML Web Service WebService是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求,轻量级的独立的通讯技术。是:通过SOAP在Web上提供的软件服务,使用WSDL文件进行说明,并通过UDDI进行注册。


Web Service的简介就说这么多,大家都懂的,网上的资料太多了,搞过Web Service编程的人员都知道它的优点,没有搞过WS编程的人员也知道它的优点,这里不再累述。

参考文章: ​​http://baike.baidu.com/view/837392.htm​



2 SOAP 消息结构

要学习WebService 肯定要了解Soap协议,它是WebService的基础。


SOAP(Simple Object Access Protocol)简单对象访问协议,它是基于XML的消息传递协议,可以让软件组件和基于服务的应用程序能够使用标准的HTTP协议进行通信(SOAP是基于HTTP之上的)。

SOAP的消息文档是一个XML格式的。相信大家都看到过:

WCF光芒下的Web Service_自定义

SOAP主要是由4部分组成:

SOAP Envelope

这是XML的顶层元素,语法如下:



(1)元素名为Envelope,必须存在,且为根元素。

(2)该元素可以包含命名空间和生命额外的属性。如果出现额外属性,则必须使用命名空间修饰。


(3 )该元素可以包含额外的子元素,如果使用这些子元素,必须有命名空间修饰并且必须跟在SOAP Body 元素之后,也就是说Envelope 的直接子元素Header 和Body 必须排列在最前面。

SOAP Header

Header应该是Envelope中的第一个子元素,为可选的,语法规则如下:



(1)元素名为Header,不是必须存在,但如果存在则必须是SOAP Envelope的第一个直接子元素。

(2)Header的所有直接子元素都是它的Item,每个Itemdoiu必须有命名空间修饰。

(3)Header的Item也可以包含下级子元素,但这些子元素不是Header的Item,而是具体Item的内容。

此外,SOAP encodingStyle属性用于指定Header条目的编码风格,mustUnderstand属性和actor属性用于指定如何处理Item和由谁来处理。如:

<ENV:Header>

<uniB2B:Priority MXLns:uniB2B="some-URI"

ENV:mustUnderstand="1">7</uniB2B:Priority>

</ENV:Header>



SOAP Body

SOAP Body元素提供一个简单的用于消息的最终接收者交换信息的机制。其语法如下:



(1)元素名为Body,必须在SOAP消息中出现,同时必须是SOAP Envelope的直接子元素,若没有Header,则Body必须是第一个直接子元素;若有Header,则Body必须紧跟Header元素存在。

(2)与Header类似,每个Body的Item都必须由命名空间修饰。此外,Body中有个SOAP Fault元素,用于指示调用错误信息。


( 3 ) Body 的 Item 下的子元素不是 Body 的 Item 了,而是 Item 的内容



SOAP Fault

用于在SOAP消息中传输错误及状态信息。如果存在则必须是Body的一个Item,且Body中只能出现一次Fault。SOAP Fault元素有以下几个子元素:



(1) faultcode 必须在SOAP Fault元素中出现

(2)faultstring 该元素是为那些错误代码提供用户可以读懂的错误解释,它不是为程序处理而设置的。

(3)faultactor 该元素描述在消息路径中错误的引发者,它类似于SOAP actor属性,不过它不是指示Header条目的接收者,而是指示错误源。


(4)tail 该元素用于传输 Body 元素相关的应用程序的错误消息

3 WebService 5种模式

请求响应模式

fire-and-forget模式

高级消息模式

增量解析和处理模式

缓存模式

4 WebService 自定义SoapHeader安全验证

SOAP 标头提供了一种方法,用于将数据传递到 XML Web services 方法或从 XML Web services 方法传递数据,条件是该数据不直接与 XML Web services 方法的主功能相关。例如,一个 XML Web services 可能包含若干个 XML Web services 方法,而每个方法都需要自定义的身份验证方案。您不用将参数添加到每个需要自定义身份验证方案的 XML Web services 方法,而可以将引用从 SoapHeader 派生的类的 ​​SoapHeaderAttribute​​ 应用于每个 XML Web services 方法。从 SoapHeader 派生的类的实现处理该自定义身份验证方案。按照此方式,XML Web services 方法使用 SOAP 标头来仅实现特定于它的功能并添加其他功能。


下面的列表概述接收和处理 SOAP 标头的基本步骤:

创建一个从 SoapHeader 派生的类,表示传入 SOAP 标头的数据。

将一个成员添加到实现 XML Web services 的类或 XML Web services 客户端代理类(它们属于在第一步创建的类型)。

指定第二步中在 ​​MemberName​​ 属性中创建的成员,将 SoapHeaderAttribute 应用于 XML Web services 方法或代理类中的对应方法。


在 XML Web services 方法或 XML Web services 客户端代码中访问 MemberName 属性,以处理在 SOAP 标头中发送的数据。



验证例子代码:

(1)首先定义一个自定义SoapHeader


WCF光芒下的Web Service_自定义_02



public class MyHeader:SoapHeader
{
public int ID { get; set; }

public string Name { get; set; }

public string PassWord { get; set; }



}



WCF光芒下的Web Service_自定义_02


(2) 在WebService暴露的方法中添加SoapHeader描述


WCF光芒下的Web Service_自定义_02



" http://tempuri.org/ ")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem( false)]
public class Info : System.Web.Services.WebService
{
public SoapUnknownHeader[] unknownHeaders;

public MyHeader MyHeader { get; set; }

[WebMethod]
[SoapHeader( " unknownHeaders ")]
public string HelloWorld()
{
Console.WriteLine( " ?>>>>>>>>>>>>>>>>>>>> ");
return " Hello World ";
}


[SoapHeader( " MyHeader ", Direction = SoapHeaderDirection.InOut)]
[WebMethod]
public string Audit()
{
Validate();

return " 这里是验证 ";
}

// 验证函数 自定义
private string Validate()
{
if (MyHeader != null)
{
if (MyHeader.Name == MyHeader.PassWord)
{
return " 验证通过 ";
}
else
{
return " 验证失败 ";
}
}
return " 未传递消息头 ";
}



}



WCF光芒下的Web Service_自定义_02


这里重点看的是服务暴露出来的方法public string

在WebService中的类定义一个属性 public MyHeader MyHeader { get; set; } 就是我们自定义的SOAPHeader,同时还要在Audit()方法上加上如下描述:

[SoapHeader("MyHeader", Direction = SoapHeaderDirection.InOut)]

如果客户端传递了相应的SoapHeader就会使用此属性来接收消息,主要特性描述中的参数要指向定义的属性名。

(3)客户端生存代理

客户端生成代理大家都知道,工程--右键--添加Web引用即可,现在我们看看里面生成的代码

WCF光芒下的Web Service_客户端_06

MyHeader客户端生成的代理类

代理类中生存对应的属性,同时此类也是继承了SoapHeader类。


WCF光芒下的Web Service_自定义_02


" MyHeaderValue ", Direction=System.Web.Services.Protocols.SoapHeaderDirection.InOut)]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute( " http://tempuri.org/Audit ", RequestNamespace= " http://tempuri.org/ ", ResponseNamespace= " http://tempuri.org/ ", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public string Audit() {
object[] results = this.Invoke( " Audit ", new object[ 0]);
return (( string)(results[ 0]));


}


public MyHeader MyHeaderValue {


get {


return this.myHeaderValueField;


}


set {


this.myHeaderValueField = value;


}


}


WCF光芒下的Web Service_自定义_02



(4)客户端调用


WCF光芒下的Web Service_自定义_02


new MyService.Info();
MyService.MyHeader myHeader = new MyService.MyHeader();
myHeader.ID = 1;
myHeader.Name = " qingyuan ";
myHeader.PassWord = " qingyuan ";
info.MyHeaderValue = myHeader;
string


Response.Write(content);


WCF光芒下的Web Service_自定义_02


5 异语言之间的调用

在我们的工作中,很多时候遇到这样的问题,有人说Java不能调用.NET 写WebService。对他们没有撒谎,那WebService跨平台岂不是笑话,其实不是这样的,这个时候我们真正应该研究的是SOAP的自己个组成部分,我们可以使用WSDL来查看他们的不同之处。

.NET生存的WSDL一部分

WCF光芒下的Web Service_java_11

Java生存的WSDL一部分

WCF光芒下的Web Service_客户端_12

这里是同一个SoapHeader生成的WSDL,仔细看看还是有区别的,在java中的缺少一个form元素。 缺少这个元素就导致.NET的序列化机制无法识别这个xml文件。那该怎么办呢。

在.NET的自定义SoapHeader的属性上添加如下描述

[System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Qualified)]

6 .NET中获取匿名SoapHeader 内容

很多时候,我们做事情就和孔雀开屏,当然这不是讽刺啊,题外话!我们喜欢强加一些自己的意愿和想法在里面。


我存在一个WebService的服务端,现在客户端调用这个WebService的时候强制提交了一个SOAPHeader报文,希望WebService能够处理。上面一部分我们可以发现客户端提交一个SOAPHeader的时候服务端有一个承载体,因为我们在服务端的时候就在暴露的方法中用SOAPHeader描述过了,现在就是服务端没有使用自定义SOAPHeader描述,而客户端强制提交了一个SOAPHeader,这样我们怎么处理。

1. 客户端定义一个SOAPHeader自定义对象


WCF光芒下的Web Service_自定义_02


public class MyInfo:SoapHeader
{
public MyInfo()
{
}

public int ID { get; set; }

public string Name { get; set; }


}


WCF光芒下的Web Service_自定义_02


这里是在客户端添加的SoapHeader ,不要在服务端添加此类

2. 客户端代理方法添加SOAPHeader的描述


WCF光芒下的Web Service_自定义_02


/* ***************************************************************** */
public MyInfo MyInfo { get; set; }

[SoapHeader( " MyInfo ", Direction = SoapHeaderDirection.InOut)]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute( " http://tempuri.org/HelloWorld ", RequestNamespace= " http://tempuri.org/ ", ResponseNamespace= " http://tempuri.org/ ", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public string HelloWorld() {
object[] results = this.Invoke( " HelloWorld ", new object[ 0]);
return (( string)(results[ 0]));
}


/*******************************************************************/


WCF光芒下的Web Service_自定义_02


这里和普通的代理方式一样,添加客户端SOAPHeader的描述

3. 服务端添加匿名SOAPHeader的处理


WCF光芒下的Web Service_自定义_02


public

[WebMethod]

[SoapHeader( "

unknownHeaders ")]

public string HelloWorld()


{

Console.WriteLine( "

?>>>>>>>>>>>>>>>>>>>> ");

return "

Hello World ";

}

WCF光芒下的Web Service_自定义_02


服务端需要使用SoapUnknownHeader来接收匿名提交的SoapHeader。因为在服务端没有对应的载体,而在暴露的方法上面也需要添加相应的描述

4. 客户端调用


WCF光芒下的Web Service_自定义_02


new MyService.Info();
MyInfo myInfo = new MyInfo();
myInfo.ID = 1;
myInfo.Name = " dddd ";
info.MyInfo = myInfo;
string


Response.Write(name);


WCF光芒下的Web Service_自定义_02


启动服务端的调试模式跟踪一下看看提交的数据

WCF光芒下的Web Service_java_21

从上图可以看出,数据已经传输过来了,得到的是一个xml格式的数据,对于xml的解析在.NET中不是难事,在此就可以完成匿名SoapHeader报文的发送和接收