Expresiones regulares y ‘wildcards’ en servicios REST con JAX-RS

En el proyecto en el que estoy trabajando nos surgió hace poco la necesidad de que la ruta de un ‘endpoint’ de un servicio REST pudiera recibir, como variable en el path, una cadena que fuera una ruta de directorios, por ejemplo:

/micarpeta/otracarpeta/nombrefichero

Si eso lo intentamos hacer definiendo un ‘endpoint’ como este:

/api/files/{filepath}

… no funciona. La url completa sería /api/files/micarpeta/otracarpeta/nombrefichero, y no va a encontrarla, dado que no tenemos ningún endpoint definido así realmente.

Aunque inicialmente parece que no es posible conseguir un endpoint que se trague eso, si que se puede hacer :). Vamos a verlo.

A la hora de definir los ‘endpoints’ o urls de nuestros Resources (o Controllers en Spring), tenemos los básicos: endpoints fijos o los típicos con ‘path variable’:

Endpoint fijo:

/api/customers/orders

Con path variable:

/api/customers/{id}
/api/customers/{id}/orders/{idOrder}

Pero no son las únicas opciones, vamos a ver alguna más, En un Resource con url base ‘/api/foo’:

  • Podemos tener un ‘endpoint’ que tenga un parámetro como path variable que obligatoriamente sea un entero:
        
        @GET
        @Path("onlyinteger/{id : \\d+}")
        @Produces(MediaType.TEXT_PLAIN)
        public String getFooWithIntegerId(@PathParam("id") int id) {
            return Integer.toString(id);
        }
    

    De esta manera, si accedemos a la url ‘/api/foo/onlyinteger/ID_123’, no funcionará. Tiene que ser un número entero si o si: ‘/api/foo/onlyinteger/123’.

  • Podemos también concatenar varios parámetros, sin usar la barra ‘/’, por ejemplo, con un guión:
        @GET
        @Path("twoparams/{firstname}-{surname}")
        @Produces(MediaType.TEXT_PLAIN)
        public String getFooWithNameAndSurname(@PathParam("firstname") String firstname,
                                               @PathParam("surname") String surname) {
            return firstname + " " + surname;
        }
    

    Ejemplo de url: ‘/api/foo/twoparams/bilbo-baggins’.

  • Y si queremos que el parámetro sea, como lo que comentaba al comenzar el post, una cadena en la que pueda venir una ruta de ficheros:
        @GET
        @Path("wildcard/{subpath : .+}")
        @Produces(MediaType.TEXT_PLAIN)
        public String getFooWithWildcard(@PathParam("subpath") String subpath) {
            return subpath;
        }
    

    Si accediéramos, por ejemplo, a esta url: ‘/api/foo/wildcard/midir/anotherdir/ficName.txt’, funcionaria.

Si queréis juguetear con un ejemplo, en mi github he creado un proyecto (jaxrs-wildcards-example), arrancable con jetty desde maven (‘mvn jetty:run’), con el que podéis probar el código del post.