上一篇说的是wp的切换动画,有了华丽的效果还要有数据,数据当然是从云上来(哈哈,现在流行叫云,虽然我也不知道什么是云)。
wp支持http,web service,wcf和socket。今天要说的是通过http请求来实现的通信,这种方式不管是客户端还是服务端实现起来都很方便,服务端只要在原有的web端扩展一下(个人理解)。下面的demo请求的是中国天气网的天气预报,返回的事json数据。wp的http请求可以通过两个类实现,httpwebrequest和webclient,多说无益,直接上代码。
1 //----------------------------APM--------------------------- 2 WebRequest request = HttpWebRequest.Create("http://www.weather.com.cn/data/cityinfo/101010100.html "); 3 IAsyncResult result = (IAsyncResult)request.BeginGetResponse((ret) => 4 { 5 WebResponse response = ((HttpWebRequest)ret.AsyncState).EndGetResponse(ret); 6 using (Stream stream = response.GetResponseStream()) 7 using (StreamReader sr = new StreamReader(stream)) 8 { 9 string s = sr.ReadToEnd(); 10 Dispatcher.BeginInvoke(() => 11 { 12 MessageBox.Show(s); 13 }); 14 } 15 }, request);
httpwebrequest是个抽象类,所以不能new要create出来。httpwebreqeust里用到的是异步编程模型模式(APM),通过beginXXX开始,再在回调里调用endXXX获得异步执行的结果。需要注意的是httpwebrequest的回掉不在UI线程中,所以不能直接更新UI(包括操作viewModel)要用Dispatcher.BeginInvoke和UI进行通信。
1 //---------------------------------EPM-------------------------- 2 WebClient client = new WebClient(); 3 client.DownloadStringAsync(new Uri("http://www.weather.com.cn/data/cityinfo/101010100.html ", UriKind.RelativeOrAbsolute)); 4 client.DownloadStringCompleted += (object sender, DownloadStringCompletedEventArgs e) => 5 { 6 MessageBox.Show(e.Result); 7 };
webclient是webrequest的高级封装,所以使用webclient不用太多的设置。webclient原生的支持基于事件的异步(EPM),给XXXcompleted绑定回调方法,执行XXXAsync。这是我最熟悉的方式,当初用的Silverlight3和flex4都是这种模式。但是这种用回调的方式也很不方便,一是,一般这样底层的功能都是封装在公共类里面使用,要想把返回数据返回给调用的方法就很麻烦,我的做法是在类里定义带参数的delegate,通过委托方法把数据带回去;二是,不方便捕获异常。我的demo里都没写try catch,细心的朋友可以加上。而且加try的时候不能像写同步程序那样把所有可疑的地方都包括进去,那样一旦是回调引发异常就捕获不到。如果在回调里面加上try又不能通过throw的方法往上层抛,只能通过delegate把error带回去。最后写出来的代码很丑陋(水平不行吧)。 在新一代异步编程里微软推荐使用task,理论上是任何异步都可以封装成基于task的异步(TAP),在.net4.5里很多类都原生支持task。下面是把webclient封装成TAP,添加一个方法
1 //---------------------------------TAP----------------------------- 2 public Task<string> getRequest(string url) 3 { 4 var source = new TaskCompletionSource<string>(); 5 var webClient = new WebClient(); 6 webClient.Encoding = Encoding.UTF8; 7 webClient.DownloadStringCompleted += (senders, args) => 8 { 9 if (args.Cancelled) 10 { 11 source.SetCanceled(); 12 return; 13 } 14 if (args.Error != null) 15 { 16 source.SetException(args.Error); 17 return; 18 } 19 source.SetResult(args.Result); 20 }; 21 UriBuilder fullurl = new UriBuilder(url); 22 webClient.DownloadStringAsync(fullurl.Uri); 23 return source.Task; 24 }
调用方法声明上要加上async关键字,表示该方法是异步的。
MessageBox.Show(await getRequest("http://www.weather.com.cn/data/cityinfo/101010100.html "));
在调用的时候要在方法前面加上await,表示等待方法执行完毕再执行下面的。这样的代码看起来可读性确实比一层套一层的拉姆达表达式更清晰了而且数据确实return回来了,但是异常仍然很头疼。并且在httpwebrequest和webclient里并没有timeout属性,如果服务端没返回总不能无限等吧。
下面救世主来了,Microsoft HTTP Client Libraries,这是微软传说中的modern HTTP库。在vs里安上nuget扩展,然后直接在nuget包管理器里下载这个库添加到项目的引用里,看到下面的代码我被惊艳了
1 //-----------------------------modern HTTP------------------------ 2 HttpClient hc = new HttpClient(); 3 try 4 { 5 var task = await hc.GetAsync("http://www.weather.com.cn/data/cityinfo/101010100.html "); 6 string result = await task.Content.ReadAsStringAsync(); 7 MessageBox.Show(result); 8 } 9 catch (Exception e) 10 { 11 MessageBox.Show(e.Message); 12 }
调用方法同样要标记为async,看吧,简洁到家了而且捕获异常也很方便。