使用括号和åäö的Twitter搜索失败

时间:2017-01-27 11:04:34

标签: c# .net twitter oauth twitter-oauth

我正在使用以下代码搜索twitter。大多数搜索工作得很好,但有几个搜索没有。请参阅下面的列表。这是我的问题(签名?)或者这可能被认为是API中的错误?

我得到的错误:

{"errors":[{"message":"Could not authenticate you","code":32}]}

搜索我尝试过:

"(Malmö OR Lund) (Sverige OR Skåne)" // Fails
"(Malmo OR lund) (Skane OR Sweden)" // Works, returns correct result
"Malmö" // Works

我的代码:

public async Task SearchTwitter(string query)
{
    var oauth_token = "xxxxxxxxxxxx";
    var oauth_token_secret = "xxxxxxxxxxxx";
    var oauth_consumer_key = "xxxxxxxxxxxx";
    var oauth_consumer_secret = "xxxxxxxxxxxx";

    var baseUrl = "https://api.twitter.com/1.1/search/tweets.json";

    var parameters = new Dictionary<string, string>
    {
        {"tweet_mode", "extended"},
        {"result_type", "recent"},
        {"count", 100.ToString()},
        {"q", query},

        {"oauth_consumer_key", oauth_consumer_key},
        {"oauth_timestamp", DateTime.UtcNow.ToUnixStringFromDateTime()},
        {"oauth_nonce", Guid.NewGuid().ToString("N")},
        {"oauth_version", "1.0"},
        {"oauth_signature_method", "HMAC-SHA1"},
        {"oauth_token", oauth_token}
    };

    var sortedParameterString =
        string.Join("&",
            (from parm in parameters
             orderby parm.Key
             select Uri.EscapeDataString(parm.Key) + "=" + Uri.EscapeDataString(parameters[parm.Key]))
                .ToArray());

    var signatureBaseString = "GET&" + Uri.EscapeDataString(baseUrl) + "&" + Uri.EscapeDataString(sortedParameterString);

    var signingKey = Uri.EscapeDataString(oauth_consumer_secret) + "&" + Uri.EscapeDataString(oauth_token_secret);

    string oauth_signature;
    using (HMACSHA1 hasher = new HMACSHA1(ASCIIEncoding.ASCII.GetBytes(signingKey)))
    {
        oauth_signature = Convert.ToBase64String(hasher.ComputeHash(ASCIIEncoding.ASCII.GetBytes(signatureBaseString)));
    }

    var headerString = "OAuth " + string.Join(", ", parameters.Where(kv => kv.Key.StartsWith("oauth")).Select(kv => kv.Key + "=\"" + Uri.EscapeDataString(kv.Value) + "\"")) + ", oauth_signature=\"" + Uri.EscapeDataString(oauth_signature) + "\"";

    var uri = new Uri(baseUrl + $"?count={Uri.EscapeDataString(100.ToString())}&q={Uri.EscapeDataString(query)}&result_type={Uri.EscapeDataString("recent")}&tweet_mode={Uri.EscapeDataString("extended")}");

    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
    httpRequestMessage.Headers.Add("Authorization", headerString);
    httpRequestMessage.Headers.ExpectContinue = false;

    var httpResponseMessage = await GetHttpClient().SendAsync(httpRequestMessage).ConfigureAwait(false);
    var resultString = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
    if (httpResponseMessage.IsSuccessStatusCode)
    {
        // Request succeeded
    }
    else {
        // Request failed
    }
}

private static HttpClient GetHttpClient()
{
    var handler = new HttpClientHandler();
    if (handler.SupportsAutomaticDecompression)
    {
        handler.AutomaticDecompression = DecompressionMethods.GZip;
    }

    return new HttpClient(handler);
}

2 个答案:

答案 0 :(得分:1)

要转义URI字符串上的查询数据,必须使用Uri.EscapeUriString而不是Uri.EscapeDataString,这两个函数非常相似,但在某些情况下会有差异,例如空格字符,表示为{{1另一个是+

答案 1 :(得分:0)

因此,Uri根据unicode字符编码和解码网址的方式不同:https://github.com/dotnet/corefx/issues/15865

我的解决方案是解析Uri.AbsoluteUri(以相同,不一致的方式对url进行编码)的内容,并在计算身份验证的签名时使用它。而不是像以前那样使用Uri.EscapeDataString。

public async Task SearchTwitter(string query)
    {
        var oauth_token = "xxxxxx";
        var oauth_token_secret = "xxxxxx";
        var oauth_consumer_key = "xxxxxx";
        var oauth_consumer_secret = "xxxxxx";

        var baseUrl = "https://api.twitter.com/1.1/search/tweets.json";

        var oauthParameters = new Dictionary<string, string>
        {
            {"oauth_consumer_key", Uri.EscapeDataString(oauth_consumer_key)},
            {"oauth_timestamp", Uri.EscapeDataString(DateTime.UtcNow.ToUnixStringFromDateTime())},
            {"oauth_nonce", Uri.EscapeDataString(Guid.NewGuid().ToString("N"))},
            {"oauth_version", Uri.EscapeDataString("1.0")},
            {"oauth_signature_method", Uri.EscapeDataString("HMAC-SHA1")},
            {"oauth_token", Uri.EscapeDataString(oauth_token)}
        };

        var uri = new Uri(baseUrl + $"?count={Uri.EscapeDataString(100.ToString())}&q={Uri.EscapeDataString(query)}&result_type={Uri.EscapeDataString("recent")}&tweet_mode={Uri.EscapeDataString("extended")}");

        var queryString = uri.AbsoluteUri;
        queryString = queryString.Split('?')[1];
        var queryStringParameters = queryString.Split('&');
        var queryStringParameterDictionary = queryStringParameters.Select(queryStringParameter => queryStringParameter.Split('=')).ToDictionary(keyValue => keyValue[0], keyValue => keyValue[1]);

        var allParameters = queryStringParameterDictionary.Concat(oauthParameters).GroupBy(d => d.Key).ToDictionary(d => d.Key, d => d.First().Value);
        var sortedParameterString =
            string.Join("&",
                (from parm in allParameters
                 orderby parm.Key
                 select parm.Key + "=" + parm.Value)
                    .ToArray());

        var signatureBaseString = "GET&" + Uri.EscapeDataString(baseUrl) + "&" + Uri.EscapeDataString(sortedParameterString);

        var signingKey = Uri.EscapeDataString(oauth_consumer_secret) + "&" + Uri.EscapeDataString(oauth_token_secret);

        string oauth_signature;
        using (HMACSHA1 hasher = new HMACSHA1(Encoding.ASCII.GetBytes(signingKey)))
        {
            oauth_signature = Convert.ToBase64String(hasher.ComputeHash(Encoding.ASCII.GetBytes(signatureBaseString)));
        }

        var headerString = "OAuth " + string.Join(", ", oauthParameters.Select(kv => kv.Key + "=\"" + kv.Value + "\"")) + ", oauth_signature=\"" + Uri.EscapeDataString(oauth_signature) + "\"";


        var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
        httpRequestMessage.Headers.Add("Authorization", headerString);
        httpRequestMessage.Headers.ExpectContinue = false;

        var httpResponseMessage = await GetHttpClient().SendAsync(httpRequestMessage);

        var resultString = await httpResponseMessage.Content.ReadAsStringAsync();
    }