1.1 基础概念
Web Service是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求,轻量级的独立的通讯技术。通过SOAP在Web上提供的服务,使用WSDL文件进行说明,并通过UDDI进行注册。
XML:(Extensible Markup Language)扩展型可标记语言。面向短期的临时数据处理、面向万维网络,是Soap的基础。
Soap:(Simple Object Access Protocol)简单对象存取协议。是XML Web Service 的通信协议。当用户通过UDDI找到你的WSDL描述文档后,他通过可以SOAP调用你建立的Web服务中的一个或多个操作。SOAP是XML文档形式的调用方法的规范,它可以支持不同的底层接口,像HTTP(S)或者SMTP。
WSDL:(Web Services Description Language) WSDL 文件是一个 XML 文档,用于说明一组 SOAP 消息以及如何交换这些消息。大多数情况下由软件自动生成和使用。
UDDI (Universal Description Discovery and Integration) 是一种根据描述文档来引导系统查找相应服务的机制。UDDI利用SOAP消息机制(标准的XML/HTTP)来发布,编辑,浏览以及查找注册信息。它采用XML格式来封装各种不同类型的数据,并且发送到注册中心或者由注册中心来返回需要的数据。
1.2 Webservice基础实例
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
namespace AspNetWebServiceClient
{
/// <summary>
/// Service1 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。
// [System.Web.Script.Services.ScriptService]
public class Service1 : System.Web.Services.WebService
{
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
[WebMethod]
public int Add(int a, int b)
{
return a + b;
}
[WebMethod]
public int Subtract(int a, int b)
{
return a - b;
}
}
}
直接运行看看效果,
1.3 Webservice中webMethod的各种特性:
属性 |
功能 |
示例 |
BufferResponse |
设置为True时,XML Web服务的响应就保存在内存中,并发送为一个完整的包。如果该属性设置为False,则响应在服务器上构造的同时,会发送给客户机。 |
[WebMethod(BufferResponse=true)] |
CacheDuration |
指定响应在系统的高速缓存中的保存时间(秒),默认值为0,表示禁用高速缓存。把XML Web服务的响应放在高速缓存中,会提高Web服务的性能。 |
[WebMethod(BufferResponse=true,CacheDuration=30)] |
Description |
对在XML Web服务的测试页面上显示的Web Method应用文本的描述。 |
[WebMethod(Description="该方法用于获取一个简单的字符串")] |
EnableSession |
设置为True时,会激活Web Method的会话状态,其默认值为False。 |
[WebMethod(EnableSession=true)] |
MessageName |
给Method指定一个唯一的名称,如果要使用重载的Web Method,则必须指定。 |
[WebMethod(MessageName="Method1")] |
TransactionOption |
为Web Method指定事务的支持,其默认值为Disbled。如果Web Method是启动事务的根对象,Web服务就可以用另一个需要事务处理的WebMethod参与事务处理。其值可以是NotSupported、Supported、Required和RequiresNew。 |
[WebMethod(TransactionOption=System.EnterpriseServices.TransactionOption.Sup |
关于Session的特性见下面,其他的特性的代码实例就不贴了
服务端代码:
public void Login(string userName)
{
if (userName.Length > 0)
{
Session["Name"] = userName;
}
}
[WebMethod(EnableSession = true)]
public string TestSession()
{
string userName = Session["Name"].ToString();
return userName;
}
客户端代码:
proxy.CookieContainer = new System.Net.CookieContainer();
proxy.Login("123");
Label1.Text = proxy.TestSession().ToString();
1.4 WebService的异步开发模式
当用到WebService时,由于服务器处理速度、网络传输速度等各种原因会使一个WebService从请求开始到获得响应结果之间等待一段时间,这时候线程会处于阻塞状态,程序会等待请求结果导致客户端无法进行其他的动作或处理。这时候就需要异步的使用WebService。
{
localhost3.AsyncWebService service = new localhost3.AsyncWebService();
service.HelloWorldCompleted+=new WebClientTest.localhost3.HelloWorldCompletedEventHandler(service_HelloWorldCompleted);
service.HelloWorldAsync("China");
}
void service_HelloWorldCompleted(object sender, WebClientTest.localhost3.HelloWorldCompletedEventArgs e)
{
if (e.Result != null && e.Result.Length > 0)
{
string result= e.Result;
Label3.Text = result;
}
if (e.Error != null)
{
Console.WriteLine(e.Error.Message);
}
}
1.5 webService的验证方式及安全性
1.5.1 使用SOAP头来验证
服务端代码
{
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
public MySoapHeader header;
[SoapHeader("header", Direction = SoapHeaderDirection.In)]
[WebMethod]
public string CheckHeader()
{
if (header == null)
{
throw new SoapHeaderException("认证失败", SoapException.ClientFaultCode);
}
else
{
if (header.Name != "admin" || header.PassWord != "admin")
{
throw new SoapHeaderException("认证失败", SoapException.ClientFaultCode);
}
}
//业务逻辑.
return "Something done";
}
}
public class MySoapHeader : SoapHeader
{
string _name;
string _passWord;
public string Name
{
get { return _name; }
set { _name = value; }
}
public string PassWord
{
get { return _passWord; }
set { _passWord = value; }
}
}
客户端代码:
{
MySoapHeader h = new MySoapHeader();
h.Name = "cqs";
h.PassWord = "123";
localhost1.SOAPWebService service = new SOAPWebService();
service.MySoapHeaderValue = h;
try
{
string retval = service.CheckHeader();
Label1.Text="Return:" + retval;
}
catch (SoapHeaderException soapEx)
{
Label2.Text="Soap Header Exception:" + soapEx.Message;
}
catch (Exception ex)
{
Label3.Text="Exception:" + ex.Message;
}
}
1.6 Webservice 的动态代理
在程序中需要调用WebService时,都是通过“添加Web引用”,让VS.NET环境来为我们生成服务代理,然后调用对应的Web服务。这样是使工作简单了,但是却和提供Web服务的URL、方法名、参数绑定在一起了,这是VS.NET自动为我们生成Web服务代理的限制。如果发布Web服务的URL改变了,则我们需要重新让VS.NET生成代理,并重新编译。动态调用WebService就能解决上述难题,比如我们可以把Web服务的URL保存在配置文件中,这样,当服务URL改变时,只需要修改配置文件就可以了。具体的代码参见网上常见的动态代理类:
///
/// </summary>
public void TestDynamicWebService()
{
try
{
string url = "http://localhost:4640/DynamicWebService.asmx";
string[] args = new string[1];
args[0] = "China";
object result = DynamicWebServiceHelper.InvokeWebService(url, "HelloWorld", args);
this.Label1.Text = result.ToString();
}
catch (Exception ex)
{
Label3.Text = "Exception:" + ex.Message;
}
}
上述的参数具体可以利用配置文件的方式获取,更增加灵活性,但是通过动态代理的方式将会使性能有所降低。
1.7 扩展SOAP
代码
public class SoapExtensionWS : System.Web.Services.WebService
{
public MySoapHeader header;
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
[MyExtensionAttribute]
[SoapHeader("header", Direction = SoapHeaderDirection.In)]
[WebMethod]
public string CheckHeader()
{
//业务逻辑.
return "Something done";
}
}
[AttributeUsage(AttributeTargets.Method)]
public class MyExtensionAttribute : SoapExtensionAttribute
{
int _priority = 1;
public override int Priority
{
get { return _priority; }
set { _priority = value; }
}
public override Type ExtensionType
{
get { return typeof(MyExtension); }
}
}
/// <summary>
///
/// </summary>
public class MyExtension : SoapExtension
{
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public override void ProcessMessage(SoapMessage message)
{
if (message.Stage == SoapMessageStage.AfterDeserialize)//反序列化之后处理
{
bool check = false;
foreach (SoapHeader header in message.Headers)
{
if (header is MySoapHeader)
{
MySoapHeader myHeader = (MySoapHeader)header;
LogEntry log2 = new LogEntry();
log2.Message = myHeader.Name + myHeader.PassWord;
log2.Categories.Add(Category.ExceptionCategory);
log2.Priority = Priority.Highest;
Logger.Write(log2);
if (myHeader.Name == "admin" || myHeader.PassWord == "admin")
{
check = true;
break;
}
}
}
if (!check)
throw new SoapHeaderException("认证失败", SoapException.ClientFaultCode);
}
}
/// <summary>
///
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public override Object GetInitializer(Type type)
{
return GetType();
}
/// <summary>
///
/// </summary>
/// <param name="info"></param>
/// <param name="attribute"></param>
/// <returns></returns>
public override Object GetInitializer(LogicalMethodInfo info, SoapExtensionAttribute attribute)
{
return null;
}
/// <summary>
///
/// </summary>
/// <param name="initializer"></param>
public override void Initialize(Object initializer)
{
}
}
public class MySoapHeader : SoapHeader
{
string _name;
string _passWord;
public string Name
{
get { return _name; }
set { _name = value; }
}
public string PassWord
{
get { return _passWord; }
set { _passWord = value; }
}
}
{
public MySoapHeader header;
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
[MyExtensionAttribute]
[SoapHeader("header", Direction = SoapHeaderDirection.In)]
[WebMethod]
public string CheckHeader()
{
//业务逻辑.
return "Something done";
}
}
[AttributeUsage(AttributeTargets.Method)]
public class MyExtensionAttribute : SoapExtensionAttribute
{
int _priority = 1;
public override int Priority
{
get { return _priority; }
set { _priority = value; }
}
public override Type ExtensionType
{
get { return typeof(MyExtension); }
}
}
/// <summary>
///
/// </summary>
public class MyExtension : SoapExtension
{
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public override void ProcessMessage(SoapMessage message)
{
if (message.Stage == SoapMessageStage.AfterDeserialize)//反序列化之后处理
{
bool check = false;
foreach (SoapHeader header in message.Headers)
{
if (header is MySoapHeader)
{
MySoapHeader myHeader = (MySoapHeader)header;
LogEntry log2 = new LogEntry();
log2.Message = myHeader.Name + myHeader.PassWord;
log2.Categories.Add(Category.ExceptionCategory);
log2.Priority = Priority.Highest;
Logger.Write(log2);
if (myHeader.Name == "admin" || myHeader.PassWord == "admin")
{
check = true;
break;
}
}
}
if (!check)
throw new SoapHeaderException("认证失败", SoapException.ClientFaultCode);
}
}
/// <summary>
///
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public override Object GetInitializer(Type type)
{
return GetType();
}
/// <summary>
///
/// </summary>
/// <param name="info"></param>
/// <param name="attribute"></param>
/// <returns></returns>
public override Object GetInitializer(LogicalMethodInfo info, SoapExtensionAttribute attribute)
{
return null;
}
/// <summary>
///
/// </summary>
/// <param name="initializer"></param>
public override void Initialize(Object initializer)
{
}
}
public class MySoapHeader : SoapHeader
{
string _name;
string _passWord;
public string Name
{
get { return _name; }
set { _name = value; }
}
public string PassWord
{
get { return _passWord; }
set { _passWord = value; }
}
}
通过扩展SOAP可以针对消息做日志
1.8 Webservice的异常处理
Webservice客户端使用一个WebMethod时,如果WebMethod内部出现异常,不管异常是系统级异常或者自定义的异常,均会被包装为SoapException类型的异常,返回给客户端。
服务端代码:
代码
public class Service1 : System.Web.Services.WebService
{
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
/// <summary>
/// 根据ID读取人员信息
/// </summary>
/// <returns>人员信息类</returns>
[WebMethod]
public int Division(int x,int y)
{
try
{
return x / y;
}
catch (System.Exception ex)
{
throw new ExceptionHelper().RaiseException(
"Division",
ex.Message,
"1000",
ex.Source,
FaultCode.Server
);
}
}
[WebMethod]
public void TestException()
{
throw new SoapException("Invalid Operation.", SoapException.ServerFaultCode);
}
}
/// <summary>
/// 异常类型
/// </summary>
public enum FaultCode
{
Client = 0,
Server = 1
}
public class ExceptionHelper
{
/// <summary>
/// 封装异常为SoapException
/// </summary>
/// <param name="uri">引发异常的方法uri</param>
/// <param name="errorMessage">错误信息</param>
/// <param name="errorNumber">错误号</param>
/// <param name="errorSource">错误源</param>
/// <param name="code">异常类型</param>
/// <returns>封装后的SoapException</returns>
public SoapException RaiseException(string uri, string errorMessage, string errorNumber, string errorSource, FaultCode code)
{
//初始化限定名
XmlQualifiedName faultCodeLocation = null;
//异常类型代码转换
switch (code)
{
case FaultCode.Client:
faultCodeLocation = SoapException.ClientFaultCode;
break;
case FaultCode.Server:
faultCodeLocation = SoapException.ServerFaultCode;
break;
}
//构建异常信息结构体
string strXmlOut = @"<detail>"
+ "<Error>"
+ "<ErrorNumber>" + errorNumber + "</ErrorNumber>"
+ "<ErrorMessage>" + errorMessage + "</ErrorMessage>"
+ "<ErrorSource>" + errorSource + "</ErrorSource>"
+ "</Error>"
+ "</detail>";
//装载为Xml文档
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(strXmlOut);
//实例化SoapException
SoapException soapEx = new SoapException(errorMessage, faultCodeLocation, uri, xmlDoc.DocumentElement);
//返回SoapException
return soapEx;
}
{
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
/// <summary>
/// 根据ID读取人员信息
/// </summary>
/// <returns>人员信息类</returns>
[WebMethod]
public int Division(int x,int y)
{
try
{
return x / y;
}
catch (System.Exception ex)
{
throw new ExceptionHelper().RaiseException(
"Division",
ex.Message,
"1000",
ex.Source,
FaultCode.Server
);
}
}
[WebMethod]
public void TestException()
{
throw new SoapException("Invalid Operation.", SoapException.ServerFaultCode);
}
}
/// <summary>
/// 异常类型
/// </summary>
public enum FaultCode
{
Client = 0,
Server = 1
}
public class ExceptionHelper
{
/// <summary>
/// 封装异常为SoapException
/// </summary>
/// <param name="uri">引发异常的方法uri</param>
/// <param name="errorMessage">错误信息</param>
/// <param name="errorNumber">错误号</param>
/// <param name="errorSource">错误源</param>
/// <param name="code">异常类型</param>
/// <returns>封装后的SoapException</returns>
public SoapException RaiseException(string uri, string errorMessage, string errorNumber, string errorSource, FaultCode code)
{
//初始化限定名
XmlQualifiedName faultCodeLocation = null;
//异常类型代码转换
switch (code)
{
case FaultCode.Client:
faultCodeLocation = SoapException.ClientFaultCode;
break;
case FaultCode.Server:
faultCodeLocation = SoapException.ServerFaultCode;
break;
}
//构建异常信息结构体
string strXmlOut = @"<detail>"
+ "<Error>"
+ "<ErrorNumber>" + errorNumber + "</ErrorNumber>"
+ "<ErrorMessage>" + errorMessage + "</ErrorMessage>"
+ "<ErrorSource>" + errorSource + "</ErrorSource>"
+ "</Error>"
+ "</detail>";
//装载为Xml文档
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(strXmlOut);
//实例化SoapException
SoapException soapEx = new SoapException(errorMessage, faultCodeLocation, uri, xmlDoc.DocumentElement);
//返回SoapException
return soapEx;
}