Einführung in RESTX

1. Übersicht

In diesem Tutorial werden wir einen Rundgang durch das leichtgewichtige Java REST Framework RESTX machen.

2. Funktionen

Das Erstellen einer RESTful-API ist mit dem RESTX-Framework recht einfach. Es enthält alle Standardeinstellungen, die wir von einem REST-Framework erwarten können, z. B. das Bereitstellen und Konsumieren von JSON, Abfrage- und Pfadparametern, Routing- und Filtermechanismen, Nutzungsstatistiken und Überwachung.

RESTX wird außerdem mit einer intuitiven Admin-Webkonsole und einem Befehlszeileninstallationsprogramm für einfaches Bootstrapping geliefert.

Es ist auch unter der Apache-Lizenz 2 lizenziert und wird von einer Community von Entwicklern verwaltet. Die Mindestanforderung an Java für RESTX ist JDK 7.

3. Konfiguration

RESTX wird mit einer praktischen Shell- / Befehls-App geliefert, mit der Sie ein Java-Projekt schnell booten können.

Wir müssen zuerst die App installieren, bevor wir fortfahren können. Die ausführliche Installationsanleitung finden Sie hier.

4. Core Plugins installieren

Jetzt ist es Zeit, die Kern-Plugins zu installieren, um eine App aus der Shell selbst erstellen zu können.

Führen Sie in der RESTX-Shell den folgenden Befehl aus:

shell install

Anschließend werden wir aufgefordert, die Plugins für die Installation auszuwählen. Wir müssen die Zahl auswählen, die auf io.restx zeigt: restx-core-shell . Die Shell wird nach Abschluss der Installation automatisch neu gestartet.

5. Shell App Bootstrap

Mit der RESTX-Shell ist es sehr praktisch, eine neue App zu booten. Es enthält eine assistentenbasierte Anleitung.

Wir beginnen mit der Ausführung des folgenden Befehls auf der Shell:

app new

Dieser Befehl löst den Assistenten aus. Dann können wir entweder die Standardoptionen verwenden oder sie gemäß unseren Anforderungen ändern:

Da wir uns entschieden haben, eine pom.xml zu generieren , kann das Projekt problemlos in jede Standard-Java-IDE importiert werden.

In einigen Fällen müssen wir möglicherweise die IDE-Einstellungen anpassen.

Unser nächster Schritt wird sein, das Projekt aufzubauen:

mvn clean install -DskipTests

Sobald der Build erfolgreich ist, können wir die AppServer- Klasse als Java-Anwendung über die IDE ausführen . Dadurch wird der Server mit der Administratorkonsole gestartet und Port 8080 überwacht.

Wir können zu //127.0.0.1:8080/api/@/ui navigieren und die grundlegende Benutzeroberfläche sehen.

Die mit / @ / beginnenden Routen werden für die Administratorkonsole verwendet, die in RESTX ein reservierter Pfad ist.

Um sich bei der Administratorkonsole anzumelden, können wir den Standardbenutzernamen „admin und das Kennwort verwenden, das wir beim Erstellen der App angegeben haben.

Bevor wir mit der Konsole herumspielen, wollen wir den Code untersuchen und verstehen, was der Assistent generiert hat.

6. Eine RESTX-Ressource

Die Routen werden in der Klasse < main_package> .rest.HelloResource definiert :

@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"))); } }

Es ist sofort ersichtlich, dass RESTX Standard-J2EE-Annotationen für Sicherheits- und REST-Bindungen verwendet. Zum größten Teil werden eigene Anmerkungen für die Abhängigkeitsinjektion verwendet.

RESTX unterstützt auch viele sinnvolle Standardeinstellungen für die Zuordnung von Methodenparametern zur Anforderung.

Zusätzlich zu diesen Standardanmerkungen ist @RestxResource , das es als eine von RESTX erkannte Ressource deklariert.

Der Basispfad wird in der Datei src / main / webapp / WEB-INF / web.xml hinzugefügt . In unserem Fall ist es / api , sodass wir eine GET-Anforderung an // localhost: 8080 / api / message senden können , sofern eine ordnungsgemäße Authentifizierung vorausgesetzt wird.

Die Message- Klasse ist nur eine Java-Bean, die RESTX für JSON serialisiert.

Wir steuern den Benutzerzugriff, indem wir die Annotation RolesAllowed mithilfe von HELLO_ROLE angeben, die vom Bootstrapper generiert wurde.

7. Die Modulklasse

Wie bereits erwähnt, verwendet RESTX Annotationen zur Abhängigkeitsinjektion nach J2EE-Standard wie @Named und erfindet bei Bedarf eigene Annotationen, wobei wahrscheinlich ein Hinweis aus dem Dagger-Framework für @Module und @Provides verwendet wird.

Damit werden die Hauptmodule der Anwendung erstellt, die unter anderem das Administratorkennwort definieren:

@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 definiert eine Klasse, die andere Komponenten definieren kann, ähnlich wie @Module in Dagger oder @Configuration in Spring.

@Provides macht eine Komponente programmgesteuert verfügbar , z. B. @Provides in Dagger oder @Bean in Spring.

Und schließlich wird die Annotation @Named verwendet, um den Namen der erzeugten Komponente anzugeben.

AppModule bietet auch einen SignatureKey zum Signieren von an die Clients gesendeten Inhalten. Beim Erstellen der Sitzung für die Beispiel-App wird beispielsweise ein Cookie gesetzt, das mit dem konfigurierten Schlüssel signiert ist:

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

Weitere Informationen finden Sie in der Dokumentation zur RESTX-Komponentenfabrik / Abhängigkeitsinjektion.

8. Die Launcher-Klasse

Zuletzt wird die AppServer- Klasse verwendet, um die Anwendung als Standard-Java-App auf einem eingebetteten Jetty-Server auszuführen:

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(); } }

Hier wird der Entwickler wird Modus während der Entwicklungsphase verwendete Funktionen zu ermöglichen , wie auto-Kompilierung , die die Entwicklung Rückkopplungsschleife verkürzt.

Wir können die App als Kriegsdatei (Webarchiv) verpacken , um sie in einem eigenständigen J2EE-Webcontainer bereitzustellen.

Im nächsten Abschnitt erfahren Sie, wie Sie die App testen können.

9. Integrationstests mit Spezifikationen

Eines der starken Merkmale von RESTX ist das Konzept der „Spezifikationen“. Eine Probe spec würde wie folgt aussehen:

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" }

Die Benutzerrollen sind in der Datei /data/users.json definiert :

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

In der Beispiel-App werden die Dateien über die FileBasedUserRepository- Klasse in das AppModule geladen :

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

Die StdUser- Klasse enthält die Benutzerobjekte. Es kann eine benutzerdefinierte Benutzerklasse sein, die jedoch in JSON serialisierbar sein muss.

Wir können natürlich eine andere UserRepository- Implementierung verwenden, wie eine, die auf eine Datenbank trifft.

13. Schlussfolgerung

Dieses Tutorial gab einen Überblick über das leichtgewichtige Java-basierte RESTX-Framework.

Das Framework befindet sich noch in der Entwicklung und kann zu rauen Kanten führen. Weitere Informationen finden Sie in der offiziellen Dokumentation.

Die Beispiel-Bootstrap-App ist in unserem GitHub-Repository verfügbar.