[英]WP8 HttpClient.PostAsync never returns result
我有一個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<string> 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;
}
需要在UI線程中執行await httpClient.PostAsync()
之后的代碼,但由於被阻止而無法執行。
因此,替換為:
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
}
注意,我正在使用async
和await
。 您將需要公開GenerateAuthUrlToken()
。 您可以擦除GetAuthUrlToken()
。
簡而言之, Task<T>.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<string, string> requestParameters)
{
var url = GetNormalizedUrl(UriScheme, HostName, RequestPath);
var allParameters = new List<QueryParameter>(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<string, string> 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<string, string> 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<QueryParameter>
{
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<QueryParameter> 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<QueryParameter>
{
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
或.Wait
或await
因此最終將導致代碼死鎖 。
您可以在async
方法中使用ConfigureAwait(false)
來防止死鎖
像這樣:
var response = await httpClient.PostAsync(RequestTokenUrl, content).ConfigureAwait(false);
您可以對“請勿阻止異步代碼”盡可能使用
ConfigureAwait(false)
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.