2010-12-13 79 views
3

Je télécharge des photos sur TwitPic, en utilisant OAuth, à partir d'une application .NET écrite en C#.en utilisant TwitPic + OAuth pour télécharger une photo + tweet sur Twitter (.NET C#) - pourquoi pas de tweet?

Le truc oAuth est un peu compliqué. J'ai trouvé deux bits de code. NET pour le gérer, mais je n'étais pas satisfait non plus. DotNetOpenAuth semblait être assez lourd, plus que nécessaire. (Je veux juste faire des signatures oAuth et des demandes de jetons). Le code OAuthBase.cs semblait confus et inélégant pour moi. Je devais passer 6 paramètres de chaîne à des méthodes, et si je devais en déranger, malheur à moi.

J'ai donc écrit du code pour le faire moi-même, c'est assez petit et ça semble fonctionner. Cela fonctionne pour acquérir des "jetons de requête". Il fonctionne pour ouvrir la page Web d'autorisation et pour acquérir des «jetons d'accès». Il fonctionne également pour télécharger des photos sur TwitPic.

Toutes les réponses HTTP reviennent 200 ou 201.

Le upload HTTP message ressemble à ceci:

POST http://api.twitpic.com/2/upload.json HTTP/1.1 
Content-Type: multipart/form-data; boundary=48cb9a6d-1f1d-432d-b6e3-307e32e8228a 
X-Auth-Service-Provider: https://api.twitter.com/1/account/verify_credentials.json 
X-Verify-Credentials-Authorization: OAuth realm="http://api.twitter.com/", 
    oauth_consumer_key="Dv1er93yKzEMn74hZfPmJA", 
    oauth_nonce="51fi305k", 
    oauth_signature="4oWcmZcd%2F%2F81JslJ70xFXFm8%2BQs%3D", 
    oauth_signature_method="HMAC-SHA1", 
    oauth_timestamp="1292277715", 
    oauth_token="59152613-z8EP4GoYS1Mgo3E29JfIqBnyTRlruAJs8Bkvs3q0T", 
    oauth_version="1.0" 
Host: api.twitpic.com 
Content-Length: 14605 
Expect: 100-continue 
Connection: Keep-Alive 

--48cb9a6d-1f1d-432d-b6e3-307e32e8228a 
Content-Disposition: file; name="media"; filename="CropperCapture[10].jpg" 
Content-Type: image/jpeg 
.... 
--48cb9a6d-1f1d-432d-b6e3-307e32e8228a 
Content-Disposition: form-data; name="key" 

twitpic-api-key-here 
--48cb9a6d-1f1d-432d-b6e3-307e32e8228a 
Content-Disposition: form-data; name="message" 

uploaded from Yappy. (at 12/13/2010 5:01:55 PM) 
--48cb9a6d-1f1d-432d-b6e3-307e32e8228a-- 

JSON Je reviens du téléchargement est comme ceci:

{"id":"3f0jeiw5", 
"text":"uploaded from Yappy. (at 12\/13\/2010 5:01:55 PM)", 
"url":"http:\\/twitpic.com\/3f0jeiw5", 
"width":257, 
"height":184, 
"size":14156, 
"type":"jpg", 
"timestamp":"Mon, 13 Dec 2010 22:02:06 +0000", 
"user":{ 
    "id":54591561,"screen_name":"bfavre"} 
} 

Mais le problème est, après avoir téléchargé l'image sur Twitpic, l'image est disponible sur TwitPic, mais le message associé n'apparaît jamais sur Twitter.

Ce qui donne?

J'ai lu dans a random blog post que l'utilisation de TwitPic + oAuth nécessite que je publie le message tweet dans une transaction HTTP séparée, directement sur Twitter. Hein? Je pensais que le but du courrier d'oAuth était de permettre aux consommateurs de faire des choses en mon nom - comme permettre à TwitPic de publier un tweet pour moi.

Des indices?


EDIT
J'apprends un peu plus ici. This blog post à partir de mai 2010 me suggère que l'utilisation d'une valeur pour X-Auth-Service-Provider de https://api.twitter.com/1/account/verify_credentials.json dit à TwitPic d'appeler "verify_credentials.json" sur twitter.com quand il obtient ma demande. Si c'est vraiment juste vérifier mes informations d'identification, cela expliquerait pourquoi aucun Tweet n'est publié. Le poste suggère également que l'échange de cela et de le remplacer par https://api.twitter.com/1/status/update.jsondevrait me permettre de mettre à jour Twitter via TwitPic avec délégation. Mais c'est un post tourné vers l'avenir - il dit que pour obtenir cette capacité, il faut travailler sur Twitter.

Je n'ai pas trouvé d'exemple de message HTTP qui le fait encore. N'importe qui?


MISE À JOUR
Après conversion de la vérification URL à https://api.twitter.com/1/status/update.json et en utilisant POST pour la signature, je reçois un code de réponse 401:

{"errors": 
    [{"code":401, 
    "message":"Could not authenticate you (header rejected by twitter)."}] 
} 

Ceci est essentiellement le même problème que décrit here, in the twitter dev forum.La suggestion à la fin de ce thread était que l'algorithme de calcul de signature est faux, mais je pense que c'est incorrect, car mon algorithme sig fonctionne avec toutes les autres demandes.

Répondre

1

Je ne connais pas la réponse finale, mais je pense que les changements qui ont été décrits comme venant "Real Soon Now" dans Raffi's blog post of May 2010, n'ont pas été réellement faits, pour une raison quelconque.

En fait, en utilisant OAuth, devez poster séparément à TwitPic et Twitter.

Ce n'est pas difficile de le faire sur Twitter, cependant. Faites simplement un POST sur l'URL statuses/update.xml.

private void Tweet(string message) 
{ 
    var twitterUpdateUrlBase = "http://api.twitter.com/1/statuses/update.xml?status="; 
    var url = twitterUpdateUrlBase + UrlEncode(message); 

    var authzHeader = oauth.GenerateAuthzHeader(url, "POST"); 

    var request = (HttpWebRequest)WebRequest.Create(url); 
    request.Method = "POST"; 
    request.PreAuthenticate = true; 
    request.AllowWriteStreamBuffering = true; 
    request.Headers.Add("Authorization", authzHeader); 

    using (var response = (HttpWebResponse)request.GetResponse()) 
    { 
     if (response.StatusCode != HttpStatusCode.OK) 
     MessageBox.Show("There's been a problem trying to tweet:" + 
         Environment.NewLine + 
         response.StatusDescription + 
         Environment.NewLine + 
         Environment.NewLine + 
         "You will have to tweet manually." + 
         Environment.NewLine); 
    } 
} 

Il y a deux parties difficiles à ce code: Premièrement, l'appel UrlEncode(). OAuth spécifie que l'urlencoding doit utiliser des majuscules. La routine .NET intégrée utilise des minuscules. Donc, assurez-vous de mettre en valeur.

La deuxième partie délicate est l'obtention de l'en-tête d'autorisation OAuth. Si vous utilisez un paquet de bibliothèque OAuth, il devrait être assez simple. Pour ceux qui veulent un simple, vous pouvez get one here: OAuth.cs. (Obtenez le downloaD d'OAuthManager)

+0

FYI - J'aime votre bibliothèque OAuth simple. J'ai fait une solution rapide pour gérer les caractères étendus, et l'ai posté ici: http://cropperplugins.codeplex.com/discussions/249415 – russau

+0

Juste après - merci, vos changements sont maintenant dans la bibliothèque OAuth; aussi, il y a une DLL maintenant, que n'importe qui peut télécharger. – Cheeso

1

J'ai travaillé sur l'utilisation de l'API Twitpic pour générer l'intégration oAuth et j'ai trouvé que je pouvais obtenir l'image postée et le commentaire Twitter envoyé avec l'image de Twitpic. Cela affiche une image sur le compte d'utilisateur Twitter de notre chat vidéo et client Twitter Eplixo (http://eplixo.com/m/)

Cependant, je n'arrive pas à obtenir la réponse. Maintenant, je peux vivre avec l'affichage et le téléchargement, mais il serait utile de comprendre comment obtenir les données de réponse pour d'autres parties de l'application.

Voici ce que j'ai. C'est une variante de l'encapsuleur Twipli API

protected void Button1_Click(object sender, EventArgs e) 
{ 

    string ct = img.PostedFile.ContentType.ToString(); 
    string usertoken = Session["usrToken"].ToString(); 
    string userSecret = Session["usrSecret"].ToString(); 
    string conkey = Session["ConsumerKey"].ToString(); 
    string consecret = Session["ConsumerSecret"].ToString(); 
    string twitkey = Session["twitpickey"].ToString(); 

    string _m = m.Text; // This takes the Tweet to be posted 


    HttpPostedFile myFile = img.PostedFile; 
    string fileName = myFile.FileName.ToString(); 

    int nFileLen = myFile.ContentLength; 
    byte[] myData = new byte[nFileLen]; 
    myFile.InputStream.Read(myData, 0, nFileLen); 

    TwitPic tw = new TwitPic(); 
    upres.Text = tw.UploadPhoto(myData, ct, _m, fileName, twitkey, usertoken, userSecret, conkey, consecret).ToString(); 
    Response.Redirect("twittercb.aspx?oauth_verifier=none"); 
} 
public class TwitPic 
{ 
    private const string TWITPIC_UPLADO_API_URL = "http://api.twitpic.com/2/upload"; 
    private const string TWITPIC_UPLOAD_AND_POST_API_URL = "http://api.twitpic.com/1/uploadAndPost"; 
    /// 
    /// Uploads the photo and sends a new Tweet 
    /// 
    /// <param name="binaryImageData">The binary image data. 
    /// <param name="tweetMessage">The tweet message. 
    /// <param name="filename">The filename. 
    /// Return true, if the operation was succeded. 
    public string UploadPhoto(byte[] binaryImageData, string ContentType, string tweetMessage, string filename, string tpkey, string usrtoken, string usrsecret, string contoken, string consecret) 
    {    
     string boundary = Guid.NewGuid().ToString(); 
     string requestUrl = String.IsNullOrEmpty(tweetMessage) ? TWITPIC_UPLADO_API_URL : TWITPIC_UPLOAD_AND_POST_API_URL; 
     HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUrl); 
     string encoding = "iso-8859-1"; 

     request.PreAuthenticate = true; 
     request.AllowWriteStreamBuffering = true; 
     request.ContentType = string.Format("multipart/form-data; boundary={0}", boundary); 
     request.Method = "POST"; 

     string header = string.Format("--{0}", boundary); 
     string footer = string.Format("--{0}--", boundary); 

     StringBuilder contents = new StringBuilder(); 
     contents.AppendLine(header); 

     string fileContentType = ContentType; 
     string fileHeader = String.Format("Content-Disposition: file; name=\"{0}\"; filename=\"{1}\"", "media", filename); 
     string fileData = Encoding.GetEncoding(encoding).GetString(binaryImageData); 

     contents.AppendLine(fileHeader); 
     contents.AppendLine(String.Format("Content-Type: {0}", fileContentType)); 
     contents.AppendLine(); 
     contents.AppendLine(fileData); 

     contents.AppendLine(header); 
     contents.AppendLine(String.Format("Content-Disposition: form-data; name=\"{0}\"", "key")); 
     contents.AppendLine(); 
     contents.AppendLine(tpkey); 

     contents.AppendLine(header); 
     contents.AppendLine(String.Format("Content-Disposition: form-data; name=\"{0}\"", "consumer_token")); 
     contents.AppendLine(); 
     contents.AppendLine(contoken); 

     contents.AppendLine(header); 
     contents.AppendLine(String.Format("Content-Disposition: form-data; name=\"{0}\"", "consumer_secret")); 
     contents.AppendLine(); 
     contents.AppendLine(consecret); 

     contents.AppendLine(header); 
     contents.AppendLine(String.Format("Content-Disposition: form-data; name=\"{0}\"", "oauth_token")); 
     contents.AppendLine(); 
     contents.AppendLine(usrtoken); 

     contents.AppendLine(header); 
     contents.AppendLine(String.Format("Content-Disposition: form-data; name=\"{0}\"", "oauth_secret")); 
     contents.AppendLine(); 
     contents.AppendLine(usrsecret); 

     if (!String.IsNullOrEmpty(tweetMessage)) 
     { 
      contents.AppendLine(header); 
      contents.AppendLine(String.Format("Content-Disposition: form-data; name=\"{0}\"", "message")); 
      contents.AppendLine(); 
      contents.AppendLine(tweetMessage); 
     } 

     contents.AppendLine(footer);    
     byte[] bytes = Encoding.GetEncoding(encoding).GetBytes(contents.ToString());    
     request.ContentLength = bytes.Length; 

     string mediaurl = ""; 
     try 
     { 
      using (Stream requestStream = request.GetRequestStream()) // this is where the bug is due to not being able to seek. 
      {   
       requestStream.Write(bytes, 0, bytes.Length); // No problem the image is posted and tweet is posted 
       requestStream.Close();      
       using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) // here I can't get the response 
       { 
        using (StreamReader reader = new StreamReader(response.GetResponseStream())) 
        { 
         string result = reader.ReadToEnd(); 

         XDocument doc = XDocument.Parse(result); // this shows no root elements and fails here 

         XElement rsp = doc.Element("rsp"); 
         string status = rsp.Attribute(XName.Get("status")) != null ? rsp.Attribute(XName.Get("status")).Value : rsp.Attribute(XName.Get("stat")).Value; 
         mediaurl = rsp.Element("mediaurl").Value; 
         return mediaurl;        
        } 
       } 

      } 
     } 
     catch (Exception ex) 
     { 
      ex.ToString(); 
     } 
     return mediaurl; 
    } 

}