代码改变世界

在Silverlight开发基于http协议客户端连接器(一)

2010-09-02 17:42  姜 萌@cnblogs  阅读(638)  评论(0编辑  收藏  举报

之前在一个项目中需要Silverilght应用与基于java 的web应用进行通讯。我当时 的可选方案有三个:

1.通过HTML Bridge,silverlight调用外部js代码,js再通过ajax与服务器通讯,在获得返回的数据后js再调用silverlight的方法。具体来讲,主要是通过HtmlPage.Document操作DOM,HtmlPage.Window调用js function。通过ScriptableMemberAttribute标注来是方法对js可见,并且在html中包含silverlight 的<object>中加入<param name="enableHtmlAccess" value="true" /> 使其具备相关权限。

关于有关HTML Bridge的详细用法您可以参见MSDN中的:HTML Bridge: Interaction Between HTML and Managed Code

2.silverlight直接与服务器通讯。虽然silverlight应用(非OOB模式)承载于HTML中但silverlight是一个完整的RIA,其实对于这样一个需求而言,我们完全可以让silverlight自己解决。silverlight为我们提供了两个方便http通讯的类型:WebClient和HttpWebRequest,相比之下WebClient更傻瓜一些,如其名,就是一个封装好的mini web客户端。笔者笔者以前在wpf开发中也使用webclient解决类似问题,可是偏偏silverlight的WebClient缩水太严重,没有CookieContainer属性,而且像修改HTTP Headers这种正常的操作在silverlight版本中居然出错,相比而言HttpWebRequest可以提供更灵活的操作,具体方案在下面讨论。

3.使用Web Service。虽然在.NET平台下wcf的功能更为强大并且同样是基于SOAP,像BingMap为silveright提供的都是wcf服务,但对于跨平台的通讯而言web service在java平台已经有像axis这样的成熟产品。WS更容易被人们接受。不过考虑到java这边自动生成silverlight客户端代码不方便所以暂不考虑。

Silverlight中WebClient的局限性以及遇到的问题

下面代码使用的是完整BCL中的System.Net.WebClient,可以正常获得响应头中的SESSIONID,。

WebClient client = new WebClient();
           client.DownloadStringCompleted += (o, e)
               => Console.WriteLine(client.ResponseHeaders[HttpResponseHeader.SetCookie]);
           client.DownloadStringAsync(new Uri("http://www.mywebsite.com/testservice.do%22));

这样我们只需通过anotherClient.Headers[HttpRequestHeader.Cookie] = ……将得到的SET-COOKIE值(里面有sessionid)设置进去,这样下次访问时就可以保持住session。

但是在silverlight中就出现问题了,首先就没有HttpResponseHeader,并且ResponseHeaders竟然是null的image ,更不解的是居然不能设置WebClient.Headers,每次设置都会引发异常image 。得不到设置不了Headers也得不到ResponseHeaders中的cookie值,也就是说使用silverlight的WebClient没法保持住session。

好吧,几人silverlight版本的webclient有如此多的局限性,我们就尝试使用HttpWebRequest(如果这哥们再出问题那就干脆用socket实现http算了。。)

定制自己的WebClientEx

为了方便别人使用,我们提供与WebClient方法类似的两个委托签名:public event Action<string> DownloadStringCompleted,public event Action DownloadError。实现起来也容易,就是组合一个HttpWebRequest,使用.NET中惯用的二段式异步编程,代码如下。

 

WebClientEx
public class WebClientEx 
    { 
        
private readonly static string No_Cache = "no-cache"
        
private readonly static string Tricky_NeverIfNoneMatch = "2701-4c4585e8"
        
private HttpWebRequest _request; 
        
public WebClientEx() 
        { 
        } 
        
public void SendAsync(string url, CookieContainer cookie) 
        { 
            _request 
= HttpWebRequest.CreateHttp(url); 
            _request.CookieContainer 
= cookie; 
            _request.Headers[HttpRequestHeader.CacheControl] 
= No_Cache; 
            _request.Headers[HttpRequestHeader.IfNoneMatch] 
= Tricky_NeverIfNoneMatch; 
            _request.BeginGetResponse(onResponseCallback, 
null); 
        } 
        
private void onResponseCallback(IAsyncResult ar) 
        { 
            
try 
            { 
                WebResponse response 
= _request.EndGetResponse(ar); 
                
string result = null
                
using (StreamReader sr = new StreamReader(response.GetResponseStream())) 
                { 
                    result 
= sr.ReadToEnd(); 
                } 
                
if (DownloadStringCompleted != null
                    DownloadStringCompleted(result); 
            } 
            
catch (Exception ) 
            { 
                
if (DownloadError != null
                    DownloadError(); 
            } 
        } 
        
public event Action<string> DownloadStringCompleted; 
        
public event Action DownloadError; 
    }

 

这样:

WebClientEx client = new WebClientEx();
var cookie = CookieContainer();
client.SendAsync(URI1, cookie);
client.SendASync(URI2, cookie);

当第二次调用时就能够保持住session。