none
silverlight与HttpHandler通信 RRS feed

  • 常规讨论

  • 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进行简单的数据传递将变的困难,我们将在下回探讨通信过程的另一个环节,那就是序列化和编码。

    2009年4月23日 10:02