簡體   English   中英

WP8 HttpClient.PostAsync永遠不會返回結果

[英]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
}

注意,我正在使用asyncawait 您將需要公開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.Waitawait因此最終將導致代碼死鎖

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

像這樣:

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

您可以對“請勿阻止異步代碼”盡可能使用ConfigureAwait(false)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM