martes, 18 de marzo de 2014

REST, HTTP request mediaType

Como sabéis últimamente el término REST se está extendiendo bastante porque es una técnica más ligera en aspectos de comunicación que por ejemplo SOAP y más sencillo de implementar lo cual facilita las cosas para trabajar contra un back-end remoto desde dispositivos móviles con lenguajes como podrían ser javascript.

A priori no sólo vale con exponer un servicio por HTTP y consumirlo para decir que estamos haciendo REST ya que es toda una filosofía y contra más sigues las pautas más RESTFul es tu API y más molas. En resumen REST se basa en HTTP para comunicar 2 extremos así que tenemos varios aspectos que pueden variar y hay recomendaciones o buenas prácticas pero en definitiva todo queda bastante abierto a la persona/s que está implementando la API y te puedes encontrar de todo. Por ejemplo, centrándonos en como ENVIAR datos en una petición/request tenemos varios modos de codificar esta información. El valor típico y por defecto para MediaType es form-url/encoded pero podríamos cambiar este comportamiento en cliente al realizar una petición si la situación lo requiere.

Form-url/encoded no envía nada en el payload (cuerpo de la petición HTTP) se pasa todo codificado en la URL como una colección de clave-valor. Esto puede darte problemas cuando quieres enviar mucha información ya sea en un objeto complejo con sub-objetos y/o listas o directamente un binario pesado.

Hace un tiempo me tocó hacer un cliente a toda pastilla donde no tenía control sobre la parte servidor y estaba obligado a enviar archivos PDF a servidor (verbo GET) codificados en la URL. Todo esto era un requisito para que llegase el archivo a servidor ya que imagino que leía directamente de la URL e ignoraba headers. Poderse hacer se puede pero tal vez si hubiese tenido control del server hubiese trasteado para aceptar codificación MultiData en servidor y desde cliente enviaría la info en el payload lo cual en este caso considero que sería lo recomendable. Además si enviamos mucha info en la URL en .Net por defecto tendremos problemas con el serializador que usa System.Net.Http aquí se explica cómo saltarse la limitación.

Otra pega que veo si quieres enviar de golpe un objeto complejo que a su vez contiene alguna sublista o subobjeto con más atributos. Con json podríamos representar esta jerarquía pero con listas de clave-valor como sería querystring tendríamos que montarnos nosotros algún modo de hacerlo. Aquí la propuesta que pasaba en su día (serialiceListAsParamArray) un poco de aquella manera. Ahora estoy trabajando contra una web api que estamos desarrollando nosotros y no tenemos ese problema ya que el servidor acepta peticiones de todo tipo siempre que el mediaType esté correctamente informado en el header de la petición HTTP.

En su día me creé un cliente genérico que poco a poco voy puliendo, veamos como quedaría el método POST que ahora también contempla que desde cliente se pueda pasar la información en formato json.
private HttpResponseMessage Post(T pValue, bool serialiceListAsParamArray, bool ignoreURLEncondingSizeLimit, bool returnResponseMessage, RequestMediaTypeHeader pMediaType)
{
  try
  {
    HttpContent content = null;

    string pURLEncodedContent = "";
 
    switch (pMediaType)
    {
      #region FormEncodedURL
        case RequestMediaTypeHeader.FormEncodedUrl:
          List> oListTest = new DgestJavaScriptSerializer().GetKeyValuePair(pValue, serialiceListAsParamArray);
          if (!ignoreURLEncondingSizeLimit)
            content = new FormUrlEncodedContent(oListTest);
          else
          {
            foreach (KeyValuePair item in oListTest)
               pURLEncodedContent += string.Format("{0}={1}&", System.Web.HttpUtility.UrlEncode(item.Key), System.Web.HttpUtility.UrlEncode(item.Value));

            pURLEncodedContent = pURLEncodedContent.Substring(0, pURLEncodedContent.Length - 1);

            content = new ByteArrayContent(this.ConvertStringToByteArray(pURLEncodedContent));
            content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
          }
          break;
       #endregion

       #region JSON
         case RequestMediaTypeHeader.Json:
           string json = new DgestJavaScriptSerializer().Serialize(pValue);
           content = new ByteArrayContent(this.ConvertStringToByteArray(json));
           content.Headers.ContentType = new MediaTypeHeaderValue(this.GetMediaTypeHeader(pMediaType));
           break;
       #endregion
     }

     HttpResponseMessage response = _Client.PostAsync(_ServiceName, content).Result;
     if (!response.IsSuccessStatusCode)
     {
       throw new Exception(string.Format("{0}: No se ha podido conectar al servicio.", response.StatusCode), new Exception(response.ReasonPhrase)); ;
     }
     return response;
   }
   catch (Exception ex)
   {
     throw ex;
   }
}
Veamos una llamada desde C#:
public ActionResult CalcularProvision(Simulador simulador)
{
  //llamada a Servicio
  simulador = new RESTClient(new Uri(_urlWebApi), "Simulador/Calcular").Post(simulador, RESTClient.RequestMediaTypeHeader.Json);

  return Json(simulador.Provisiones);
}
Hasta aquí el artículo de hoy, como siempre comentaros que podéis seguir areaTIC en las redes sociales y todos vuestros comentarios, críticas y aportaciones son bienvenidas. Que vaya bien la semana!

No hay comentarios:

Publicar un comentario