重新想象 Windows 8 Store Apps (61) - 通信: http, oauth
作者:webabcd
介绍
重新想象 Windows 8 Store Apps 之 通信
- HttpClient 概述
- http get string
- http get stream
- http post string
- http post stream
- OAuth 2.0 验证的客户端
示例
用于演示 http 通信的服务端
WebServer/HttpDemo.aspx.cs
/* * 用于响应 http 请求 */ using System; using System.IO; using System.Threading; namespace WebServer { public partial class HttpDemo : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { // 停 3 秒,以方便测试 http 请求的取消 Thread.Sleep(3000); var action = Request.QueryString["action"]; switch (action) { case "getString": // 响应 http get string Response.Write("hello webabcd"); break; case "getStream": // 响应 http get stream Response.Write("hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd hello webabcd"); break; case "postString": // 响应 http post string Response.Write(string.Format("param1:{0}, param2:{1}, referrer:{2}", Request.Form["param1"], Request.Form["param2"], Request.UrlReferrer)); break; case "postStream": // 响应 http post stream using (StreamReader reader = new StreamReader(Request.InputStream)) { string body = reader.ReadToEnd(); Response.Write(Server.HtmlEncode(body)); } break; default: break; } Response.End(); } } }
1、通过 HttpClient, HttpRequestMessage, HttpResponseMessage 实现 HTTP 通信
Communication/HTTP/Summary.xaml
<Page x:Class="XamlDemo.Communication.HTTP.Summary" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Communication.HTTP" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="Transparent"> <StackPanel Margin="120 0 0 0"> <TextBlock Name="lblMsg" FontSize="14.667" /> <Button Name="btnPost" Content="http post" Click="btnPost_Click_1" Margin="0 10 0 0" /> <Button Name="btnCancel" Content="cancel" Click="btnCancel_Click_1" Margin="0 10 0 0" /> </StackPanel> </Grid> </Page>
Communication/HTTP/Summary.xaml.cs
/* * 通过 HttpClient, HttpRequestMessage, HttpResponseMessage 实现 HTTP 通信 * * HttpClient - 用于发起 http 请求,以及接收 http 响应 * BaseAddress - 发送请求的 uri * DefaultRequestHeaders - 默认的 http 请求头信息 * MaxResponseContentBufferSize - 读取响应内容时,所可以缓冲的最大字节数。默认值:64K * Timeout - http 请求的超时时间 * CancelPendingRequests() - 取消该 HttpClient 对象所有挂起的 http 请求 * GetStringAsync(), GetStreamAsync(), GetByteArrayAsync(), GetAsync() - http get 数据 * PostAsync(), DeleteAsync(), PutAsync() - http post delete put 数据 * 参数:HttpContent content - http 请求的数据(HttpContent 类型) * 继承自 HttpContent 的类有:StringContent, ByteArrayContent, StreamContent, FormUrlEncodedContent 等 * 参数:HttpCompletionOption completionOption(HttpCompletionOption 枚举) * ResponseContentRead - 获取到全部内容后再返回数据,默认值 * ResponseHeadersRead - 获取到头信息后就返回数据,用于流式获取 * * HttpRequestMessage - http 请求 * Method - http 方法 * RequestUri - 请求的 uri * Version - http 版本,默认是 1.1 * Headers - http 的请求头信息 * Content - http 请求的内容(HttpContent 类型) * 继承自 HttpContent 的类有:StringContent, ByteArrayContent, StreamContent, FormUrlEncodedContent 等 * * HttpResponseMessage - http 响应 * RequestMessage - 对应的 HttpRequestMessage 对象 * Headers - http 的响应头信息 * Version - http 版本,默认是 1.1 * StatusCode - http 响应的状态码 * ReasonPhrase - http 响应的状态码所对应的短语 * IsSuccessStatusCode - http 响应的状态码是否是成功的值(200-299) * EnsureSuccessStatusCode() - 当 IsSuccessStatusCode 为 false 时会抛出异常 * * * 注:关于下载/上传的进度获取,请参见“后台任务” * * 另:win8 metro 的 http 抓包可用 fiddler * * 还有: * http 通信还可以通过如下方法实现 * HttpWebRequest webRequest = WebRequest.Create(url); */ using System; using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace XamlDemo.Communication.HTTP { public sealed partial class Summary : Page { private HttpClient _httpClient; public Summary() { this.InitializeComponent(); } protected override void OnNavigatedFrom(NavigationEventArgs e) { // 释放资源 if (_httpClient != null) { _httpClient.Dispose(); _httpClient = null; } } private async void btnPost_Click_1(object sender, RoutedEventArgs e) { _httpClient = new HttpClient(); try { string url = "http://localhost:39629/HttpDemo.aspx?action=postString"; // 创建一个 HttpRequestMessage 对象 HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url); // 需要 post 的数据 var postData = new FormUrlEncodedContent( new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("param1", "web"), new KeyValuePair<string, string>("param2", "abcd") } ); // http 请求的数据 request.Content = postData; // http 请求的头信息 request.Headers.Referrer = new Uri("http://webabcd.cnblogs.com"); // 请求 HttpRequestMessage 对象,并返回 HttpResponseMessage 数据 HttpResponseMessage response = await _httpClient.SendAsync(request); // http 响应的状态码及其对应的短语 lblMsg.Text += ((int)response.StatusCode) + " " + response.ReasonPhrase; lblMsg.Text += Environment.NewLine; // 以字符串的方式获取响应数据 lblMsg.Text += await response.Content.ReadAsStringAsync(); lblMsg.Text += Environment.NewLine; } catch (TaskCanceledException) { lblMsg.Text += "取消了"; lblMsg.Text += Environment.NewLine; } catch (Exception ex) { lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; } } private void btnCancel_Click_1(object sender, RoutedEventArgs e) { // 取消 http 请求 _httpClient.CancelPendingRequests(); } } }
2、演示 http get string
Communication/HTTP/GetString.xaml.cs
/* * 演示 http get string */ using System; using System.Net.Http; using System.Threading.Tasks; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace XamlDemo.Communication.HTTP { public sealed partial class GetString : Page { private HttpClient _httpClient; public GetString() { this.InitializeComponent(); } protected override void OnNavigatedFrom(NavigationEventArgs e) { // 释放资源 if (_httpClient != null) { _httpClient.Dispose(); _httpClient = null; } } private async void btnGetString_Click_1(object sender, RoutedEventArgs e) { _httpClient = new HttpClient(); try { HttpResponseMessage response = await _httpClient.GetAsync(new Uri("http://localhost:39629/HttpDemo.aspx?action=getString")); lblMsg.Text += ((int)response.StatusCode) + " " + response.ReasonPhrase; lblMsg.Text += Environment.NewLine; // HttpContent.ReadAsStringAsync() - 以 string 方式获取响应数据 // HttpContent.ReadAsByteArrayAsync() - 以 byte[] 方式获取响应数据 // HttpContent.ReadAsStreamAsync() - 以 stream 方式获取响应数据 lblMsg.Text += await response.Content.ReadAsStringAsync(); lblMsg.Text += Environment.NewLine; } catch (TaskCanceledException) { lblMsg.Text += "取消了"; lblMsg.Text += Environment.NewLine; } catch (Exception ex) { lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; } } private void btnCancel_Click_1(object sender, RoutedEventArgs e) { // 取消 http 请求 _httpClient.CancelPendingRequests(); } } }
3、演示 http get stream
Communication/HTTP/GetStream.xaml.cs
/* * 演示 http get stream */ using System; using System.IO; using System.Net.Http; using System.Threading.Tasks; using Windows.Security.Cryptography; using Windows.Storage.Streams; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace XamlDemo.Communication.HTTP { public sealed partial class GetStream : Page { private HttpClient _httpClient; public GetStream() { this.InitializeComponent(); } protected override void OnNavigatedFrom(NavigationEventArgs e) { // 释放资源 if (_httpClient != null) { _httpClient.Dispose(); _httpClient = null; } } private async void btnGetStream_Click_1(object sender, RoutedEventArgs e) { _httpClient = new HttpClient(); try { // HttpCompletionOption.ResponseHeadersRead - 获取到头信息后就返回数据,用于流式获取 HttpResponseMessage response = await _httpClient.GetAsync( new Uri("http://localhost:39629/HttpDemo.aspx?action=getStream"), HttpCompletionOption.ResponseHeadersRead); lblMsg.Text += ((int)response.StatusCode) + " " + response.ReasonPhrase; lblMsg.Text += Environment.NewLine; // HttpContent.ReadAsStringAsync() - 以 string 方式获取响应数据 // HttpContent.ReadAsByteArrayAsync() - 以 byte[] 方式获取响应数据 // HttpContent.ReadAsStreamAsync() - 以 stream 方式获取响应数据 using (Stream responseStream = await response.Content.ReadAsStreamAsync()) { byte[] buffer = new byte[32]; int read = 0; while ((read = await responseStream.ReadAsync(buffer, 0, buffer.Length)) > 0) { lblMsg.Text += "读取的字节数: " + read.ToString(); lblMsg.Text += Environment.NewLine; IBuffer responseBuffer = CryptographicBuffer.CreateFromByteArray(buffer); lblMsg.Text += CryptographicBuffer.EncodeToHexString(responseBuffer); lblMsg.Text += Environment.NewLine; buffer = new byte[32]; } } } catch (TaskCanceledException) { lblMsg.Text += "取消了"; lblMsg.Text += Environment.NewLine; } catch (Exception ex) { lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; } } private void btnCancel_Click_1(object sender, RoutedEventArgs e) { // 取消 http 请求 _httpClient.CancelPendingRequests(); } } }
4、演示 http post string
Communication/HTTP/PostString.xaml.cs
/* * 演示 http post string */ using System; using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace XamlDemo.Communication.HTTP { public sealed partial class PostString : Page { private HttpClient _httpClient; public PostString() { this.InitializeComponent(); } protected override void OnNavigatedFrom(NavigationEventArgs e) { // 释放资源 if (_httpClient != null) { _httpClient.Dispose(); _httpClient = null; } } private async void btnPostString_Click_1(object sender, RoutedEventArgs e) { _httpClient = new HttpClient(); try { // 需要 post 的数据 var postData = new FormUrlEncodedContent( new List<KeyValuePair<string, string>> { new KeyValuePair<string, string>("param1", "web"), new KeyValuePair<string, string>("param2", "abcd") } ); HttpResponseMessage response = await _httpClient.PostAsync( new Uri("http://localhost:39629/HttpDemo.aspx?action=postString"), postData); lblMsg.Text += ((int)response.StatusCode) + " " + response.ReasonPhrase; lblMsg.Text += Environment.NewLine; // HttpContent.ReadAsStringAsync() - 以 string 方式获取响应数据 // HttpContent.ReadAsByteArrayAsync() - 以 byte[] 方式获取响应数据 // HttpContent.ReadAsStreamAsync() - 以 stream 方式获取响应数据 lblMsg.Text += await response.Content.ReadAsStringAsync(); lblMsg.Text += Environment.NewLine; } catch (TaskCanceledException) { lblMsg.Text += "取消了"; lblMsg.Text += Environment.NewLine; } catch (Exception ex) { lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; } } private void btnCancel_Click_1(object sender, RoutedEventArgs e) { // 取消 http 请求 _httpClient.CancelPendingRequests(); } } }
5、演示 http post stream
Communication/HTTP/PostStream.xaml.cs
/* * 演示 http post stream */ using System; using System.IO; using System.Net.Http; using System.Threading.Tasks; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace XamlDemo.Communication.HTTP { public sealed partial class PostStream : Page { private HttpClient _httpClient; public PostStream() { this.InitializeComponent(); } protected override void OnNavigatedFrom(NavigationEventArgs e) { // 释放资源 if (_httpClient != null) { _httpClient.Dispose(); _httpClient = null; } } private async void btnPostStream_Click_1(object sender, RoutedEventArgs e) { _httpClient = new HttpClient(); try { // 需要 post 的 stream 数据 Stream stream = GenerateSampleStream(128); StreamContent streamContent = new StreamContent(stream); HttpResponseMessage response = await _httpClient.PostAsync( new Uri("http://localhost:39629/HttpDemo.aspx?action=postStream"), streamContent); lblMsg.Text += ((int)response.StatusCode) + " " + response.ReasonPhrase; lblMsg.Text += Environment.NewLine; // HttpContent.ReadAsStringAsync() - 以 string 方式获取响应数据 // HttpContent.ReadAsByteArrayAsync() - 以 byte[] 方式获取响应数据 // HttpContent.ReadAsStreamAsync() - 以 stream 方式获取响应数据 lblMsg.Text += await response.Content.ReadAsStringAsync(); lblMsg.Text += Environment.NewLine; } catch (TaskCanceledException) { lblMsg.Text += "取消了"; lblMsg.Text += Environment.NewLine; } catch (Exception ex) { lblMsg.Text += ex.ToString(); lblMsg.Text += Environment.NewLine; } } // 生成一个指定大小的内存流 private static MemoryStream GenerateSampleStream(int size) { byte[] subData = new byte[size]; for (int i = 0; i < subData.Length; i++) { subData[i] = (byte)(97 + i % 26); // a-z } return new MemoryStream(subData); } private void btnCancel_Click_1(object sender, RoutedEventArgs e) { // 取消 http 请求 _httpClient.CancelPendingRequests(); } } }
6、演示如何开发一个基于 OAuth 2.0 验证的客户端
Communication/OpenAuth/ClientDemo.xaml
<Page x:Class="XamlDemo.Communication.OpenAuth.ClientDemo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Communication.OpenAuth" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="Transparent"> <StackPanel Margin="120 0 0 0"> <Button Name="btnWeibo" Content="登录新浪微博,并返回登录用户好友最新发布的微博" Click="btnWeibo_Click" /> <TextBlock Name="lblMsg" FontSize="14.667" TextWrapping="Wrap" Margin="0 10 0 0" /> </StackPanel> </Grid> </Page>
Communication/OpenAuth/ClientDemo.xaml.cs
/* * 演示如何开发一个基于 OAuth 2.0 验证的客户端 * * WebAuthenticationBroker - 用于 OAuth 2.0 验证的第一步,可以将第三方 UI 无缝整合进 app * AuthenticateAsync(WebAuthenticationOptions options, Uri requestUri, Uri callbackUri) - 请求 authorization code,返回一个 WebAuthenticationResult 类型的数据 * * WebAuthenticationResult - 请求 authorization code(OAuth 2.0 验证的第一步)的结果 * ResponseData - 响应的数据 * ResponseStatus - 响应的状态 * * * 关于 OAuth 2.0 协议参见:http://tools.ietf.org/html/draft-ietf-oauth-v2-20 */ using System; using System.Net.Http; using System.Text.RegularExpressions; using Windows.Data.Json; using Windows.Security.Authentication.Web; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace XamlDemo.Communication.OpenAuth { public sealed partial class ClientDemo : Page { public ClientDemo() { this.InitializeComponent(); } private async void btnWeibo_Click(object sender, RoutedEventArgs e) { try { var appKey = "39261162"; var appSecret = "652ec0b02f814d514fc288f3eab2efda"; var callbackUrl = "http://webabcd.cnblogs.com"; // 在新浪微博开放平台设置的回调页 var requestAuthorizationCode_url = string.Format("https://api.weibo.com/oauth2/authorize?client_id={0}&response_type=code&redirect_uri={1}", appKey, callbackUrl); // 第一步:request authorization code WebAuthenticationResult WebAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync( WebAuthenticationOptions.None, new Uri(requestAuthorizationCode_url), new Uri(callbackUrl)); // 第一步的结果 lblMsg.Text = WebAuthenticationResult.ResponseStatus.ToString() + Environment.NewLine; if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success) { // 从第一步返回的数据中获取 authorization code var authorizationCode = QueryString(WebAuthenticationResult.ResponseData, "code"); lblMsg.Text += "authorizationCode: " + authorizationCode + Environment.NewLine; var requestAccessToken_url = string.Format("https://api.weibo.com/oauth2/access_token?client_id={0}&client_secret={1}&grant_type=authorization_code&redirect_uri={2}&code={3}", appKey, appSecret, callbackUrl, authorizationCode); // 第二步:request access token HttpClient client = new HttpClient(); var response = await client.PostAsync(new Uri(requestAccessToken_url), null); // 第二步的结果:获取其中的 access token var jsonString = await response.Content.ReadAsStringAsync(); JsonObject jsonObject = JsonObject.Parse(jsonString); var accessToken = jsonObject["access_token"].GetString(); lblMsg.Text += "accessToken: " + accessToken + Environment.NewLine; var requestProtectedResource_url = string.Format("https://api.weibo.com/2/statuses/friends_timeline.json?access_token={0}", accessToken); // 第三步:request protected resource,获取需要的数据(本例为获取登录用户好友最新发布的微博) var result = await client.GetStringAsync(new Uri(requestProtectedResource_url)); lblMsg.Text += "result: " + result; } } catch (Exception ex) { lblMsg.Text += Environment.NewLine; lblMsg.Text += ex.ToString(); // 由于本 app 没有提交新浪微博开放平台审核,所以需要在新浪微博开放平台中添加测试账号,否则会出现异常 } } /// <summary> /// 模拟 QueryString 的实现 /// </summary> /// <param name="queryString">query 字符串</param> /// <param name="key">key</param> private string QueryString(string queryString, string key) { return Regex.Match(queryString, string.Format(@"(?<=(&|?|^)({0})=).*?(?=&|$)", key), RegexOptions.IgnoreCase).Value; } } } /* * OAuth 2.0 的 Protocol Flow +--------+ +---------------+ | |--(A)- Authorization Request ->| Resource | | | | Owner | | |<-(B)-- Authorization Grant ---| | | | +---------------+ | | | | +---------------+ | |--(C)-- Authorization Grant -->| Authorization | | Client | | Server | | |<-(D)----- Access Token -------| | | | +---------------+ | | | | +---------------+ | |--(E)----- Access Token ------>| Resource | | | | Server | | |<-(F)--- Protected Resource ---| | +--------+ +---------------+ */
OK
[源码下载]