Introduction à RESTX

1. Vue d'ensemble

Dans ce didacticiel, nous allons faire une visite guidée du framework léger Java REST RESTX.

2. Caractéristiques

Construire une API RESTful est assez facile avec le framework RESTX. Il a toutes les valeurs par défaut que nous pouvons attendre d'un framework REST, comme le service et la consommation de JSON, les paramètres de requête et de chemin, les mécanismes de routage et de filtrage, les statistiques d'utilisation et la surveillance.

RESTX est également livré avec une console Web d'administration intuitive et un programme d'installation de ligne de commande pour un démarrage facile.

Il est également sous licence Apache License 2 et maintenu par une communauté de développeurs. La configuration Java minimale requise pour RESTX est JDK 7.

3. Configuration

RESTX est livré avec une application shell / commande pratique qui est utile pour démarrer rapidement un projet Java.

Nous devons d'abord installer l'application avant de pouvoir continuer. Les instructions d'installation détaillées sont disponibles ici.

4. Installation des plugins principaux

Maintenant, il est temps d'installer les plugins principaux pour pouvoir créer une application à partir du shell lui-même.

Dans le shell RESTX, exécutons la commande suivante:

shell install

Il nous demandera ensuite de sélectionner les plugins pour l'installation. Nous devons sélectionner le nombre qui pointe vers io.restx: restx-core-shell . Le shell redémarrera automatiquement une fois l'installation terminée.

5. Bootstrap de l'application Shell

En utilisant le shell RESTX, il est très pratique de démarrer une nouvelle application. Il fournit un guide basé sur un assistant.

Nous commençons par exécuter la commande suivante sur le shell:

app new

Cette commande déclenchera l'assistant. Ensuite, nous pouvons soit choisir les options par défaut, soit les modifier selon nos besoins:

Puisque nous avons choisi de générer un pom.xml, le projet peut être facilement importé dans n'importe quel IDE Java standard.

Dans quelques cas, nous pouvons avoir besoin de modifier les paramètres IDE.

Notre prochaine étape sera de construire le projet:

mvn clean install -DskipTests

Une fois la construction réussie, nous pouvons exécuter la classe AppServer en tant qu'application Java à partir de l'EDI . Cela démarrera le serveur avec la console d'administration, en écoutant sur le port 8080.

Nous pouvons accéder à //127.0.0.1:8080/api/@/ui et voir l'interface utilisateur de base.

Les routes commençant par / @ / sont utilisées pour la console d'administration qui est un chemin réservé dans RESTX.

Pour nous connecter à la console d'administration, nous pouvons utiliser le nom d'utilisateur par défaut «admin » et le mot de passe que nous avons fourni lors de la création de l'application.

Avant de jouer avec la console, explorons le code et comprenons ce que l'assistant a généré.

6. Une ressource RESTX

Les routes sont définies dans la classe < main_package> .rest.HelloResource :

@Component @RestxResource public class HelloResource { @GET("/message") @RolesAllowed(Roles.HELLO_ROLE) public Message sayHello() { return new Message().setMessage(String.format("hello %s, it's %s", RestxSession.current().getPrincipal().get().getName(), DateTime.now().toString("HH:mm:ss"))); } }

Il est immédiatement évident que RESTX utilise les annotations J2EE par défaut pour la sécurité et les liaisons REST. Pour la plupart, il utilise ses propres annotations pour l'injection de dépendances.

RESTX prend également en charge de nombreuses valeurs par défaut raisonnables pour mapper les paramètres de méthode à la requête.

Et, en plus de ces annotations standard , @RestxResource , qui le déclare comme une ressource que RESTX reconnaît.

Le chemin de base est ajouté dans le src / main / webapp / WEB-INF / web.xml. Dans notre cas, il s'agit de / api , nous pouvons donc envoyer une requête GET à // localhost: 8080 / api / message , en supposant une authentification correcte.

La classe Message est juste un bean Java que RESTX sérialise en JSON.

Nous contrôlons l'accès utilisateur en spécifiant l' annotation RolesAllowed à l'aide du HELLO_ROLE qui a été généré par le bootstrapper.

7. La classe de module

Comme indiqué précédemment, RESTX utilise des annotations d'injection de dépendances au standard J2EE, telles que @Named , et invente les siennes si nécessaire, en s'inspirant probablement du framework Dagger pour @Module et @Provides.

Il les utilise pour créer le module principal des applications, qui définit entre autres le mot de passe administrateur:

@Module public class AppModule { @Provides public SignatureKey signatureKey() { return new SignatureKey("restx-demo -44749418370 restx-demo 801f-4116-48f2-906b" .getBytes(Charsets.UTF_8)); } @Provides @Named("restx.admin.password") public String restxAdminPassword() { return "1234"; } @Provides public ConfigSupplier appConfigSupplier(ConfigLoader configLoader) { return configLoader.fromResource("restx/demo/settings"); } // other provider methods to create components }

@Module définit une classe qui peut définir d'autres composants, similaires à @Module dans Dagger ou @Configuration dans Spring.

@Provides expose un composant par programme, comme @Provides dans Dagger ou @Bean in Spring.

Et, enfin, l' annotation @Named est utilisée pour indiquer le nom du composant produit.

AppModule fournit également une SignatureKey utilisée pour signer le contenu envoyé aux clients. Lors de la création de la session pour l'exemple d'application, par exemple, cela définira un cookie, signé avec la clé configurée:

HTTP/1.1 200 OK ... Set-Cookie: RestxSessionSignature-restx-demo="ySfv8FejvizMMvruGlK3K2hwdb8="; RestxSession-restx-demo="..." ...

And check out RESTX's components factory/dependency injection documentation for more.

8. The Launcher Class

And lastly, the AppServer class is used to run the application as a standard Java app in an embedded Jetty server:

public class AppServer { public static final String WEB_INF_LOCATION = "src/main/webapp/WEB-INF/web.xml"; public static final String WEB_APP_LOCATION = "src/main/webapp"; public static void main(String[] args) throws Exception { int port = Integer.valueOf(Optional.fromNullable(System.getenv("PORT")).or("8080")); WebServer server = new Jetty8WebServer(WEB_INF_LOCATION, WEB_APP_LOCATION, port, "0.0.0.0"); System.setProperty("restx.mode", System.getProperty("restx.mode", "dev")); System.setProperty("restx.app.package", "restx.demo"); server.startAndAwait(); } }

Here, the dev mode is used during the development phase to enable features such as auto-compile which shortens the development feedback loop.

We can package the app as a war (web archive) file to deploy in a standalone J2EE web container.

Let's find out how to test the app in the next section.

9. Integration Testing Using Specs

One of the strong features of RESTX is its concept of “specs”. A sample spec would look like this:

title: should admin say hello given: - time: 2013-08-28T01:18:00.822+02:00 wts: - when: | GET hello?who=xavier then: | {"message":"hello xavier, it's 01:18:00"}

The test is written in a Given-When-Then structure within a YAML file which basically defines how the API should respond (then) to a specific request (when) given a current state of the system (given).

The HelloResourceSpecTest class in src/test/resources will trigger the tests written in the specs above:

@RunWith(RestxSpecTestsRunner.class) @FindSpecsIn("specs/hello") public class HelloResourceSpecTest {}

The RestxSpecTestsRunner class is a custom JUnit runner. It contains custom JUnit rules to:

  • set up an embedded server
  • prepare the state of the system (as per the given section in the specs)
  • issue the specified requests, and
  • verify the expected responses

The @FindSpecsIn annotation points to the path of the spec files against which the tests should be run.

The spec helps to write integration tests and provide examples in the API docs. Specs are also useful to mock HTTP requests and record request/response pairs.

10. Manual Testing

We can also test manually over HTTP. We first need to log in, and to do this, we need to hash the admin password in the RESTX console:

hash md5 

And then we can pass that to the /sessions endpoint:

curl -b u1 -c u1 -X POST -H "Content-Type: application/json" -d '{"principal":{"name":"admin","passwordHash":"1d528266b85cf052803a57288"}}' //localhost:8080/api/sessions

(Note that Windows users need to download curl first.)

And now, if we use the session as part of our /message request:

curl -b u1 "//localhost:8080/api/message?who=restx"

Then we'll get something like this:

{"message" : "hello admin, it's 09:56:51"}

11. Exploring the Admin Console

The admin console provides useful resources to control the app.

Let's take a look at the key features by browsing to //127.0.0.1:8080/admin/@/ui.

11.1. API Docs

The API docs section lists all available routes including all the options:

And we can click on individual routes and try them out on the console itself:

11.2. Monitoring

The JVM Metrics section shows the application metrics with active sessions, memory usage, and thread dump:

Under Application Metrics we have mainly two categories of elements monitored by default:

  • BUILD corresponds to the instantiation of the application components
  • HTTP corresponds to HTTP requests handled by RESTX

11.3. Stats

RESTX lets the user choose to collect and share anonymous stats on the application to give information to the RESTX community. We can easily opt out by excluding the restx-stats-admin module.

The stats report things like the underlying OS and the JVM version:

Because this page shows sensitive information,make sure to review its configuration options.

Apart from these, the admin console can also help us:

  • check the server logs (Logs)
  • view the errors encountered (Errors)
  • check the environment variables (Config)

12. Authorization

RESTX endpoints are secured by default. That means if for any endpoint:

@GET("/greetings/{who}") public Message sayHello(String who) { return new Message(who); }

When called without authentication will return a 401 by default.

To make an endpoint public, we need to use the @PermitAll annotation either at the method or class level:

@PermitAll @GET("/greetings/{who}") public Message sayHello(String who) { return new Message(who); }

Note that at the class level, all methods are public.

Further, the framework also allows specifying user roles using the @RolesAllowed annotation:

@RolesAllowed("admin") @GET("/greetings/{who}") public Message sayHello(String who) { return new Message(who); }

With this annotation, RESTX will verify if the authenticated user also has an admin role assigned. In case an authenticated user without admin roles tries to access the endpoint, the application will return a 403 instead of a 401.

By default, the user roles and credentials are stored on the filesystem, in separate files.

So, the user id with the encrypted password is stored under /data/credentials.json file:

{ "user1": "$2a$10$iZluUbCseDOvKnoe", "user2": "$2a$10$oym3Swr7pScdiCXu" }

Et, les rôles des utilisateurs sont définis dans le fichier /data/users.json :

[ {"name":"user1", "roles": ["hello"]}, {"name":"user2", "roles": []} ]

Dans l'exemple d'application, les fichiers sont chargés dans l' AppModule via la classe FileBasedUserRepository :

new FileBasedUserRepository(StdUser.class, mapper, new StdUser("admin", ImmutableSet. of("*")), Paths.get("data/users.json"), Paths.get("data/credentials.json"), true)

La classe StdUser contient les objets utilisateur. Il peut s'agir d'une classe d'utilisateurs personnalisée, mais elle doit être sérialisable dans JSON.

Nous pouvons, bien sûr, utiliser une implémentation UserRepository différente , comme celle qui frappe une base de données.

13. Conclusion

Ce tutoriel a donné une vue d'ensemble du framework RESTX léger basé sur Java.

Le cadre est encore en développement et il pourrait y avoir des difficultés à l'utiliser. Consultez la documentation officielle pour plus de détails.

L'exemple d'application amorcée est disponible dans notre référentiel GitHub.