简体   繁体   中英

Twitter API 1.1 Search with C# (in a PCL)

I have a Windows 8 and a Windows Phone 8 app that serves as a social media dashboard and it get's tweets using the twitter search api. This was originally implemented using the Twitter API 1 and worked with no authentication so since it didn't need any additional 3rd party libs it was placed in the PCL part of the solution. For the same reason I wanted the update to using 1.1 API to be done without any libs. I have found various samples online on the Twitter dev community and on codeproject and modified them to work with the PCL.

Here is the part of the code attempting to search twitter:

private static async Task<string> doTheTwitterMagic(string query)
    {
        var oauth_token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
        var oauth_token_secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
        var oauth_consumer_key = "XXXXXXXXXXXXXXXXX";
        var oauth_consumer_secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

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

        // oauth implementation details
        var oauth_version = "1.0";
        var oauth_signature_method = "HMAC-SHA1";

        Encoding ascii = new AsciiEncoding();

        // unique request details
        var oauth_nonce = Convert.ToBase64String(
            ascii.GetBytes(DateTime.Now.Ticks.ToString()));
        var timeSpan = DateTime.UtcNow
            - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
        var oauth_timestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString();


        // create oauth signature
        var baseFormat = "oauth_consumer_key={0}&oauth_nonce={1}&oauth_signature_method={2}" +
                        "&oauth_timestamp={3}&oauth_token={4}&oauth_version={5}&q={6}";

        var baseString = string.Format(baseFormat,
                                    oauth_consumer_key,
                                    oauth_nonce,
                                    oauth_signature_method,
                                    oauth_timestamp,
                                    oauth_token,
                                    oauth_version,
                                    Uri.EscapeDataString(query)
                                    );

        baseString = string.Concat("GET&", Uri.EscapeDataString(resource_url), "&", Uri.EscapeDataString(baseString));

        var compositeKey = string.Concat(Uri.EscapeDataString(oauth_consumer_secret),
                                "&", Uri.EscapeDataString(oauth_token_secret));

        string oauth_signature;
        using (HMACSHA1 hasher = new HMACSHA1(ascii.GetBytes(compositeKey)))
        {
            oauth_signature = Convert.ToBase64String(
                hasher.ComputeHash(ascii.GetBytes(baseString)));
        }

        // create the request header
        var headerFormat = "OAuth oauth_nonce=\"{0}\", oauth_signature_method=\"{1}\", " +
                           "oauth_timestamp=\"{2}\", oauth_consumer_key=\"{3}\", " +
                           "oauth_token=\"{4}\", oauth_signature=\"{5}\", " +
                           "oauth_version=\"{6}\"";

        var authHeader = string.Format(headerFormat,
                                Uri.EscapeDataString(oauth_nonce),
                                Uri.EscapeDataString(oauth_signature_method),
                                Uri.EscapeDataString(oauth_timestamp),
                                Uri.EscapeDataString(oauth_consumer_key),
                                Uri.EscapeDataString(oauth_token),
                                Uri.EscapeDataString(oauth_signature),
                                Uri.EscapeDataString(oauth_version)
                        );

        return authHeader;
    }
public static async Task<List<TwitterObject>> GetTweetsFromSearchAsync(string query)
    {
        try
        {
            string authHeader = await doTheTwitterMagic(query);

            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create("https://api.twitter.com/1.1/search/tweets.json?q="+query);
            webRequest.Headers[HttpRequestHeader.Authorization] = authHeader;
            webRequest.ContentType = "application/x-www-form-urlencoded";
            webRequest.Method = "GET";

            var webResponse = await webRequest.GetResponseAsync();
            var json = string.Empty;
            using (var reader = new StreamReader(webResponse.GetResponseStream()))
                json = await reader.ReadToEndAsync();

            var jsonObject = JObject.Parse(json);
            var results = new List<TwitterObject>();
            foreach (JToken token in jsonObject["results"])
                results.Add(token.ToObject<TwitterObject>());
            return results;
        }
        catch (Exception e) { throw e; }
    }

NB:The AsciiEncoding is a custom implementation of ASCIIEncoding since it's not available in the PCL at the moment.

Now I attempted the standard debugging and the first thing I looked at was the Twitter OAuth tool and here are the results of OAuth tools vs my app

OAuth Tool Signature base string:

GET&https%3A%2F%2Fapi.twitter.com%2F1.1%2Fsearch%2Ftweets.json&oauth_consumer_key
%3DywWuYGASkHMsHsC22Vpw%26oauth_nonce%3D3c30afb0c2a391d3b4e4ba3da057c2ca%26oauth_
signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1371309728%26oauth_token%3D16702
294-3WNjfZ9p0QWhEDPL1jR14s5safYhiruowHa3cXWbM%26oauth_version%3D1.0%26q%3Dcsmcr

My app Signature base string:

GET&https%3A%2F%2Fapi.twitter.com%2F1.1%2Fsearch%2Ftweets.json&oauth_consumer_key
%3DywWuYGASkHMsHsC22Vpw%26oauth_nonce%3DNjM1MDY5MTA1MzA2MTU1NTgz%26oauth_signatur
e_method%3DHMAC-SHA1%26oauth_timestamp%3D1371310131%26oauth_token%3D16702294-3WNj
fZ9p0QWhEDPL1jR14s5safYhiruowHa3cXWbM%26oauth_version%3D1.0%26q%3D%2540csmcr

And at the first look the only difference being the nonce but that shouldn't be an issue Edit note: I have just noticed that there is a small difference in the above at the end of the both where in the OAuth Tool one I have %26q%3Dcsmcr and my app generates %26q%3D%2540csmcr but that's just since I forgot to include a character in the query in the OAuth tool. After adding the character the string became the same at the end.

OAuth Tool Authorization header:

Authorization: OAuth oauth_consumer_key="ywWuYGASkHMsHsC22Vpw",    
oauth_nonce="3c30afb0c2a391d3b4e4ba3da057c2ca", 
oauth_signature="tofmGp6qaAUAPZnQHhQlVFWN40M%3D", 
oauth_signature_method="HMAC-SHA1", oauth_timestamp="1371309728", 
oauth_token="16702294-3WNjfZ9p0QWhEDPL1jR14s5safYhiruowHa3cXWbM", 
oauth_version="1.0"

My app Authorization header:

Authorization: OAuth oauth_nonce="NjM1MDY5MDk5NTY5Nzc1MTc5", 
oauth_signature_method="HMAC-SHA1", oauth_timestamp="1371309557", 
oauth_consumer_key="ywWuYGASkHMsHsC22Vpw", 
oauth_token="16702294-3WNjfZ9p0QWhEDPL1jR14s5safYhiruowHa3cXWbM", 
oauth_signature="q1544JDPUXdUwY3fmEk3mtGGEwk%3D", oauth_version="1.0"

Again not much difference other than the nonce and the signature. The order of the key-value pairs seems to make no difference as I tested with rearranging them and still had the same problem.

So the problem that I have is that when the function runs I always get the 401 Unauthorized error with the

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

error.

Any pointers would be very much appreciated.

Edit: Did some more testing with this, took the nonce and the timestamp generated by the Twitter OAuth tool and just hardcoded them in my app to check if the signature was fine and it turned out it was, I was getting the same oauth_signature as the one in the OAuth tool. That would mean that the Auth header should also be totally fine. So I guess the problem is somewhere in my request.

(Answered by the OP in a question edit. Converted to a community wiki answer. See Question with no answers, but issue solved in the comments (or extended in chat) )

The OP wrote:

Problem solved by replacing the httpwebrequest implementation with the httpclient . The issue was that the Expect:100-Continue header was being added, and twitter doesn't support this. Due to limitations in PCL it wasn't possible to disable this in httpwebrequest replacing it all with a httpclie nt was a way to go. Hope this helps anyone who experiences the same issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM