使用括号和 åäö 的 Twitter 搜索失败
Twitter search using parenthesis and åäö fails
我正在使用下面的代码来搜索 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);
}
要转义 URI 字符串上的查询数据,您必须使用 Uri.EscapeUriString
而不是 Uri.EscapeDataString
,这两个函数非常相似,但在某些情况下有所不同,例如 space char wich一个表示为 +
而另一个表示为 %20
.
因此,结果证明 Uri 编码和解码 urls 的方式有所不同,具体取决于 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();
}
我正在使用下面的代码来搜索 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);
}
要转义 URI 字符串上的查询数据,您必须使用 Uri.EscapeUriString
而不是 Uri.EscapeDataString
,这两个函数非常相似,但在某些情况下有所不同,例如 space char wich一个表示为 +
而另一个表示为 %20
.
因此,结果证明 Uri 编码和解码 urls 的方式有所不同,具体取决于 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();
}