[玩转Silverlight]第一回:基础篇,品尝与HttpHandler通信

《你必须知道的.NET》网站 | Anytao技术博客 

[玩转Silverlight]第一回:基础篇,品尝与HttpHandler通信

发布日期:2009.01.20 作者:Anytao
© 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处。

Silverlight实在是带给我们很多的惊喜和收获,作为2009年的目标之一(详见我的2008,专注而行),未来的日子我就将与银光相伴,玩玩Silverlight这个超酷感受的技术精灵。

作为[玩转Silverlight]开篇的系列,本文开始将Silverlight的体验和学习做以系列报道,作为系列文章我将从以下几个方面着手记录:

  • 基础篇,讲述技术基础,例如本篇分析与HttpHandler通信的实现细节;
  • 深入篇,讲述技术本质,解开应用和基础谜团,例如DependencyProperty、CrossDomain Policy以及CoreCLR等相关深入内容;
  • 应用篇,以例讲理,通过小实例分享大技术;
  • 翻译篇,翻译国外优秀的Silverlight技术贴,师夷长技以制夷;
  • 推荐篇,推荐好的系列,推荐好的作品,推荐好的文章。

言归正传,我们开始第一回的分享之旅。以HttpHandler方式进行数据通信,是Silverlight不可拒绝的交互方式,关于HttpHandler不是本文探讨的重点,我们的目标是通过应用实例来品尝与HttpHandler通信的常见应用,为你的代码提供更多数据交互的选择方式,同时解决通信过程中的细枝末节。

基础课堂

基础课堂将以简短的论述,讨论技术要点,今天的主角是WebClient、WebRequest/WebResponse和HttpHandler。

WebClient

WebClient是个好东西,在与服务数据进行发送和接收式的太极套路中,WebClient以其简单性、易用性而名声大震,简单的说WebClient就是对WebRequest和WebResponse的封装,以发送请求而言,本质上正是通过WebRequest来实现资源请求的,这大大简化了操作模型,使得应用Uri进行资源请求和接收的操作异常顺手,这源于其提供了以下的几个方法:

  • OpenWrite/OpenRead
  • UploadData/DownloadData
  • UploadFile/DownloadString
  • UploadString/DownloadString

同时每个方法都提供了相应的异步调用方法,例如OpenWriteAsync、UploadDataAsync等,为实现基于WebClient的数据通信提供了很多支持,方便我们在后文轻松的应用。

WebRequest/WebResponse

顾名思义,WebRequest和WebResponse是分别用于发送请求和响应请求的。有意思的是,在.NET中的WebRequest和WebResponse是两个抽象类型,创建一个WebRequest或WebResponse实例的一般方法是:

WebRequest request = WebRequest.Create(uri);

通过在Create方法中指定不同uri的参数来创建不同类型的WebRequest具体实例,例如HttpWebRequest实例或者FileWebRequest实例。熟悉工程方法模式的读者不难发现,原来.NET FCL中处处有经典,只需稍微深入的研究一下IWebRequestCreate与WebRequest之间的关系,就会为简单的request实例化过程感到惊讶,这不过是基础论述中的小小插曲,但愿没有扫您继续关注Silverlight的兴致。

以WebRequest和WebResponse方式进行数据通信,我们将在后文以实例分析。同时,应当注意的是,不管是WebClient方式还是WebRequest方式,Silverlight中所有的数据通信,必须以异步方式进行。

HttpHandler

简单来说,HttpHandler是ASP .NET的Http请求处理中心,不同的文件类型通过提供不同的handler进行分派处理,大部分的操作由ASP .NET内置handler进行处理,而很多时候自定义Handler同样有其市场,在.NET中自定义handler一般需要实现IHttpHandler接口,

public interface IHttpHandler
{
    bool IsReusable { get; }
    void ProcessRequest(HttpContext context);
}

由其接口定义可知,IsResuable属性指示是否可以重用于其他IHttpHandler实例,而ProcessRequest方法中则实现自定义的http请求处理逻辑。

实例分析

下面我们以一个简单的实例为例,来分析两种方式操作下与HttpHandler进行数据交互的各种方式,首先做一点必要的准备在新建的Silverlight项目中建立必要的UI准备:

<StackPanel Orientation="Vertical">
    <TextBox  Margin="20, 5, 20, 5" x:Name="name" />
    <TextBox  Margin="20, 5, 20, 5" x:Name="pwd" />
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
        <Button x:Name="btnGetByWebClient" Width="200" Content="GetByWebClient" Click="btnGetByWebClient_Click" />
        <Button x:Name="btnGetByWebRequest" Width="200" Content="GetByWebRequest" Click="btnGetByWebRequest_Click" />
    </StackPanel>
    <TextBlock x:Name="result" Margin="20, 5, 20, 5" Text="Result: " />
    <TextBlock x:Name="copyright" Margin="5" Text="@ 2009, Anytao.com" Foreground="Red" HorizontalAlignment="Center" />
</StackPanel>

目的是通过检测Name和Password来判断输入的用户是否合法,执行的判断逻辑交由Handler来执行,而通过WebClient方式或者WebRequest方式请求和响应数据通信,所以Handler的逻辑也很简单:

// Release : 2009/01/20

// Author : Anytao, http://www.anytao.com

public class UserHandler : IHttpHandler, IRequiresSessionState
{
    public void ProcessRequest(HttpContext context)
    {
        string name = context.Request.QueryString["name"];
        string pwd = context.Request.QueryString["pwd"];

        string result = ValidateUser(name, pwd);

        context.Response.ContentType = "application/x-www-form-urlencoded";

        if (string.IsNullOrEmpty(result))
        {
            context.Response.Write("Null");
        }
        else
        {
            context.Response.Write(result);
        }
    }

    private string ValidateUser(string name, string pwd)
    {
        if (name.ToLower().CompareTo("anytao") == 0 && pwd.ToLower().CompareTo("123") == 0)
        {
            return name + " is a authorized user.";
        }
        else
        {
            return name + " is invalid user.";
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

下面继续数据通信部分的实现。

WebClient方式

对于这种应用WebClient提供了最简单的理想方式,可以选择相应的既存方法处理不同数据通信的需求,例如在本例中我们将通过DownloadString下载从handler处理之后的响应:

// Release : 2009/01/20

// Author : Anytao, http://www.anytao.com

private void GetResultByWebClient(string name, string pwd)
{
    string absolutePath = HtmlPage.Document.DocumentUri.AbsoluteUri;
    string address = absolutePath.Substring(0, absolutePath.LastIndexOf('/'))
        + "/Handler/UserHandler.ashx?name=" + name + "&pwd=" + pwd;

    Uri uri = new Uri(address);

    WebClient client = new WebClient();
    client.DownloadStringCompleted += (sender, e) =>
        {
            if (null == e.Error)
            {
                result.Text = e.Result;
                //Call other service here to continue operation
            }
            else
            {
                MessageBox.Show(e.Error.Message);
            }
        };
        
      client.DownloadStringAsync(uri);
}

注意,以QueryString进行数据传递并非一成不变的方式,不同浏览器对于Url的长度和编码都有不同的规则,例如大数据量通信情况下应该考虑以UploadString方式进行,通过Http Post方法向Handler发送请求,我们将在下回中讲述序列化主题时进行相关的讨论和应用示例。

WebRequest/WebResponse方式

以WebRequest方式进行数据通信,实现相同的请求操作,需要如下的实现:

// Release : 2009/01/20

// Author : Anytao, http://www.anytao.com

private void GetResultByWebRequest(string name, string pwd)
{
    string absolutePath = HtmlPage.Document.DocumentUri.AbsoluteUri;
    string address = absolutePath.Substring(0, absolutePath.LastIndexOf('/'))
        + "/Handler/UserHandler.ashx?name=" + name + "&pwd=" + pwd;

    Uri uri = new Uri(address);
    WebRequest request = WebRequest.Create(uri);
    request.Method = "POST";
    request.ContentType = "application/x-www-form-urlencoded";
    request.BeginGetRequestStream(new AsyncCallback(RequestReady), request);
}

private void RequestReady(IAsyncResult ar)
{
    WebRequest request = ar.AsyncState as WebRequest;
    using (System.IO.StreamWriter sw = new System.IO.StreamWriter(request.EndGetRequestStream(ar)))
    {
        sw.Write("Post data to server.");
    }
    request.BeginGetResponse(new AsyncCallback(ResponseReady), request);
}

private void ResponseReady(IAsyncResult ar)
{
    WebRequest request = ar.AsyncState as WebRequest;
    using (WebResponse response = request.EndGetResponse(ar))
    {
        using (System.IO.Stream stream = response.GetResponseStream())
        {
            using (System.IO.StreamReader reader = new System.IO.StreamReader(stream))
            {
                Dispatcher.BeginInvoke((InvokeDelegate)BindAndNext, reader.ReadToEnd());
            }
        }
    }
}

private void BindAndNext(string result)
{
    if (!string.IsNullOrEmpty(result))
    {
        this.result.Text = result;

        //Call another WCF Service continuously
    }
    else
    {
        MessageBox.Show("Error");
    }
}

最后,欣赏以下应用示例,品尝一下Silverlight下的数据通信体验,收获更多关于Silverlight的美妙感受:

另外,提及HttpHandler,另一个重要的问题是关于Session的处理,这同样是Silverlight开发中可能关注的问题。

关照Session

在HttpHandler中使用Session,必须实现IRequiresSessionState或者IReadOnlySessionState接口,

public class UserHandler : IHttpHandler, IRequiresSessionState
{
}

二者的区别是:

  • IReadOnlySessionState,提供了Session状态值的只读访问权限
  • IRequiresSessionState,提供了Session状态值的读写权限

细心的读者会发现这两个接口都是空接口,没有任何方法签名,只是作为标记接口,因此必须在自定义Handler中实现相应的接口才赋予了处理程序相应的操作权限,否则通过Session进行读写操作都返回null值。如本例UserHandler所示,实现IRequiresSessionState接口将使得在UserHandler中进行Session的读写处理变成可能,从而实现更多在Silverlight端与Web端的数据交互手段。

实际上,在进行Web通信的操作中简单的string传递只是冰山一角,更多的操作设计到对实体类型在不同环境的传递和交互,以本文的实例而言Silverlight客户端向Server端发送的name和pwd换成一个用户列表信息或者更加复杂的自定义类型,而Server端返回的请求也不仅仅是简单字符串(name + " is a authorized user."),那么通过querystring进行简单的数据传递将变的困难,我们将在下回探讨通信过程的另一个环节,那就是序列化和编码。

Anytao.SLScenario.AccessHandler.rar 

anytao | © 2009 Anytao.com

2009/01/20 | http://anytao.cnblogs.com/

本文地址:http://www.cnblogs.com/anytao/archive/2009/01/20/anytao_silverlight_01_communication_handler.html

本文以“现状”提供且没有任何担保,同时也没有授予任何权利。 | This posting is provided "AS IS" with no warranties, and confers no rights.

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

posted @ 2009-01-20 01:12  Anytao  阅读(5339)  评论(33编辑  收藏  举报