.Net Compact Framework高级篇(2)-- 扩展SOAP应用 

Author:冯峰
Date:2008-06-28

本文主要讲述如何在 .Net CF中发送自定义的 SOAP消息来调用 WebService。可能大家对如何实现自定义的 SOAP有一定的了解。但是在 .Net CF中,有一些地方值得大家注意。

为何要实现自定义的 SOAP呢?以及 SOAP的好处在于?
一般调用 WebService时,我们可以发送 Http信息,也可以发送 SOAP1.1/1.2信息。如果我们希望在调用某些方法时只针对于特定的用户时。那通常做法,在调用函数中加入一些判断参数,然后来判断是否是被授权的用户。使用自定义 SOAP消息不但可以减少传入参数,可以在该方法调用前,就过滤掉,通知客户端,没有足够的权限。通常,我们可以将这些信息放到 SOAP的 header部分,传递到服务器端时,解析时,可以验证 header部分的信息是否符合。

概念讲述那么多。通过代码来说明吧。

首先定义一个 WebService。

1 [WebService(Namespace = "http://tempuri.org/")]
 2 [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
 3 public class Service : System.Web.Services.WebService
 4  {
 5     public MySoapHeader myHeader;
 6 
 7      public Service () {
 8     }
 9 
10     [WebMethod]
11      public string HelloWorld() {
12         return "Hello World";
13     }
14 
15     [WebMethod, SoapHeader("myHeader")]
16     public string GetInfo(string userName)
17      {
18         return string.Format("Hello, {0}", userName);
19     }
20 }

该 Service中声明了 2个方法,其中 GetInfo就是验证 SOAP的方法。在该 Service中提供了一个 MySoapHeader,这个是自定义的 SOAP头,用于定义一些验证信息。为何这样定义,等下文中叙述。

实现完 WebService后,该实现扩展的 SOAP信息了。

1         public override void ProcessMessage(SoapMessage message)
 2          {
 3             switch (message.Stage)
 4              {
 5                 case SoapMessageStage.AfterDeserialize:
 6                     break;
 7                 case SoapMessageStage.AfterSerialize:
 8                     break;
 9                 case SoapMessageStage.BeforeDeserialize:
10                     Check(message);
11                     break;
12                 case SoapMessageStage.BeforeSerialize:
13                     break;
14                 default:
15                     break;
16             }
17         }
18 
19         private void Check(SoapMessage message)
20          {
21             if (message is SoapServerMessage)
22              {
23                 MemoryStream ms = new MemoryStream();
24                 System.IO.StreamWriter sw = new StreamWriter(ms);
25                 StreamReader sr = new StreamReader(message.Stream);
26                 char[] buffer = new char[(int)message.Stream.Length];
27                 sr.Read(buffer, 0, (int)message.Stream.Length);
28 
29                 sw.Write(buffer, 0, buffer.Length);
30                 sw.Flush();
31                 message.Stream.Position = 0;
32                 string sb = System.Text.Encoding.ASCII.GetString(ms.ToArray());
33 
34                 Debug.WriteLine(sb);
35 
36                 XDocument doc = XDocument.Parse(sb);
37                 XNamespace name = "urn:com-appleseeker";
38                 XNamespace soapheader = "https://schemas.xmlsoap.org/soap/envelope/";
39                 MySoapHeader header = new MySoapHeader();
40 
41                 if (doc.Descendants(soapheader + "Header").Descendants().Elements(name + "userid").First() == null)
42                  {
43                     throw new Exception("");
44                 }
45                 header.userid = doc.Descendants(soapheader + "Header").Descendants().Elements(name + "userid").First().Value;
46 
47                 string[] temp = message.Action.ToString().Split('/');
48                 string methodName = temp[temp.Length - 1];
49 
50                 if (header.userid == "0345")
51                  {
52                 }
53                 else
54                  {
55                     throw new Exception("No Access");
56                 }
57             }
58         }

这是服务器端的扩展的 SOAP处理,必须重写一些方法,其中 ProcessMessage中,在做 BeforeDeserialize这一步操作时,解析接受到的 SOAP消息。通常 SOAP会有 4部分操作。可以参考代码说列。
在验证消息时,从 SOAP消息中取到内容,就是流中的内容,该流只读,可以通过复制到内存中来取得。当然,取道流的内容后,肯定是一个 XML格式,通过对 XML格式的文件解析方式,取到 SOAP头部说含内容。这里我只验证 userid=0345时,才能调用。否则则返回一个异常。

客户端部份相对简单。

1         public override void ProcessMessage(SoapMessage message)
 2          {
 3             switch (message.Stage)
 4              {
 5                 case SoapMessageStage.AfterDeserialize:
 6                     break;
 7                 case SoapMessageStage.AfterSerialize:
 8                     break;
 9                 case SoapMessageStage.BeforeDeserialize:
10                     break;
11                 case SoapMessageStage.BeforeSerialize:
12                      message.Headers.Add(new MySoapHeader() { userid = "0345" });
13                     break;
14                 default:
15                     break;
16             }
17         }

只是在 ProcessMessage的 BeforeSerialize前加入头文件的内容。

自定义的 SOAPHeader

1     [XmlRoot("MyHeader", Namespace = "urn:com-appleseeker")]
2     public class MySoapHeader: SoapHeader
3      {
4         public string userid;
5         public string username;
6         public string department;
7     }

Namespace约定了在 header部分的 tag,通过定义,我们可以用它来解析。

我们做完了代码上的大部分活,接下来就是配置了,如何让 WebService在接收到 SOAP消息后,自动来解析呢,只需要在 web.config中加入下面配置即可

1 <webServices>
2     <soapExtensionTypes>
3         <add type="ExtensionLib.MyServerSoapExtension, ExtensionLib"/>
4     </soapExtensionTypes>
5 </webServices>

在 add type中,第一个是自定义的扩展 SOAP类名,需加命名空间,后面一个是程序集名

服务器端配置完成了。接下来就是客户端部分。
这里介绍下非 Mobile程序如何使用,我创建一个控制台程序简单说明下,在客户端部分,有 2种方式使用扩展 SOAP。
1.可以像服务器端那样,自定义一个 SOAP类,然后加入相应信息,再在 app.config中加入相应配置即可。
2.在服务器的 WebService的方法的 Attribute中加入 SoapHeader参数,表明可以使用的自定义 SOAP头文件内容。
并在 WebService中提供一个 Public的自定义的头属性。在客户端只需要在调用时对自定义的头属性赋值即可。

在 WM中,只能使用第 2种方法来实现,不支持第 1种方式。

1 private void menuItem1_Click(object sender, EventArgs e)
2  {
3      localhost.Service service = new SmartDeviceProject1.localhost.Service();
4      service.Url = service.Url.Replace("localhost", "192.168.0.157");
5      localhost.MySoapHeader myHeader = new SmartDeviceProject1.localhost.MySoapHeader();
6      myHeader.userid = "0345";
7      service.MyHeader = myHeader;
8      MessageBox.Show(service.GetInfo("Gordon"));
9 }

以上在 WM中就能使用自定义的 SOAP消息了。各位在实际应用中如果遇到什么问题,可以参考我下面提供的代码。我在做 Demo的过程中也或多或少遇到一些问题,大家可以相互探讨交流。

这篇文章的主旨在于,如何在 WebService中验证用户信息。
在下一篇文章中,我可能会讲述如何在 WCF中实现用户的验证。同样会基于 .Net CF,希望大家能够留意。

很长时间我的 Blog没有更新了,一方面个人确实比较忙,今年个人的私事比较多。所以,对 Blog的更新就耽搁了。
我一致要求自己写出高质量的文章,给大家一些在开发上的经验,希望有更多的人来和我一起分享、一起交流、一起在 WM这个平台上有所成就。谢谢大家对我的支持。非常感谢 ~

代码下载:SOAPExtensionDemo.rar

 

下一篇:(冯峰).Net Compact Framework 3.5对WCF的支持