Problems with Twitter POST using api 1.1

We just changed api 1.1 on Twitter and now Tweeting does not work and returns an error “The remote server responded with an error: (400)“ Bad request. ”Studying SO says that this is related to authentication, but we are sending accessToken and the secret we just got on the login page. All of this worked fine with api 1.0. The code is

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()); } } 

Basically, I would like to be able to Tweet without using any third-party libraries like Twitterizer (even using TweetStation, it seems like with Api 1.1) - for sure it can't be that hard!

Any help is much appreciated as it looks a bit like a brick wall at the moment - I'm also pretty new to C #, which doesn't help ...

Edited to show code that was not previously clear.

+4
source share
4 answers

Finally found a solution, as usual, with most of these things, it was pretty simple. The code below is

  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 ()); } 

With Twitter api 1.0, I used WebClient to publish, which does not work with api 1.1, and it seems that the reason for this is that you cannot set the ContentType or ServicePoint.Expect100Continue properties - without these as I set them, the request is sent as (401 ) unauthorized. Nothing to do with coding problems at the end.

Thanks to others for the various helper methods.

+5
source

I had the same problem:

This is exactly what you need to do here:

Authenticate and request a user timeline with the Twitter API 1.1 oAuth

I created a project for this: https://github.com/andyhutch77/oAuthTwitterTimeline

It also includes a demo of MVC, a web application, and a console.

+3
source

I ran into this problem or at least one striking similarity (from my point of view noob), recently for the application that I am creating. What seemed to solve this for me (by looking at the tool on dev.twitter.com) simply got rid of the quotes around the parameter names so that (in your case):

I notice that in fact you do not have quotes around the names of your parameters. However, it bothers me that you send authentication data twice (hence my erroneous post). This works for me without doing this, and I briefly parsed it and found: https://dev.twitter.com/discussions/12322#comment-27120 , which confirms this, there may be a problem that generates an Autetication error.

0
source

400 means that you are not authenticated. I recommend getting a user context.

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

-1
source

Source: https://habr.com/ru/post/1487461/