4

我们刚刚更改为 Twitter api 1.1,现在 Tweeting 不起作用并返回错误“远程服务器返回错误:(400) 错误请求。” 对 SO 的研究表明这与身份验证有关,但我们正在发送我们刚刚从登录页面获得的 accessToken 和 secret。使用 api 1.0 一切正常。代码是 -

    public void Tweet(Action<string> response, string message)
    {
        StringBuilder sb = new StringBuilder();
        sb.Append("POST&");
        sb.Append(Uri.EscapeDataString(_postUrl));
        sb.Append("&");

        string oauthNonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
        string timeStamp = MakeTimestamp();

        var dict = new SortedDictionary<string, string>
        {
            { "oauth_consumer_key", _oAuthConfig.ConsumerKey },
            { "oauth_nonce", oauthNonce },
            { "oauth_signature_method", "HMAC-SHA1" },
            { "oauth_timestamp", timeStamp },
            { "oauth_token", _accessToken },
            { "oauth_version", "1.0" },
        };

        foreach (var keyValuePair in dict)
        {
            sb.Append(Uri.EscapeDataString(string.Format("{0}={1}&", keyValuePair.Key, keyValuePair.Value)));
        }

        string encodedMessage = EscapeAdditionalChars(Uri.EscapeDataString(message));
        sb.Append(Uri.EscapeDataString("status=" + encodedMessage));

        string signatureBaseString = sb.ToString();


        // create the signature

        string signatureKey = Uri.EscapeDataString(_oAuthConfig.ConsumerSecret) + "&" + Uri.EscapeDataString(_accessTokenSecret);

        var hmacsha1 = new HMACSHA1(new ASCIIEncoding().GetBytes(signatureKey));

        string signatureString = Convert.ToBase64String(hmacsha1.ComputeHash(new ASCIIEncoding().GetBytes(signatureBaseString)));


        // create the headers

        string authorizationHeaderParams = String.Empty;

        authorizationHeaderParams += "OAuth ";
        authorizationHeaderParams += "oauth_consumer_key=\"" + _oAuthConfig.ConsumerKey + "\", ";
        authorizationHeaderParams += "oauth_nonce=\"" + oauthNonce + "\", ";
        authorizationHeaderParams += "oauth_signature=\"" + Uri.EscapeDataString(signatureString) + "\", ";
        authorizationHeaderParams += "oauth_signature_method=\"" + "HMAC-SHA1" + "\", ";
        authorizationHeaderParams += "oauth_timestamp=\"" + timeStamp + "\", ";
        authorizationHeaderParams += "oauth_token=\"" + _accessToken + "\", ";
        authorizationHeaderParams += "oauth_version=\"" + "1.0" + "\"";

        string messageToPost = EscapeAdditionalChars(SpacesToPlusSigns(message));


        // initialise the WebClient

        WebClient client = new WebClient();

        client.Headers [HttpRequestHeader.Authorization] = authorizationHeaderParams;

        client.UploadDataCompleted += (s, eArgs) =>
        {
            if (eArgs.Error == null)
                response(DefaultSuccessMessage());
            else
                response(eArgs.Error.Message);
        };

        try
        {
            Uri uri = new Uri(_postUrl);
            try
            {
                client.UploadDataAsync(uri, "POST", Encoding.UTF8.GetBytes("status=" + messageToPost));
            }
            catch (WebException e)
            {
                Log.Info("TwitterService->Tweet web error: " + e.Message);
                response(DefaultErrorMessage());
            }
            catch (Exception e)
            {
                // Can happen if we had already favorited this status
                Log.Info("TwitterService->Tweet error: " + e.Message);
                response(DefaultErrorMessage());
            }
        }
        catch (WebException e)
        {
            Log.Info("TwitterService->Tweet web error 2: " + e.Message);
            response(DefaultErrorMessage());
        }
        catch (Exception e)
        {
            Log.Info("TwitterService->Tweet error 2: " + e.Message);
            response(DefaultErrorMessage());
        }
    }

基本上,我希望能够在不使用任何 3rd 方库(如 Twitterizer)的情况下发布推文(甚至 TweetStation 似乎被 api 1.1 破坏了)——当然这不会那么困难!

非常感谢任何帮助,因为它现在感觉有点像一堵砖墙 - 我对 c# 也很陌生,这无济于事......

编辑显示以前不清楚的代码。

4

4 回答 4

5

终于找到了解决方案,与大多数这些事情一样,这很简单。下面的代码 -

    public void Tweet(Action<string> response, string message)
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendFormat ("status={0}", PercentEncode(message));

        string content = sb.ToString();


        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_postUrl);

        request.Headers.Add("Authorization", AuthorizeRequest(_accessToken, _accessTokenSecret, "POST", new Uri(_postUrl), content));
        request.ContentType = "application/x-www-form-urlencoded";
        request.ServicePoint.Expect100Continue = false;
        request.Method = "POST";


        try
        {
            try
            {
                using (Stream stream = request.GetRequestStream())
                {
                    Byte[] streamContent = Encoding.UTF8.GetBytes("status=" + PercentEncode(message));
                    stream.Write(streamContent, 0, streamContent.Length);
                }


                HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse();

                string contents = "";
                using (Stream stream = webResponse.GetResponseStream())
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        contents = reader.ReadToEnd();
                    }

                Console.WriteLine("Twitter response: " + contents);

                response(DefaultSuccessMessage());

            }
            catch (WebException e)
            {
                Log.Info("TwitterService->Tweet web error: " + e.Message);
                response(DefaultErrorMessage());
            }
            catch (Exception e)
            {
                // Can happen if we had already favorited this status
                Log.Info("TwitterService->Tweet error: " + e.Message);
                response(DefaultErrorMessage());
            }
        }
        catch (WebException e)
        {
            Log.Info("TwitterService->Tweet web error 2: " + e.Message);
            response(DefaultErrorMessage());
        }
        catch (Exception e)
        {
            Log.Info("TwitterService->Tweet error 2: " + e.Message);
            response(DefaultErrorMessage());
        }
    }


    private string AuthorizeRequest(string oauthToken, string oauthTokenSecret, string method, Uri uri, string data)
    {
        string oauthNonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));

        var headers = new Dictionary<string, string>()
        {
            { "oauth_consumer_key", _oAuthConfig.ConsumerKey },
            { "oauth_nonce", oauthNonce },
            { "oauth_signature_method", "HMAC-SHA1" },
            { "oauth_timestamp", MakeTimestamp() },
            { "oauth_token", oauthToken },
            { "oauth_verifier", PercentEncode(_authorizationVerifier) },
            { "oauth_version", "1.0A" }
        };
        var signatureHeaders = new Dictionary<string,string>(headers);

        // Add the data and URL query string to the copy of the headers for computing the signature
        if (data != null && data != "")
        {
            var parsed = HttpUtility.ParseQueryString(data);
            foreach (string k in parsed.Keys)
            {
                signatureHeaders.Add(k, PercentEncode(parsed [k]));
            }
        }

        var nvc = HttpUtility.ParseQueryString(uri.Query);
        foreach (string key in nvc)
        {
            if (key != null)
                signatureHeaders.Add(key, PercentEncode(nvc [key]));
        }

        string signature = MakeSignature (method, uri.GetLeftPart(UriPartial.Path), signatureHeaders);
        string compositeSigningKey = MakeSigningKey(_oAuthConfig.ConsumerSecret, oauthTokenSecret);
        string oauth_signature = MakeOAuthSignature(compositeSigningKey, signature);

        headers.Add ("oauth_signature", PercentEncode(oauth_signature));


        return HeadersToOAuth(headers);
    }


    private static string PercentEncode (string s)
    {
        var sb = new StringBuilder ();

        foreach (byte c in Encoding.UTF8.GetBytes (s))
        {
            if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '~')
                sb.Append ((char) c);
            else
            {
                sb.AppendFormat ("%{0:X2}", c);
            }
        }
        return sb.ToString ();
    }


    private static string MakeTimestamp ()
    {
        return ((long) (DateTime.UtcNow - _unixBaseTime).TotalSeconds).ToString ();
    }

    private static string MakeSignature (string method, string base_uri, Dictionary<string,string> headers)
    {
        var items = from k in headers.Keys orderby k 
            select k + "%3D" + PercentEncode (headers [k]);

        return method + "&" + PercentEncode (base_uri) + "&" + 
            string.Join ("%26", items.ToArray ());
    }

    private static string MakeSigningKey (string consumerSecret, string oauthTokenSecret)
    {
        return PercentEncode (consumerSecret) + "&" + (oauthTokenSecret != null ? PercentEncode (oauthTokenSecret) : "");
    }

    private static string MakeOAuthSignature (string compositeSigningKey, string signatureBase)
    {
        var sha1 = new HMACSHA1 (Encoding.UTF8.GetBytes (compositeSigningKey));

        return Convert.ToBase64String (sha1.ComputeHash (Encoding.UTF8.GetBytes (signatureBase)));
    }

    private static string HeadersToOAuth (Dictionary<string,string> headers)
    {
        return "OAuth " + String.Join (",", (from x in headers.Keys select String.Format ("{0}=\"{1}\"", x, headers [x])).ToArray ());
    }

使用 Twitter api 1.0,我使用 WebClient 发布,这不适用于 api 1.1,似乎原因是您无法设置 ContentType 或 ServicePoint.Expect100Continue 属性 - 没有这些设置为我'已经设置它们,请求被发送回(401)未经授权。与编码问题到底无关。

感谢其他人提供各种帮助方法。

于 2013-07-02T09:22:31.390 回答
3

我有完全相同的问题:

这正是您需要在这里做的:

使用 Twitter API 1.1 oAuth 验证和请求用户的时间线

我为此创建了一个项目:https ://github.com/andyhutch77/oAuthTwitterTimeline

它还包括一个 MVC、Web 应用程序和控制台演示。

于 2013-07-02T12:46:04.537 回答
0

我最近在我正在构建的一个应用程序中遇到了这个问题,或者至少有一个惊人的类似问题(从我的菜鸟的角度来看)。似乎为我解决了这个问题(在查看了 dev.twitter.com 上的工具之后)只是去掉了参数名称周围的引号,这样(在你的情况下):

我注意到您实际上在参数名称周围没有引号。但是,您两次发送身份验证详细信息让我感到困惑(因此我发错了帖子。)它在不这样做的情况下对我有用,我用谷歌搜索了一下,发现:https ://dev.twitter.com/discussions/12322#comment- 27120,它确认这可能是产生身份验证错误的问题。

于 2013-07-01T20:40:33.693 回答
-1

400 表示您未通过身份验证。我建议获取用户上下文。

https://dev.twitter.com/docs/auth/oauth#user-context

于 2013-06-30T20:27:39.030 回答