miércoles, 30 de noviembre de 2011

De vuelta a lo básico, codificar html, javascript y url.

Aunque puede resultar algo obvio a muchos, hoy quiero hablar sobre algo que de vez en cuando me ha dado más de un problema. Estoy hablando de estos maravillosos códigos de producto (o de cliente o de lo que sea) que puede incluir caracteres no habituales como la comilla simple, comilla doble, etc.

Imaginemos que tenemos un producto con un identificador que incluye tanto una comilla simple como una comilla doble, por ejemplo, a partir de ahora nuestro código maldito será “sergio’s” (incluyendo las 2 comillas dobles y la comilla simple).

Aunque lo iremos viendo poco a poco, te adelanto que en este post intentaré resolver como trabajar con este código en HTML, Javascript y como QueryString en una Url.

Si no nos metemos en ningún jardín y utilizamos ASP.NET para renderizar nuestro código HTML, lo cierto es que no tendremos ningún problema. Veamos un ejemplo sencillo donde se vuelca nuestro  código en una caja de texto.

El código de servidor ASP.NET es el siguiente:

txtIdProducto.Text = """sergio's"""


Y el HTML generado es:

<input name="txtIdProducto" type="text" value="&quot;sergio&#39;s&quot;" id="txtIdProducto" />

Si nos centramos en el atributo value, podemos ver como ciertos caracteres han sido sustituidos por nombres de entidad HTML:

·         La comilla doble pasó a ser &quot;

·         La comilla simple pasó a ser &#39;

Esto ha sucedido porque cuando se ha renderizado nuestra caja de texto se ha llamado automáticamente al método Server.HtmlEncode para su atributo value.

Es decir, Server.HtmlEncode("""sergio's""") devuelve &quot;sergio&#39;s&quot;

Y por el contrario, Server.HtmlDecode("&quot;sergio&#39;s&quot;") devuelve "sergio's"

La referencia de las entidades HTML (ya sea con nombre o numérico) las puedes encontrar aquí, aunque una simple búsqueda en google te arrojará decenas de resultados.

Para que te hagas una idea y veas que cualquier carácter puede ser representado con una entidad HTML, podríamos escribir lo siguiente (que es totalmente ilegible pero es igualmente válido):

<input type="text" value="&quot;&#115;&#101;&#114;&#103;&#105;&#111;&#39;&#115;&quot;" />

En este ejemplo, además de las comillas dobles y la comilla simple, hemos sustituido el resto de caracteres por su entidad numérico HTML:

·         . &quot;

·         s. &#115;

·         e. &#101;

·         r. &#114;

·         g. &#103;

·         i. &#105;

·         o. &#111;

·         . &#39;

·         s. &#115;

·         . &quot;

Lógicamente, asumo que no te vas a volver loco y a partir de ahora te vas a poner a escribir todo tu código HTML a través de entidades, pero en cualquier caso, podrías.

La conclusión cuando estamos trabajando con código HTML, es que cualquier valor de cualquier atributo de cualquier elemento (¿demasiado cualquier quizás?) tiene que ir acompañado de Server.HtmlEncode. Es decir, imagina que estás construyendo una cadena en servidor con código HTML:

' Mal

' Devuelve <input type='text' value='"sergio's"' />

Dim html As String = String.Format("<input type='text' value='{0}' />", """sergio's""")

 

' Bien

' Devuelve <input type='text' value='&quot;sergio&#39;s&quot;' />

html = String.Format("<input type='text' value='{0}' />", Server.HtmlEncode("""sergio's"""))

 

Aunque sobra decirlo, si recuperamos el valor de nuestro input desde javascript, siempre obtendremos el valor original:

alert(document.getElementById("txtIdProducto").value);

 

clip_image001[4]

Si nos centramos ahora en Javascript, es necesario complicar un poco más nuestro código de producto para que los ejemplos sean más completos. De esta forma, ahora nuestro código de producto pasará a ser “ser\gio’s” (hemos incluido una barra inversa).

Ahora imaginemos que desde ASP.NET queremos generar un saludo con nuestro código de producto al cargar la página. Un posible código sería el siguiente:

Dim idProducto As String = """ser\gio's"""

Dim js As String = String.Format("alert('{0}');", idProducto)

ClientScript.RegisterClientScriptBlock(Me.GetType(), "Page_Load", js)

 

Y la salida que obtenemos es… que no hay alert! Pero bueno, ¿Dónde está mi alert?

Si vemos el código HTML de la página, tiene el siguiente código:

<script type="text/javascript">

//<![CDATA[

alert('"ser\gio's"');//]]>

</script>

Está claro que el mensaje de nuestro alert no está bien construido, así que toca lidiar con ello.

Para esta situación, yo suele escribir mi propia función donde reemplazo ciertos caracteres de la cadena entrante por sus caracteres de escape Javascript:

    Public Shared Function JavascriptEncode(ByVal s As String) As String

        s = s.Replace("\", "\\")

        s = s.Replace("'", "\'")

        s = s.Replace("""", "\""")

        Return s

    End Function

 

·         \ se reemplaza por \\

·         ‘ se reemplaza por \’

·         “ se reemplaza por \”

Como es de esperar, esta función seguro que no contempla ni la mitad de los casos que te pueden ocurrir, pero al menos es un principio. De hecho, le da igual si el delimitador de cadena es la comilla simple o la comilla doble (yo personalmente utilizo indistintamente ambos delimitadores en mi código Javascript cuando trabajo con cadenas).

Si ahora llamamos a la función desde el código de servidor ASP.NET:

Dim idProducto As String = """ser\gio's"""

Dim js As String = String.Format("alert('{0}');", JavascriptEncode(idProducto))

ClientScript.RegisterClientScriptBlock(Me.GetType(), "Page_Load", js)


El HTML resultante ya sí es correcto:

<script type="text/javascript">

//<![CDATA[

alert('\"ser\\gio\'s\"');//]]>

</script>

clip_image002[4]

Por último, hablaremos sobre este tipo de situaciones cuando trabajamos con nuestro código de producto en una Url como parte de la QueryString.

De nuevo, vamos a complicar nuestro código de producto (esta es la última vez, lo juro). Ahora será “&ser\gio’s” (le hemos incluido un ampersand)

Aquí tenemos que contemplar dos escenarios (al menos son con los que yo trabajo habitualmente):

·         Generar la url desde Javascript

·         Generar la url desde ASP.NET, es decir, desde el servidor

Si creamos la url desde Javascript:

var id = document.getElementById("txtIdProducto").value;

var url = "VerProducto.aspx?IdProducto=" + id;

alert(url);

 

clip_image003[4]

 

Está claro que hay no funcionará porque, para empezar, el carácter & es el delimitador de campos en una QueryString y entonces ¿Qué tengo en la url?

clip_image004[4]

Pues resulta que ASP.NET dice tener 2 campos. El segundo campo no tiene nombre y el primer campo no tiene valor… vamos, que la url está muy mal construida.

Para solucionarlo, basta con utilizar la función nativa de Javascript, encodeURIComponent.

var id = document.getElementById("txtIdProducto").value;

var url = "VerProducto.aspx?IdProducto=" + encodeURIComponent(id);

 

clip_image005[4]

clip_image006[4]

Ahora todo funciona correctamente, porque se han sustituido en la url los caracteres reservados por sus equivalentes caracteres de escape.

·         “ por %22

·         & por %26

Ya por último, si nos centramos en la generación de la url desde código de servidor ASP.NET, habrá que utilizar la función Server.UrlEncode.

Dim url As String = "VerProducto.aspx?IdProducto={0}"

url = String.Format(url, Server.UrlEncode("""&sergio's"""))


Que devolverá la cadena:

VerProducto.aspx?IdProducto=%22%26sergio%27s%22

Lo cierto es que este post, da para mucho que escribir, pero al menos espero haber sentado ciertas bases para que los códigos indeseables de productos no te quiten el sueño.

Un saludo!

3 comentarios:

  1. Si es que estás hecho un crack. Leído y entendido. Seguro que me será muy útil para mi próxima (y espero que no muy lejana) inmersión en la web.

    A pasarlo bien!

    ResponderEliminar
  2. Ah! excelente conceptos basico que mucho ignoran por la pura y gran ignorancia!, buena entrada!

    ResponderEliminar