WP8 HttpClient.PostAsync永远不会返回结果

我有一个Windows Phone 8应用程序,我正在调用等待HttpClient.PostAsync,它永远不会返回结果。 它只是坐在那里挂起。 如果我从控制台应用程序运行完全相同的代码,它几乎立即返回结果。 执行该工作的所有代码都驻留在可移植的类库中。 我很感激你能提供的任何帮助。 我在这个状态下发现的所有其他问题都使用await client.PostAsync,我已经在做了。

我的类库中的代码是这样的:

public class Authenticator { private const string ApiBaseUrl = "http://api.fitbit.com"; private const string Callback = "http://myCallbackUrlHere"; private const string SignatureMethod = "HMAC-SHA1"; private const string OauthVersion = "1.0"; private const string ConsumerKey = "myConsumerKey"; private const string ConsumerSecret = "myConsumerSecret"; private const string RequestTokenUrl = "http://api.fitbit.com/oauth/request_token"; private const string AccessTokenUrl = "http://api.fitbit.com/oauth/access_token"; private const string AuthorizeUrl = "http://www.fitbit.com/oauth/authorize"; private string requestToken; private string requestTokenSecret; public string GetAuthUrlToken() { return GenerateAuthUrlToken().Result; } private async Task GenerateAuthUrlToken() { var httpClient = new HttpClient { BaseAddress = new Uri(ApiBaseUrl) }; var timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0); var oauthTimestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString(CultureInfo.InvariantCulture); var oauthNonce = DateTime.Now.Ticks.ToString(CultureInfo.InvariantCulture); var authHeaderValue = string.Format( "oauth_callback=\"{0}\",oauth_consumer_key=\"{1}\",oauth_nonce=\"{2}\"," + "oauth_signature=\"{3}\",oauth_signature_method=\"{4}\"," + "oauth_timestamp=\"{5}\",oauth_version=\"{6}\"", Uri.EscapeDataString(Callback), Uri.EscapeDataString(ConsumerKey), Uri.EscapeDataString(oauthNonce), Uri.EscapeDataString(this.CreateSignature(RequestTokenUrl, oauthNonce, oauthTimestamp, Callback)), Uri.EscapeDataString(SignatureMethod), Uri.EscapeDataString(oauthTimestamp), Uri.EscapeDataString(OauthVersion)); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( "OAuth", authHeaderValue); var content = new StringContent(string.Empty); var response = await httpClient.PostAsync(RequestTokenUrl, content); if (response.StatusCode != HttpStatusCode.OK) { throw new Exception("Request Token Step Failed"); } var responseContent = await response.Content.ReadAsStringAsync(); var responseItems = responseContent.Split(new[] { '&' }); this.requestToken = responseItems[0]; this.requestTokenSecret = responseItems[1]; var url = string.Format("{0}?{1}&display=touch", AuthorizeUrl, this.requestToken); return url; } public string CreateSignature(string url, string nonce, string timestamp, string callback) { // code removed return signatureString; } private static byte[] StringToAscii(string s) { // code removed return retval; } } 

我有一个调用此库的控制台应用程序,它没有问题:

 public class Program { public static void Main(string[] args) { var o = new Program(); o.LinkToFitbit(); } public void LinkToFitbit() { var authenticator = new Authenticator(); // this works and returns the url immediately var url = authenticator.GetAuthUrlToken(); // code removed } } 

当我从我的WP8应用程序运行时,它只是在它到达库中的这一行时挂起:

 var response = await httpClient.PostAsync(RequestTokenUrl, content); 

这是我的WP8代码:

 public partial class FitbitConnector : PhoneApplicationPage { public FitbitConnector() { InitializeComponent(); this.AuthenticateUser(); } private void AuthenticateUser() { var authenticator = new Authenticator(); var url = authenticator.GetAuthUrlToken(); // code removed } } 

这一行阻止了UI线程:

 public string GetAuthUrlToken() { return GenerateAuthUrlToken().Result; } 

await httpClient.PostAsync()之后的代码需要在UI线程中执行,但是由于被阻止而无法执行。

所以,替换这个:

 private void AuthenticateUser() { var authenticator = new Authenticator(); var url = authenticator.GetAuthUrlToken(); // code removed } 

有了这个:

 private async void AuthenticateUser() { var authenticator = new Authenticator(); var url = await authenticator.GenerateAuthUrlToken(); // code removed } 

注意我正在使用asyncawait 。 您需要将GenerateAuthUrlToken()公开。 您可以删除GetAuthUrlToken()

简而言之, Task.Result不是异步的。

你可以发布CreateSignature和StringToAscii代码吗? 我被困在基本的oAuth。

我可以获取请求令牌,但在执行访问令牌请求时获得“无效的OAuth签名”。

Nexus,你走了。 我创建了一个OauthHelper类,用于构建我需要的部分。 看看下面。 我希望这有帮助。

 public static class OauthHelper { public const string ConsumerKey = "MyKey"; public const string ConsumerSecret = "MySecret"; public const string UriScheme = "https"; public const string HostName = "api.somePlace.com"; public const string RequestPath = "/services/api/json/1.3.0"; public const string OauthSignatureMethod = "HMAC-SHA1"; public const string OauthVersion = "1.0"; public static string BuildRequestUri(Dictionary requestParameters) { var url = GetNormalizedUrl(UriScheme, HostName, RequestPath); var allParameters = new List(requestParameters.Select(entry => new QueryParameter(entry.Key, entry.Value))); var normalizedParameters = NormalizeParameters(allParameters); var requestUri = string.Format("{0}?{1}", url, normalizedParameters); return requestUri; } public static AuthenticationHeaderValue CreateAuthorizationHeader( string oauthToken, string oauthNonce, string oauthTimestamp, string oauthSignature) { var normalizedUrl = GetNormalizedUrl(UriScheme, HostName, RequestPath); return CreateAuthorizationHeader(oauthToken, oauthNonce, oauthTimestamp, oauthSignature, normalizedUrl); } public static AuthenticationHeaderValue CreateAuthorizationHeader( string oauthToken, string oauthNonce, string oauthTimestamp, string oauthSignature, string realm) { if (string.IsNullOrWhiteSpace(oauthToken)) { oauthToken = string.Empty; } if (string.IsNullOrWhiteSpace(oauthTimestamp)) { throw new ArgumentNullException("oauthTimestamp"); } if (string.IsNullOrWhiteSpace(oauthNonce)) { throw new ArgumentNullException("oauthNonce"); } if (string.IsNullOrWhiteSpace(oauthSignature)) { throw new ArgumentNullException("oauthSignature"); } var authHeaderValue = string.Format( "realm=\"{0}\"," + "oauth_consumer_key=\"{1}\"," + "oauth_token=\"{2}\"," + "oauth_nonce=\"{3}\"," + "oauth_timestamp=\"{4}\"," + "oauth_signature_method=\"{5}\"," + "oauth_version=\"{6}\"," + "oauth_signature=\"{7}\"", realm, Uri.EscapeDataString(ConsumerKey), Uri.EscapeDataString(oauthToken), Uri.EscapeDataString(oauthNonce), Uri.EscapeDataString(oauthTimestamp), Uri.EscapeDataString(OauthSignatureMethod), Uri.EscapeDataString(OauthVersion), Uri.EscapeDataString(oauthSignature)); var authHeader = new AuthenticationHeaderValue("OAuth", authHeaderValue); return authHeader; } public static string CreateSignature( string httpMethod, string oauthToken, string oauthTokenSecret, string oauthTimestamp, string oauthNonce, Dictionary requestParameters) { // get normalized url var normalizedUrl = GetNormalizedUrl(UriScheme, HostName, RequestPath); return CreateSignature( httpMethod, oauthToken, oauthTokenSecret, oauthTimestamp, oauthNonce, requestParameters, normalizedUrl); } public static string CreateSignature( string httpMethod, string oauthToken, string oauthTokenSecret, string oauthTimestamp, string oauthNonce, Dictionary requestParameters, string realm) { if (string.IsNullOrWhiteSpace(httpMethod)) { throw new ArgumentNullException("httpMethod"); } if (string.IsNullOrWhiteSpace(oauthToken)) { oauthToken = string.Empty; } if (string.IsNullOrWhiteSpace(oauthTokenSecret)) { oauthTokenSecret = string.Empty; } if (string.IsNullOrWhiteSpace(oauthTimestamp)) { throw new ArgumentNullException("oauthTimestamp"); } if (string.IsNullOrWhiteSpace(oauthNonce)) { throw new ArgumentNullException("oauthNonce"); } var allParameters = new List { new QueryParameter("oauth_consumer_key", ConsumerKey), new QueryParameter("oauth_token", oauthToken), new QueryParameter("oauth_nonce", oauthNonce), new QueryParameter("oauth_timestamp", oauthTimestamp), new QueryParameter("oauth_signature_method", OauthSignatureMethod), new QueryParameter("oauth_version", OauthVersion) }; allParameters.AddRange(requestParameters.Select(entry => new QueryParameter(entry.Key, entry.Value))); // sort params allParameters.Sort(new QueryParameterComparer()); // concat all params var normalizedRequestParameters = NormalizeParameters(allParameters); // create base string var signatureBase = string.Format( "{0}&{1}&{2}", UrlEncode(httpMethod.ToUpperInvariant()), UrlEncode(realm), UrlEncode(normalizedRequestParameters)); var signatureKey = string.Format( "{0}&{1}", UrlEncode(ConsumerSecret), UrlEncode(oauthTokenSecret)); // hash the base string var hmacsha1 = new HMACSHA1(StringToAscii(signatureKey)); var signatureString = Convert.ToBase64String(hmacsha1.ComputeHash(StringToAscii(signatureBase))); return signatureString; } public static string GenerateNonce() { var ts = new TimeSpan(DateTime.Now.Ticks); var ms = ts.TotalMilliseconds.ToString().Replace(".", string.Empty); var nonce = ms; return nonce; } public static string GenerateTimeStamp() { var timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0); var timestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString(CultureInfo.InvariantCulture); return timestamp; } private static string GetNormalizedUrl(string uriScheme, string hostName, string requestPath) { var normalizedUrl = string.Format( "{0}://{1}{2}", uriScheme.ToLowerInvariant(), hostName.ToLowerInvariant(), requestPath); return normalizedUrl; } private static string NormalizeParameters(IList parameters) { var result = new StringBuilder(); for (var i = 0; i < parameters.Count; i++) { var p = parameters[i]; result.AppendFormat("{0}={1}", p.Name, p.Value); if (i < parameters.Count - 1) { result.Append("&"); } } return result.ToString(); } private static byte[] StringToAscii(string s) { var retval = new byte[s.Length]; for (var ix = 0; ix < s.Length; ++ix) { var ch = s[ix]; if (ch <= 0x7f) { retval[ix] = (byte)ch; } else { retval[ix] = (byte)'?'; } } return retval; } private static string UrlEncode(string value) { const string Unreserved = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; var result = new StringBuilder(); foreach (char symbol in value) { if (Unreserved.IndexOf(symbol) != -1) { result.Append(symbol); } else { result.Append('%' + string.Format("{0:X2}", (int)symbol)); } } return result.ToString(); } } public class QueryParameter { public QueryParameter(string name, string value) { this.Name = name; this.Value = value; } public string Name { get; private set; } public string Value { get; private set; } } public class QueryParameterComparer : IComparer { public int Compare(QueryParameter x, QueryParameter y) { return x.Name == y.Name ? string.Compare(x.Value, y.Value) : string.Compare(x.Name, y.Name); } } 

由于您使用.Result.Waitawait这将最终导致代码死锁

您可以在async方法中使用ConfigureAwait(false)防止死锁

像这样:

 var response = await httpClient.PostAsync(RequestTokenUrl, content).ConfigureAwait(false); 

您可以尽可能使用ConfigureAwait(false)来禁止阻止异步代码。