我正在使用以下代码搜索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);
}
答案 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();
}