WebService Interface? 什么东西? 为什么要这个玩意儿,它有什么用?
有这样一种情况: 我有一个Client程序,要引用到多个Web Service,这一些Web Service的调用方式是一样的,只是各自Web Service本身的实现有一些区别.那么,对于Client来说,最好的调用方式就是这样的:
IObj obj
=
new
WebService1();
obj.Invoke();
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
obj
=
new
WebService2();
obj.Invoke();
即通常说到的基于Interface的编程...
但是Web Service的实现和普通的Interface又有一些不同.下面是一个例子,记录了如何使用Web Service Interface.
1. 创建一个接口 ICalculate ,加上WebServiceBinding这个Attribute,指明这个Web Service的Name和Namespace:
[WebServiceBinding(ConformsTo
=
WsiProfiles.BasicProfile1_1,Name
=
"
ICalculate
"
,Namespace
=
"
http://dev.robinzhong.com/ICalculate/
"
)]
public
interface
ICalculate
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
{
[WebMethod]
int Add(int a, int b);
[WebMethod]
int Sub(int a, int b);
}
2. 创建一个实现了上述接口的类 XCalculate ,代码如下:
[WebService(Namespace
=
"
http://tempuri.org/XCalculate/
"
)]
[WebServiceBinding(ConformsTo
=
WsiProfiles.BasicProfile1_1,Name
=
"
ICalculate
"
,Namespace
=
"
http://dev.robinzhong.com/ICalculate/
"
)]
[ToolboxItem(
false
)]
public
class
XCalculate : System.Web.Services.WebService,ICalculate
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
{
public int Add( int a, int b )
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
throw new NotImplementedException( );
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
public int Sub( int a, int b )
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
throw new NotImplementedException( );
}
}
这个地方,不用实现的代码.写这两个东西的目的,是为了得到WSDL的定义,注:仅仅只是此SOAP消息的定义,和具体的Service无关.
访问此Web Service地址,得到其WSLD文件 ( http://localhost:2839/XCalculate.asmx?wsdl ).删除 <wsdl :service />节点.
得到下面的WSDL文件:
<
?xml
version
="1.0"
encoding
="utf-8"
?
>
<
wsdl
:definitions xmlns:soap
="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tm
="http://microsoft.com/wsdl/mime/textMatching/"
xmlns:soapenc
="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:mime
="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:tns
="http://dev.robinzhong.com/ICalculate/"
xmlns:s
="http://www.w3.org/2001/XMLSchema"
xmlns:soap12
="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:http
="http://schemas.xmlsoap.org/wsdl/http/"
targetNamespace
="http://dev.robinzhong.com/ICalculate/"
xmlns:wsdl
="http://schemas.xmlsoap.org/wsdl/"
>
</
wsdl
><
wsdl
:types
>
<
s
:schema elementFormDefault
="qualified"
targetNamespace
="http://dev.robinzhong.com/ICalculate/"
>
</
s
><
s
:element name
="Add"
>
</
s
><
s
:complexType
>
</
s
><
s
:sequence
>
<
s
:element minOccurs
="1"
maxOccurs
="1"
name
="a"
type
="s:int"
/>
<
s
:element minOccurs
="1"
maxOccurs
="1"
name
="b"
type
="s:int"
/>
</
s
>
<
s
:element name
="AddResponse"
>
</
s
><
s
:complexType
>
</
s
><
s
:sequence
>
<
s
:element minOccurs
="1"
maxOccurs
="1"
name
="AddResult"
type
="s:int"
/>
</
s
>
<
s
:element name
="Sub"
>
</
s
><
s
:complexType
>
</
s
><
s
:sequence
>
<
s
:element minOccurs
="1"
maxOccurs
="1"
name
="a"
type
="s:int"
/>
<
s
:element minOccurs
="1"
maxOccurs
="1"
name
="b"
type
="s:int"
/>
</
s
>
<
s
:element name
="SubResponse"
>
</
s
><
s
:complexType
>
</
s
><
s
:sequence
>
<
s
:element minOccurs
="1"
maxOccurs
="1"
name
="SubResult"
type
="s:int"
/>
</
s
>
</
wsdl
>
<
wsdl
:message name
="AddSoapIn"
>
<
wsdl
:part name
="parameters"
element
="tns:Add"
/>
</
wsdl
>
<
wsdl
:message name
="AddSoapOut"
>
<
wsdl
:part name
="parameters"
element
="tns:AddResponse"
/>
</
wsdl
>
<
wsdl
:message name
="SubSoapIn"
>
<
wsdl
:part name
="parameters"
element
="tns:Sub"
/>
</
wsdl
>
<
wsdl
:message name
="SubSoapOut"
>
<
wsdl
:part name
="parameters"
element
="tns:SubResponse"
/>
</
wsdl
>
<
wsdl
:portType name
="ICalculate"
>
</
wsdl
><
wsdl
:operation name
="Add"
>
<
wsdl
:input message
="tns:AddSoapIn"
/>
<
wsdl
:output message
="tns:AddSoapOut"
/>
</
wsdl
>
<
wsdl
:operation name
="Sub"
>
<
wsdl
:input message
="tns:SubSoapIn"
/>
<
wsdl
:output message
="tns:SubSoapOut"
/>
</
wsdl
>
<
wsdl
:binding name
="ICalculate"
type
="tns:ICalculate"
>
<
soap
:binding transport
="http://schemas.xmlsoap.org/soap/http"
/>
</
wsdl
><
wsdl
:operation name
="Add"
>
<
soap
:operation soapAction
="http://dev.robinzhong.com/ICalculate/Add"
style
="document"
/>
</
wsdl
><
wsdl
:input
>
<
soap
:body use
="literal"
/>
</
wsdl
>
<
wsdl
:output
>
<
soap
:body use
="literal"
/>
</
wsdl
>
<
wsdl
:operation name
="Sub"
>
<
soap
:operation soapAction
="http://dev.robinzhong.com/ICalculate/Sub"
style
="document"
/>
</
wsdl
><
wsdl
:input
>
<
soap
:body use
="literal"
/>
</
wsdl
>
<
wsdl
:output
>
<
soap
:body use
="literal"
/>
</
wsdl
>
<
wsdl
:binding name
="ICalculate1"
type
="tns:ICalculate"
>
<
soap12
:binding transport
="http://schemas.xmlsoap.org/soap/http"
/>
</
wsdl
><
wsdl
:operation name
="Add"
>
<
soap12
:operation soapAction
="http://dev.robinzhong.com/ICalculate/Add"
style
="document"
/>
</
wsdl
><
wsdl
:input
>
<
soap12
:body use
="literal"
/>
</
wsdl
>
<
wsdl
:output
>
<
soap12
:body use
="literal"
/>
</
wsdl
>
<
wsdl
:operation name
="Sub"
>
<
soap12
:operation soapAction
="http://dev.robinzhong.com/ICalculate/Sub"
style
="document"
/>
</
wsdl
><
wsdl
:input
>
<
soap12
:body use
="literal"
/>
</
wsdl
>
<
wsdl
:output
>
<
soap12
:body use
="literal"
/>
</
wsdl
>
得到了Soap消息的定义和结构,我们就可以用wsdl.exe来生成代码,开始实际的编程工作了.
3. 调用 wsdl.exe /l:cs /n:xxx /out:xxx.cs /si icalculate.wsdl ,注意这个/si,完整的参数是 /serverInterface.
生成的代码如下:
[System.CodeDom.Compiler.GeneratedCodeAttribute(
"
wsdl
"
,
"
2.0.50727.42
"
)]
[System.Web.Services.WebServiceBindingAttribute(Name
=
"
ICalculate
"
, Namespace
=
"
http://dev.robinzhong.com/ICalculate/
"
)]
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
public
interface
IICalculate
{
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
/**//// <remarks />
[System.Web.Services.WebMethodAttribute()]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://dev.robinzhong.com/ICalculate/Add", RequestNamespace="http://dev.robinzhong.com/ICalculate/", ResponseNamespace="http://dev.robinzhong.com/ICalculate/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
int Add(int a, int b);
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
/**//// <remarks />
[System.Web.Services.WebMethodAttribute()]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://dev.robinzhong.com/ICalculate/Sub", RequestNamespace="http://dev.robinzhong.com/ICalculate/", ResponseNamespace="http://dev.robinzhong.com/ICalculate/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
int Sub(int a, int b);
}
注意: 生成的是一个Interface,不是具体的类.这个Interface,就是我所谓的Web Service Interface (其实实质就是WSDL定义).这个Interface的定义和前面定义的ICalculate,除了多一些Attribute外,其它的一模一样.这些个Attribute就是最大的区别,用来定义WebService调用时接收/发送的Soap消息.
4. 即然Interface都出来了.那么这个时候可以写真正的Web Service了,以下是两个Web Service的示例代码:
[WebService(Namespace
=
"
http://tempuri.org/
"
)]
[WebServiceBinding(ConformsTo
=
WsiProfiles.BasicProfile1_1,Namespace
=
"
http://dev.robinzhong.com/ICalculate/
"
,Name
=
"
ICalculate
"
,Location
=
"
http://localhost/WSInterface/ICalculate.wsdl
"
)]
[ToolboxItem(
false
)]
public
class
NewCalculate : System.Web.Services.WebService ,IICalculate
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
{
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
[WebMethod]
public int Add( int a, int b )
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
return a + b;
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
[WebMethod]
public int Sub( int a, int b )
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
return a - b;
}
}
[WebService(Namespace
=
"
http://tempuri.org/
"
)]
[WebServiceBinding(ConformsTo
=
WsiProfiles.BasicProfile1_1,Name
=
"
ICalculate
"
,Namespace
=
"
http://dev.robinzhong.com/ICalculate/
"
,Location
=
"
http://localhost/WSInterface/ICalculate.wsdl
"
)]
[ToolboxItem(
false
)]
public
class
SimpleCalculate : System.Web.Services.WebService, IICalculate
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
{
public int Add( int a, int b )
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
return a + b;
}
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
public int Sub( int a, int b )
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
{
return a - b;
}
}
注意WebServiceBinding这个Attribute,在这两个Web Service类中,都使用了这个Attribute,而且设置其Name="ICalculate", Namespace="http://dev.robinzhong.com/ICalculate"... <font color=red>(注:由于IICalculate接口已定义了WebServiceBindingAttribute,所以在其继承的类中也不必定义此Attribute,ASP.NET 2.0测试通过.)</font>
5. Web Service已写好,下面就是Client的调用代码.同样的,也得先生成IICalculate这个接口,同样的方法.
wsdl.exe /l:cs /n:xxx /out:xxx.cs /si icalculate.wsdl
得到IICalcuate接口.
然后再添加上面两个Web Service中任意一个WebService的引用.比如NewCalculate这个Web Service,得到如下代码:
[System.CodeDom.Compiler.GeneratedCodeAttribute(
"
System.Web.Services
"
,
"
2.0.50727.42
"
)]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute(
"
code
"
)]
[System.Web.Services.WebServiceBindingAttribute(Name
=
"
ICalculate
"
, Namespace
=
"
http://dev.robinzhong.com/ICalculate/
"
)]
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
public
partial
class
CalculateProxy
: System.Web.Services.Protocols.SoapHttpClientProtocol
{
private System.Threading.SendOrPostCallback AddOperationCompleted;
private System.Threading.SendOrPostCallback SubOperationCompleted;
private bool useDefaultCredentialsSetExplicitly;
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/b854634c0904529d4018c4c3336be836.gif)
修改此类,让其从IICalculate继承.然后修改构造函数,让其通过构造函数得到Web Service的Url.如下:
[System.CodeDom.Compiler.GeneratedCodeAttribute(
"
System.Web.Services
"
,
"
2.0.50727.42
"
)]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute(
"
code
"
)]
[System.Web.Services.WebServiceBindingAttribute(Name
=
"
ICalculate
"
, Namespace
=
"
http://dev.robinzhong.com/ICalculate/
"
)]
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
public
partial
class
CalculateProxy
: System.Web.Services.Protocols.SoapHttpClientProtocol,IICalculate
{
private System.Threading.SendOrPostCallback AddOperationCompleted;
private System.Threading.SendOrPostCallback SubOperationCompleted;
private bool useDefaultCredentialsSetExplicitly;
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
/**//// <remarks/>
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
public
CalculateProxy
(string url)
{
this.Url = url;
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
if ((this.IsLocalFileSystemWebService(this.Url) == true))
{
this.UseDefaultCredentials = true;
this.useDefaultCredentialsSetExplicitly = false;
}
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
else
{
this.useDefaultCredentialsSetExplicitly = true;
}
}
6. 好了,可以开始测试我们的代码了:
IICalculate calculate;
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
calculate
=
new
CalculateProxy(
"
http://localhost:2935/SimpleCalculate.asmx
"
);
Console.WriteLine(
"
calculate.Add( 10, 345 ) =
"
+
calculate.Add(
10
,
345
) );
Console.WriteLine(
"
calculate.Sub( 3455, 234 ) =
"
+
calculate.Sub(
3455
,
234
) );
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
calculate
=
new
CalculateProxy(
"
http://localhost:2935/NewCalculate.asmx
"
);
Console.WriteLine(
"
calculate.Add( 10, 345 ) =
"
+
calculate.Add(
10
,
345
) );
Console.WriteLine(
"
calculate.Sub( 3455, 234 ) =
"
+
calculate.Sub(
3455
,
234
) );
这样就达到了Web Service Interface的目的了--我不管是那里的Web Service,反正只要实现了上面的接口,给我正确的url地址,我就可以调用...
其实,我们可以用更幽雅的方式实现: Contract First . Web Service不是RPC,它只传输数据,你只要定义发送方和接收方的消息格式就够了.
推荐大家看看 :
http://www.thinktecture.com/Resources/Software/WSContractFirst/default.html
http://msdn.microsoft.com/msdnmag/issues/05/05/ServiceStation/
SOA ?