En el mes de Mayo intenté mostrar cómo podíamos recuperar los tokens de autenticación, tanto para Twitter como para Yahoo, con el objetivo de hacer uso del protocolo abierto OAuth. A pesar de todo, es posible que aún tengamos dudas sobre cómo realizar nuestras peticiones y nos hagan falta algunos tips a tener en cuenta 🙂 En este post me centraré en Twitter.
En el apartado de documentación podemos ver una barra lateral derecha donde se nos muestran todas las acciones disponibles, la URL a la cual debemos realizar la petición y el HTTP Verb necesario:
Para este ejemplo, voy a mostrar cómo sería para el caso más común que es actualizar el estado.
En primer lugar, voy a crear una aplicación de consola donde vamos a llamar a un wrapper creado para interactuar con la API y que a su vez utilizará la clase implementada para el protocolo OAuth en el post anterior.
[csharp]
using System;
using TwitterWrapper;
namespace TwitterClient
{
class Program
{
static void Main()
{
const string status = "Testing my twitter client with #OAuth and ¡special characteres! ¿*?";
var result = new Twitter().UpdateStatus(status);
Console.WriteLine("The status was successfully updated!");
Console.WriteLine("ID: {0}", result.Id);
Console.WriteLine("Date: {0}", result.Created);
Console.WriteLine("Status: {0}", result.Text);
Console.ReadLine();
}
}
}
[/csharp]
Como podéis ver, he creado una clase llamada Twitter desde la cual llamo al método UpdateStatus. El objetivo del mismo es reunir los parámetros necesarios para poder construir la petición y que posteriormente sea firmada.
[csharp]
using System.IO;
using System.Net;
using System.Web;
using System.Xml.Serialization;
using OAuthTools;
using TwitterWrapper.Resources;
namespace TwitterWrapper
{
public class Twitter
{
private const string ConsumerKey = "yourConsumerKey";
private const string ConsumerSecret = "yourConsumerSecret";
private const string AccessToken = "yourAccessToken";
private const string AccessTokenSecret = "yourAccessTokenSecret";
public Status UpdateStatus(string status, WebProxy webProxy = null)
{
var query = string.Format("http://api.twitter.com/1/statuses/update.xml?status={0}", HttpUtility.UrlEncode(status));
var request = new OAuth(ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret);
var result = request.PostResource(query);
return Serialize(result);
}
public Status Serialize(string xml)
{
var xmlSerializer = new XmlSerializer(typeof(Status));
var reader = new StringReader(xml);
var status = (Status)xmlSerializer.Deserialize(reader);
return status;
}
}
}
[/csharp]
Una vez que la petición ha sido realizada, intentamos almacenar el resultado XML dentro de la clase Status gracias a XmlSerializer y los XmlAttributes en los atributos que necesitemos almacenar en nuestra aplicación.
Tip: Es muy importante hacer uso de HttpUtility.UrlEncode para recuperar los valores Unicode del status que acabamos de pasarle al método. De lo contrario algunos caracteres especiales podrían interpretarse de un modo erróneo y serán omitidos de la petición. Por ejemplo, uno de los casos que me estaba ocurriendo, es al intentar introducir un hashtag. Al no pasar esta información en formato Unicode, si intentamos transformar el string en Uri interpretará el símbolo # y todo lo que venía detrás como un ancla 😉
Si bajamos una capa más en el código, llegamos a la clase OAuth pero con un nuevo método para las peticiones POST llamado PostResource.
[csharp]
/* POST Requests */
public string PostResource(string url)
{
var uri = new Uri(url);
var parameters = GetOAuthParameters(HttpMethod.Post, url, HttpUtility.ParseQueryString(uri.Query), _accessToken, _accessTokenSecret);
url = string.Format("{0}://{1}{2}", uri.Scheme, uri.Authority, uri.AbsolutePath);
var oAuthRequest = new OAuthRequest(parameters, url, HttpMethod.Post, proxy: _proxy);
var queryParameters = HttpUtility.ParseQueryString(uri.Query);
var response = oAuthRequest.GetResponse(queryParameters);
return response;
}
[/csharp]
En esta función vamos a formatear el parámetro de entrada (url de Twitter + el nuevo estado) dentro de la clase Uri y posteriormente vamos a recuperar los parámetros para el protocolo OAuth que irán incluidos en la cabecera.
Una vez que tenemos generados estos parámetros (nonce, timestamp, la firma, etc.) podemos hacer la llamada a través del método POST utilizando la clase oAuthRequest. Crearemos una instancia de dicha clase con los parámetros recopilados hasta el momento y por último llamaremos a GetResponse para obtener el resultado.
[csharp]
using System;
using System.Text;
using System.Collections.Specialized;
using System.Net;
using System.IO;
namespace OAuthTools
{
public class OAuthRequest
{
private readonly HttpWebRequest _request;
private static byte[] _content;
public string GetResponse(NameValueCollection queryParameters = null)
{
try
{
/*POST method*/
if (queryParameters != null)
{
var sb = new StringBuilder();
for (var i = 0; i < queryParameters.AllKeys.Length; i++)
{
var key = queryParameters.AllKeys[i];
sb.AppendFormat("{0}={1}", Uri.EscapeDataString(key), Uri.EscapeDataString(queryParameters[key]));
if (i < queryParameters.Count – 1)
sb.Append("&");
}
_content = Encoding.ASCII.GetBytes(sb.ToString());
_request.ContentLength = _content.Length;
using (var stream = _request.GetRequestStream())
{
if (_request.ContentLength > 0)
stream.Write(_content, 0, _content.Length);
}
}
using (var response = _request.GetResponse())
{
var stream = response.GetResponseStream();
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
catch (WebException ex)
{
//If something is wrong, We’ll retrieve the server response.
/* HTTP 400 Bad Request
o Unsupported parameter
o Unsupported signature method
o Missing required parameter
o Duplicated OAuth Protocol Parameter
HTTP 401 Unauthorized
o Invalid Consumer Key
o Invalid / expired Token
o Invalid signature
o Invalid / used nonce
*/
if (ex.Response is HttpWebResponse)
{
using (var reader = new StreamReader(ex.Response.GetResponseStream()))
{
var result = reader.ReadToEnd();
return result;
}
}
throw;
}
}
// 2. For each parameter, the name is immediately followed by an ‘=’ character (ASCII code 61), a ‘”’ character (ASCII code 34), the parameter value (MAY be empty), and another ‘”’ character (ASCII code 34).
// 3. Parameters are separated by a comma character (ASCII code 44) and OPTIONAL linear whitespace.
// 4. The OPTIONAL realm parameter is added
/// <summary>
/// The OAuth Protocol Parameters are sent in the Authorization header the following way:
/// 1. Parameter names and values are encoded.
/// </summary>
/// <param name="parameters">oAuthParameters</param>
/// <param name="url"></param>
/// <param name="method">Http Method</param>
/// <param name="proxy"></param>
public OAuthRequest(NameValueCollection parameters, string url, string method, IWebProxy proxy = null)
{
ServicePointManager.Expect100Continue = false;
ServicePointManager.UseNagleAlgorithm = false;
var header = new StringBuilder();
header.Append("OAuth ");
for (var i = 0; i < parameters.Count; i++)
{
var key = parameters.GetKey(i);
header.Append(string.Format("{0}="{1}"", key, parameters[key]));
if (i < parameters.Count – 1)
header.Append(",");
}
_request = CreateHttpWebRequest(url, method, proxy);
_request.Headers["Authorization"] = header.ToString();
}
private static HttpWebRequest CreateHttpWebRequest(string url, string method, IWebProxy proxy = null)
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.Method = method.ToUpper();
if (proxy != null) httpWebRequest.Proxy = proxy;
if (method == HttpMethod.Post)
httpWebRequest.ContentType = "application/x-www-form-urlencoded";
return httpWebRequest;
}
}
}
[/csharp]
Con esta clase podríamos realizar tanto peticiones GET como POST, donde en las segundas se envía el cuerpo del mensaje.
Para poder ver el ejemplo completo, he actualizado el proyecto de CodePlex.
Espero que sea de utilidad 😀
¡Saludos!


¿Dónde declaras la clase Status? ¿O cual es su librería?
Hola Kran, la clase Status puedes verla en el siguiente enlace, que hace referencia a su archivo en Codeplex: http://oauthutilitydotnet.codeplex.com/SourceControl/latest#1046536
¡Saludos!
Hola,
Ahora la versión de la API de Twitter ha pasado de 1.0 a 1.1 y me ha dejado de funcionar este código que en su día usé, ¿sabrías si se puede actualizar con OAuth?
Hola Noelia,
Lamento no poder ayudarte, no he vuelto a trabajar en ese escenario Twitter/OAuth. Seguramente en la web de desarrolladores de Twitter puedas encontrar información al respecto.
Un saludo,
Hola Gisela, ya lo solucioné, voy a dar la solución por si alguien se encuentra con el mismo problema.
El cambio se realiza en la parte del código que se hace la llamada a la API de Twitter en la calse Twitter, ahora la respuesta de la API es en JSON en lugar de xml, tuve que buscar una librería para desserializar la respuesta en JSON, usé JSON .Net y la dll es Newtonsoft.Json.dll.
Por tanto el código que cambié quedaría así por si lo quieres actualizar:
using System.Xml.Serialization;
using System.Xml.Linq;
using System.IO;
using System.Net;
using Utils;
using System.Configuration;
using System;
using Newtonsoft.Json;
namespace TwitterWrapper
{
public class Twitter
{
private const string ConsumerKey = «yourConsumerKey»;
private const string ConsumerSecret = «yourConsumerSecret»;
private const string AccessToken = «yourAccessToken»;
private const string AccessTokenSecret = «yourAccessTokenSecret»;
public Status UpdateStatus(string status, WebProxy webProxy = null)
{
var query = string.Format(«http://api.twitter.com/1/statuses/update.json?status={0}», HttpUtility.UrlEncode(status));
var request = new OAuth(ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret);
var result = request.PostResource(query);
var resultXML = JsonConvert.DeserializeXNode(result, «status»).ToString();
return Serialize(resultXML);
}
public Status Serialize(string xml)
{
var xmlSerializer = new XmlSerializer(typeof(Status));
var reader = new StringReader(xml);
var status = (Status)xmlSerializer.Deserialize(reader);
return status;
}
}
}