4 de mayo de 2020

Añadir link a /resources/search

Para conseguir en nuestra API REST el nivel 3, HATEOAS, hypermedia o RESTful hace falta que nuestros enlaces se puedan autodescrubrir. Esto no lo digo yo. El padre de REST explica que hypermedia es una condición mínima para llamar a un servicio RESTful. Una traducción (por google) de lo que ha dicho Roy Fielding al respecto:

Me siento frustrado por la cantidad de personas que llaman a cualquier interfaz basada en HTTP una API REST. El ejemplo de hoy es la API REST de SocialSite. Eso es RPC. Grita RPC. Hay tanto acoplamiento en la pantalla que se le debe dar una calificación X.

¿Qué se debe hacer para dejar claro el estilo arquitectónico REST sobre la noción de que el hipertexto es una restricción? En otras palabras, si el motor del estado de la aplicación (y, por lo tanto, la API) no está siendo impulsado por hipertexto, entonces no puede ser RESTful y no puede ser una API REST. Período. ¿Hay algún manual roto en algún lugar que deba repararse?

Con esto claro, en nuestra API no hay ningún enlace que dirija al método que expusimos en la entrada anterior: no se puede autodescubrir, o conoces la URL o navegando no hay un camino hasta ella. Vamos a añadir un link a nuestro nuevo endpoint para que no se frustre Roy.

Para ello vamos a usar RepositorySearchesResource. Éste bean permite añadir links a todos los que se crean automáticamente por Spring Data Rest. Hay varias formas de hacerlo, aunque las que he encontrado por Internet normalmente usan la versión antigua de HATEOAS (ResourceProcessor como veíamos también en la documentación de la entrada anterior). Voy a hacerlo migrado ya a la versión actual y de la forma que me parece mejor por ser más reutilizable.

Me voy a crear un bean del tipo RepresentationModelProcessor<RepositorySearchesResource> en mi clase de configuración. Es decir, voy a indicar el modo en el que se debe procesar el recurso utilizado para las búsquedas. El código quedaría así:
@Bean
RepresentationModelProcessor<RepositorySearchesResource> searchLinks(RepositoryRestConfiguration config) {
    return new RepresentationModelProcessor<RepositorySearchesResource>() {

        @Override
        public RepositorySearchesResource process(RepositorySearchesResource searchResource) {
            if (searchResource.getDomainType().equals(PartidoConId.class)) {
                try {
                    String nombreMetodo = "getPartidosConParticipanteComo";
                    Method method = PartidoController.class.getMethod(nombreMetodo, String.class,
                            PersistentEntityResourceAssembler.class);
                    URI uri = org.springframework.hateoas.server.mvc.WebMvcLinkBuilder
                            .linkTo(method, null, null).toUri();
                    String url = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(),
                            config.getBasePath() + uri.getPath(), uri.getQuery(), uri.getFragment()).toString();
                    searchResource.add(new Link(url + "{?txt}", nombreMetodo));
                } catch (NoSuchMethodException | URISyntaxException e) {
                    e.printStackTrace();
                }
            }

            return searchResource;
        }

    };
}
Lo importante de todo este código es que el objeto searchResource se usa para todos los /search. Eso quiere decir que si añadimos un enlace aparecerá en todos ellos. Si queremos añadir el enlace en sólo uno (en nuestro caso para partidos) debemos filtrar para que coincida con el tipo correcto: PartidoConId. Podría ponerse el enlace a mano, pero entonces cualquier cambio posterior nos obligaría a recordar que hay que cambiarlo aquí también.

Lo mejor es recuperar el enlace que apunta a un método. Voy a usar linkTo y le pasaré el método al que quiero apuntar y los parámetros con null (estos son para incluir valores en las variables del path y completar la URL, pero nuestro método sólo tiene query params).

NOTA: Para usar linkTo se puede usar la importación estática para evitar poner su paquete, pero lo he dejado en el código para que quede claro de dónde viene el método.

Lamentablemente, el link no incluye el basePath y hay que añadírselo (como dicen ellos: "por ahora") por eso se construye de nuevo la URI incluyéndolo. Para recuperar ese valor lo mejor es usar RepositoryRestConfiguration que puede ser inyectada en el método.

Ya con la información completa podemos añadir el enlace en searchResource incluyendo los query params.

Si ejecutamos veremos que nuestro enlace aparece al final de los que se han añadido automáticamente.

No obstante, supongo que a cualquiera le dolerá ver esos "magic number" y pensará que debe haber una forma más automática de hacerlo. Personalmente no la he encontrado y tiene sentido (no se puede saber cómo se van a añadir los endpoints porque hay varias formas). Si alguno encuentra algo oficial agradecería que lo compartiera en los comentarios para completar la entrada.

Además en la siguiente entrada voy a añadir otro endpoint usando una variable en el path y también habrá que añadir el link.

Para arreglar esta falta de automatización, después de la siguiente entrada voy a compartir una clase de configuración base que puede utilizarse de manera generalizada en los proyectos igual que hicimos con el jpa-config.xml y explicaré la forma en que debe usarse. Así veremos que sólo con eso detecta y añade este endpoint y el que veamos a continuación que incluye path variable (y es otro parámetro que debemos controlar).

Puedes obtener el código hasta aquí en su repositorio.

No hay comentarios:

Publicar un comentario

Compárteme

Entradas populares