en HTML5

Input type url REGEX pattern

En este artículo vamos a ver cómo mejorar la validación HTML5 de un input type URL mediante el atributo pattern y expresiones regulares (REGEX).

Validación HTML5 del input type URL

HTML5 hace una validación muy simple de una URL, tan simple que es suficiente con un carácter alfabético y dos puntos «:». Aunque si escribes http:// o https:// te exigirá aunque sea un único carácter a continuación, eso sí, esta vez podrá ser alfanumérico. Tampoco te exige un punto que separe los diferentes niveles del dominio y acepta espacios entre otros muchos caracteres reservados o no seguros.

Pero creo que podríamos mejorar la validación si, además, comprobamos que contenga otros elementos propios de una dirección URL y restringimos otros que no se permiten. ¿Por qué deberíais? os preguntaréis. Pues lo cierto es que las razones pueden ser múltiples en base a los requerimientos del proyecto, pero la principal es obtener datos de calidad y evitar futuras incidencias en el tratamiento de los datos. Pongamos por ejemplo que queremos pasar la URL por GET dentro de nuestro sistema, disponer de un formato estandarizado sin caracteres inseguros y con los caracteres reservados controlados, nos ahorrará mucho trabajo al formatear la URL y evitaremos posibles incidencias.

Estructura de una URL

Una URL puede o debe contener los siguientes elementos:

  1. http:// o https://
  2. Un dominio, que a su vez puede contener un TLD, sea genérico o regional, y múltiples subdominios.
  3. Además puede incluir directorios (/) y anclas (#)
  4. Y finalmente parámetros que iniciarán con un interrogante (?) e irán separados por ampersand (&), pudiendo incluir también códigos de % para por ejemplo, pasar otra url por método GET

URL REGEX para input

Basándonos en esa estructura podemos construir una expresión regular algo más restrictiva. Pero antes tenemos que comentar los distintos tipos de caracteres que debemos validar. Vamos a repasarlos.

Clasificación Caracteres incluidos Codificar
Caracteres seguros Alfanuméricos[0-9a-zA-Z] y caracteres no reservados. También los reservados cuando se usan para la finalidad para la que están reservados (p.ej. los usados para pasar parámetros ?, &, =) NO
Caracteres no reservados - . _ ~ (excluyendo espacios) NO
Caracteres reservados : / ? # [ ] @ ! $ & ' ( ) * + , ; = (excluyendo espacios) SI
Caracteres no seguros Incluye espacios y " < > % { } | \ ^ ` SI
Caracteres de control ASCII Incluye caracteres ISO-8859-1 (ISO-Latin) desde 00-1F hex (0-31 decimal) a 7F (127 decimal) SI
Caracteres No-ASCII Incluye todos la «primera mitad» de los ISO-Latin, 80-FF hex (128-255 decimal) SI
Resto de caracteres Cualquier otro carácter no mencionado arriba debe ser codificado mediante códigos de porcentaje. SI

Ahora que conocemos los tipos de caracteres pasemos a la estructura y a cómo los vamos a tratar en cada una de sus partes.

Validación del protocolo http(s)://

La expresión regular para el protocolo http es bastante sencilla:

https?:\/\/
  • Aceptamos que el protocolo http termine en «s» sin ser obligatorio, usando un condicional s?
  • Incluimos los «:» que son un carácter reservado y, mediante una barra invertida, escapamos las barras oblicuas (slash), que, aunque también es un carácter reservado, en REGEX se usan como delimitadores de la expresión. Por lo que se traduce en  \/\/. Aunque para HTML pattern será suficiente con  //, mejor escaparlas por si hemos de validar con javascript on summit *.

*Actualización 09/07/2023: parece que en algún momento de 2023, los navegadores han introducido cambios en la forma de interpretar las REGEX y, aunque cuando incluyamos la barra oblicua (slash) en un set de caracteres deberemos seguir escapándolas, parece que para el caso anterior ya no es necesario. Por otro lado tendremos que escapar otros caracteres que también son usados en REGEX, como los paréntesis \( y \), o la barra vertical \|, que antes no era necesario.

Validación del dominio y su TLD

Aquí empieza lo divertido. Los dominios siguen unas reglas bastante estrictas de las que ya hablamos en el artículo sobre validación de e-mails con REGEX, que resumiendo serían: al menos un carácter Latin (a-zA-Z), dígitos(0-9) y guiones siempre que no se encuentren al inicio o al final, o dentro del TLD. Así como puntos (.) para agrupar cada uno de sus niveles. Comencemos con el nombre de dominio:

([a-zA-Z0-9]([^ @&%$\\\/\(\)=?¿!.,:;]|\d)*[a-zA-Z0-9][\.])+

  • Incluimos ambos, Latin y dígitos [a-zA-Z0-9], al inicio y al final para controlar que no sean guiones. Siendo los únicos obligatorios, o lo que es lo mismo, permitiendo dominios de un mínimo de 2 caracteres.
  • Entre esos dos bloques permitimos cualquier carácter que no incluya algunos signos no permitidos: [^ @&%$\\\/\(\)=?¿!.,:;]; o dígitos |\d, que, en conjunto podrán sumar tantos como queramos (cero o más): (…)*. 
  • Al final de los tres bloques añadiremos el punto que nos servirá para delimitar las etiquetas del dominio (subdominios, dominio principal y TLD): [\.]
  • Y finalmente lo agrupamos para que el conjunto se pueda repetir tantas veces como queramos: (…)+ ; aunque hay una restricción 255 caracteres por subdominio y de entre 2 y 63 caracteres para cada nivel de los subdominios, que, a menos que vayamos a crear un registro de dominio, podemos dejar de lado.

Nos faltaría la parte del TLD:

[a-zA-Z0-9]{2,4}([\.][a-zA-Z]{2})?

  • La primera parte [a-zA-Z0-9]{2,4} contempla TLD de dos dígitos (como los regionales .es, .uk, etc.) hasta los de 3 y 4 (.com, .name, etc.)
  • Mientras que la segunda ([\.][a-zA-Z]{2})? está reservada exclusivamente para los regionales (dos dígitos {2}) cuando se añaden al principal, por lo que van precedidos de un punto [\.] y cerrados con un condicional que lo hace opcional: (…)?
  • Aunque el TLD no existe en los dominios de redes locales, caso para el que habría que añadir un condicional adicional que englobase ambas partes, nosotros vamos a trabajar con el concepto más tradicional.

Así, si lo uniésemos todo, obtendríamos la siguiente REGEX de URL:

https?://([a-zA-Z0-9]([^ @&%$\\\/\(\)=?¿!.,:;]|\d)+[a-zA-Z0-9][\.])+[a-zA-Z0-9]{2,4}([\.][a-zA-Z]{2})?

Pero las URL también pueden tener directorios, anclas y parámetros GET. Podríamos simplificarlos todos con un (\/(?<=\/).+)?, es decir, que a continuación del dominio podría llevar uno o más de cualquier carácter (definido por el punto «.«) siempre que fuese precedido de un slash (?<=\/) o sólo el slash que identifica el root. Sin embargo, para evitar que los usuarios puedan copiar y pegar URL con errores o que contengan deliberadamente caracteres inseguros o reservados, como hacen p.ej. WP o Google en sus parámetros, es recomendable ser más rigurosos.

Validación de directorios, anclas y parámetros

Para esta parte de la validación vamos a trabajar con un método «matriosca» donde los conjuntos o cadenas de caracteres a validar, estarán embebidos unos dentro de otros; y éste lo combinaremos con comprobaciones del tipo «Positive Lookbehind» (?<=foo) bar, es decir, comprobar que existe un elemento previo (foo) para validar la cadena (bar).

Para simplificar la estructura de nuestra REGEX vamos a empezar con un pequeño esquema:

[Conjunto general [[Conjunto de directorios] [Ancla] [Conjunto de parámetros [Parámetro 1] [Otros parámetros] ] ] ]

Cada uno de estos niveles de la matriosca serán un condicional que podrá estar o no estar, es decir podrá haber 1 o ninguno, lo que en REGEX se traduce como (…)? o, en algunos casos cero o más (…)*

(Conjunto general ((Conjunto de directorios)? (Ancla)? ( (Conjunto de parámetros (Parámetro 1) (Otros parámetros)* ) )? )?)

Previo al conjunto general encontraremos sólo el slash que nos sirve para delimitar el dominio y el inicio del resto de la URL (\/?). Una vez dentro, encontramos los otros tres conjuntos: directorios, ancla y parámetros. Cada uno de ellos tendrá su propio (?<=foo) bar.

Para todos los subconjuntos deberemos delimitar la cadena a dígitos y caracteres alfabéticos, restringiendo los reservados y no seguros [^ @&$#\\\/\(\)+=?¿!,:;’&quot;^´*%\|], permitiendo sólo su uso en la forma para la que han sido reservados (p.ej. la barra de directorio [\/]) o mediante codificación de %.

Importante: como estamos trabajando con HTML5 que delimita las cadenas mediante comillas, generalmente dobles, aunque REGEX no necesita escaparlas, para incluirlas dentro del parámetro pattern deberemos usar HTML entities, que en este caso sería &quot;

Validación de directorios en una URL

La condición para los directorios es que les preceda un slash (?<=\/) en caso de existir. También podrá terminar, o no, con un slash.

[Http y dominio] \/?( (?<=\/) ([^ @&$#\\\/\(\)+=?¿!,:;’&quot;^´*%\|]|\d)+[\/]?)* (Ancla)? (Conjunto de parámetros ( (Parámetro 1) (Otros parámetros)* ) )? )?

Validación de anclas en una URL

La condición para los directorios es que les preceda un hashtag (?<=#) en caso de existir.

[Http y dominio] \/?( (?<=\/) ([^ @&$#\\\/\(\)+=?¿!,:;’&quot;^´*%\|]|\d)+[\/]?)* (#(?<=#)[^ @&$#\\\/\(\)+=?¿!,:;’&quot;^´*%\|]*)? (Conjunto de parámetros ( (Parámetro 1) (Otros parámetros)* ) )? )?

Validación de parámetros GET

La condición para los parámetros es que les preceda una interrogación (?<=?) (todos los parámetros) en caso de existir, o, en caso de haber más de uno, un ampersand (?<=&) precediendo a los consecutivos parámetros. Y cada uno de ellos, además, deberá contener un igual [=] y un valor.

En esta parte de la URL no excluiremos el % ya que es necesario su uso para pasar el resto de símbolos mediante códigos de porcentaje.

[Http y dominio] \/?( (?<=\/) ([^ @&$#\\\/\(\)+=?¿!,:;’&quot;^´*%\|]|\d)+[\/]?)* (#(?<=#)[^ @&$#\\\/\(\)+=?¿!,:;’&quot;^´*%\|]*)? (\?(?<=\?)([^ @&$#\\\/\(\)+=?¿!,:;’&quot;^´*\|]+[=][^ @&$#\\\/\(\)+=?¿!,:;’&quot;^´*\|]+(&(?<=&)[^ @&$#\\\/\(\)+=?¿!,:;’&quot;^´*\|]+[=][^ @&$#\\\/\(\)+=?¿!,:;’&quot;^´*\|]+)* ))? )?

Y así completamos la validación de nuestro input type URL, mejorándolo gracias al atributo pattern y las expresiones regulares. Podéis comprobar el resultado en este codepen:

See the Pen
email pattern
by Daniel Abril (@elcssar)
on CodePen.

Espero vuestros comentarios.

 

Escribe un comentario

Comentario

  1. Primero agradecerte la ayuda. Soy muy novato en este tema y estoy totalmente perdido.
    Tengo un ejemplo de URL que no valida y no logro saber el motivo:
    https://www.uam.es/Ciencias/Ingenieria_Biom%C3%A9dica/1446800779713.htm?language=es&nodepath=Ingenier?a%20Biom?dica

    • Así a primera vista veo algún caracter especial como tildes que se han convertido en interrogantes. Si estás usando la REGEX de elcssar, no permitirá más de un signo de interrogación, pues está reservado para pasar parámetros tal como se explica en el último apartado del artículo. Si los conviertes en símbolos de %, debería validar. Entiendo que los códigos de porcentaje del directorio «Ingenieria_Biomédica» es porque lo has copiado y pegado desde la barra de direcciones. En principio se permite el uso de acentos aunque no se recomienda, por lo que no es necesario convertirlos a códigos de porcentaje, pero sí sería conveniente, si puedes, evitarlo en los directorios.
      Podría hacerse la validación un poco menos restrictiva y eliminar el % de la REGEX de exclusión de los directorios.
      En principio esta URL validaría:
      https://www.uam.es/Ciencias/Ingenieria_Biomédica/1446800779713.htm?language=es&nodepath=Ingenier%EDa%20Biom%E9dica

      Aquí te dejo también una tabla que te será útil en el futuro para estas cosas: https://www.w3schools.com/tags/ref_urlencode.ASP