Introduction to RESTX – RESTX简介

最后修改: 2018年 11月 26日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll be taking a tour of the lightweight Java REST framework RESTX.

在本教程中,我们将对轻量级Java REST框架RESTX进行考察。

2. Features

2.特点

Building a RESTful API is quite easy with the RESTX framework. It has all the defaults that we can expect from a REST framework like serving and consuming JSON, query and path parameters, routing and filtering mechanisms, usage statistics, and monitoring.

使用RESTX框架,构建一个RESTful API非常容易。它拥有我们可以从REST框架中期待的所有默认功能,如服务和消费JSON、查询和路径参数、路由和过滤机制、使用统计和监控。

RESTX also comes with an intuitive admin web console and command line installer for easy bootstrapping.

RESTX还配有一个直观的管理网络控制台和命令行安装程序,以方便启动。

It’s also licensed under the Apache License 2 and maintained by a community of developers. The minimum Java requirement for RESTX is JDK 7.

它还获得了Apache许可证2的授权,并由一个开发者社区维护。RESTX的最低Java要求是JDK 7。

3. Configuration

3.配置

RESTX comes with a handy shell/command app which is useful to quickly bootstrap a Java project.

RESTX带有一个方便的shell/command应用程序,对于快速启动一个Java项目非常有用。

We need to install the app first before we can proceed. The detailed installation instruction is available here.

我们需要先安装该应用程序,然后才能继续。详细的安装说明可在此处获得。

4. Installing Core Plugins

4.安装核心插件

Now, it’s time to install the core plugins to be able to create an app from the shell itself.

现在,是时候安装核心插件,以便能够从外壳本身创建一个应用程序。

In the RESTX shell, let’s run the following command:

在RESTX shell中,让我们运行以下命令。

shell install

It will then prompt us to select the plugins for installation. We need to select the number which points to io.restx:restx-core-shell. The shell will automatically restart once installation completes.

然后它将提示我们选择要安装的插件。我们需要选择指向io.restx:restx-core-shell的编号。安装完成后,shell将自动重启。

5. Shell App Bootstrap

5.贝壳类应用的Bootstrap

Using the RESTX shell it is very convenient to bootstrap a new app. It provides a wizard-based guide.

使用RESTX外壳,启动一个新的应用程序是非常方便的。它提供了一个基于向导的指南。

We start by executing the following command on the shell:

我们首先在shell上执行以下命令。

app new

This command will trigger the wizard. Then we can either go with the default options or change them as per our requirements:

这个命令将触发向导。然后,我们可以使用默认选项或根据我们的要求进行修改。

shell app bootstrap

Since we have chosen to generate a pom.xml, the project can be easily imported into any standard Java IDE.

由于我们选择了生成pom.xml,该项目可以很容易地导入到任何标准的Java IDE。

In a few cases, we may need to tweak the IDE settings.

在少数情况下,我们可能需要调整IDE的设置

Our next step will be to build the project:

我们的下一步将是建设该项目。

mvn clean install -DskipTests

Once the build is successful we can run the AppServer class as a Java Application from the IDE. This will start the server with the admin console, listening on port 8080.

一旦构建成功,我们可以从IDE中运行AppServer类作为一个Java应用程序。这将通过管理控制台启动服务器,监听端口为8080。

We can browse to http://127.0.0.1:8080/api/@/ui and see the basic UI.

我们可以浏览到http://127.0.0.1:8080/api/@/ui,看到基本的用户界面。

The routes starting with /@/ are used for the admin console which is a reserved path in RESTX.

/@/开头的路由用于管理控制台,这是RESTX的一个保留路径。

To log into the admin console we can use the default username “admin and the password we provided while creating the app.

要登录到管理控制台,我们可以使用默认的用户名 “admin和我们在创建应用程序时提供的密码。

Before we play around with the console, let’s explore the code and understand what the wizard generated.

在我们玩弄控制台之前,让我们先探索一下代码,了解向导生成的内容。

6. A RESTX Resource

6.一个RESTX资源

The routes are defined in <main_package>.rest.HelloResource class:

这些路由是在<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")));
    }
}

It’s immediately apparent that RESTX uses default J2EE annotations for security and REST bindings. For the most part, it uses its own annotations for dependency injection.

很明显,RESTX使用默认的J2EE注解来实现安全和REST绑定。在大多数情况下,它使用自己的注解进行依赖注入。

RESTX also supports many reasonable defaults for mapping method parameters to the request.

RESTX还支持许多合理的默认值,用于将方法参数映射到请求

And, in addition to these standard annotations is @RestxResource, which declares it as a resource that RESTX recognizes.

而且,除了这些标准注解外,还有@RestxResource,它将其声明为RESTX识别的资源。

The base path is added in the src/main/webapp/WEB-INF/web.xml. In our case, it’s /api, so we can send a GET request to http://localhost:8080/api/message, assuming proper authentication.

基本路径被添加到src/main/webapp/WEB-INF/web.xml中。在我们的例子中,它是/api,所以我们可以向http://localhost:8080/api/message发送一个GET请求,前提是有适当的认证。

The Message class is just a Java bean that RESTX serializes to JSON.

Message类只是一个Java bean,RESTX将其序列化为JSON。

We control the user access by specifying the RolesAllowed annotation using the HELLO_ROLE which was generated by the bootstrapper.

我们通过指定RolesAllowed注解来控制用户访问,该注解是由bootstrapper生成的HELLO_ROLE

7. The Module Class

7.模块类

As noted earlier, RESTX uses J2EE-standard dependency injection annotations, like @Named, and invents its own where needed, likely taking a cue from the Dagger framework for @Module and @Provides.

如前所述,RESTX使用J2EE标准的依赖注入注解,如@Named,并在需要时发明了自己的注解,可能是借鉴了Dagger框架的@Module@Provides.

It uses these to create the applications main module, which among other things, defines the admin password:

它使用这些来创建应用程序的主模块,其中包括定义管理员密码:

@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 defines a class that can define other components, similar to @Module in Dagger, or @Configuration in Spring.

@Module定义了一个可以定义其他组件的类,类似于Dagger中的@Module,或者Spring中的@Configuration

@Provides exposes a component programmatically, like @Provides in Dagger, or @Bean in Spring.

@Provides以编程方式公开一个组件,就像Dagger中的@Provides,或者Spring中的@Bean

And, finally, the @Named annotation is used to indicate the name of the component produced.

最后,@Named注解被用来表示产生的组件的名称。

AppModule also provides a SignatureKey used to sign content sent to the clients. While creating the session for the sample app, for example, this will set a cookie, signed with the configured key:

AppModule还提供了一个SignatureKey,用于签署发送到客户端的内容。例如,在为样本应用程序创建会话时,这将设置一个cookie,用配置的密钥签名。

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.

并查看RESTX的组件工厂/依赖注入文档以了解更多。

8. The Launcher Class

8.发射器类

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

最后,AppServer类用于将应用程序作为标准的Java应用程序在嵌入式Jetty服务器中运行。

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.

这里,dev模式在开发阶段使用,以启用自动编译等功能,缩短开发反馈循环。

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

我们可以将应用程序打包成一个war(web archive)文件,以便在独立的J2EE web容器中部署。

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

让我们在下一节中了解如何测试该应用程序。

9. Integration Testing Using Specs

9.使用规格书进行集成测试

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

RESTX的强大功能之一是其 “规格 “的概念。一个样本spec看起来像这样。

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).

该测试以Given-When-Then结构写入YAML文件中,该文件基本上定义了鉴于系统的当前状态(when),API应如何响应(then)某个特定请求(when)。

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

src/test/resources中的HelloResourceSpecTest类将触发上述规范中写的测试。

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

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

RestxSpecTestsRunner类是一个custom JUnit runner。它包含了自定义的JUnit规则,以。

  • 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.

@FindSpecsIn注解指向测试应该运行的规格文件的路径。

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.

规范有助于编写集成测试并在API文档中提供示例。规范对于模拟HTTP请求并记录请求/响应对也很有用。

10. Manual Testing

10.手动测试

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:

我们也可以通过HTTP手动测试。我们首先需要登录,要做到这一点,我们需要在RESTX控制台中哈希管理员密码。

hash md5 <clear-text-password>

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

然后我们可以将其传递给/sessions端点。

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

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

(注意,Windows用户需要先下载 curl)。

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

而现在,如果我们使用会话作为我们的/message请求的一部分。

curl -b u1 "http://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

11.探索管理控制台

The admin console provides useful resources to control the app.

管理控制台提供有用的资源来控制应用程序。

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

让我们通过浏览http://127.0.0.1:8080/admin/@/ui来看看主要功能。

11.1. API Docs

11.1.API文档

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

API文档部分列出了所有可用的路线,包括所有选项。

admin api docs

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

而且我们可以点击个别路线,在控制台本身尝试一下。

admin api docs 2

11.2. Monitoring

11.2.监测

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

JVM指标部分显示应用程序的指标,包括活动会话、内存使用和线程转储。

admin monitoring

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

11.3.统计数字

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.

RESTX允许用户选择收集和分享应用程序的匿名统计,以向RESTX社区提供信息。我们可以通过排除restx-stats-admin模块来轻松选择退出。

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

统计数据报告了底层操作系统和JVM版本等内容。

admin stats

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

12.授权

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

RESTX端点默认是安全的。这意味着,如果对于任何端点来说。

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

When called without authentication will return a 401 by default.

当调用时没有认证,将默认返回401

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

要使一个端点公开,我们需要在方法或类的层面上使用@PermitAll注解。

@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注解来指定用户角色。

@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.

有了这个注解,RESTX将验证已认证的用户是否也分配了一个管理员角色。如果一个没有管理员角色的认证用户试图访问该端点,应用程序将返回一个403,而不是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:

因此,带有加密密码的用户ID被存储在/data/credentials.json文件中。

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

And, the user roles are defined in /data/users.json file:

而且,用户角色是在/data/users.json文件中定义的。

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

In the sample app, the files get loaded in the AppModule via the FileBasedUserRepository class:

在示例应用程序中,文件通过AppModule类加载到FileBasedUserRepository

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

The StdUser class holds the user objects. It can be a custom user class but it needs to be serializable into JSON.

StdUser类持有用户对象。它可以是一个自定义的用户类,但它需要可序列化为JSON。

We can, of course, use a different UserRepository implementation, like one that hits a database.

当然,我们可以使用不同的UserRepository实现,比如打入数据库的实现。

13. Conclusion

13.结语

This tutorial gave an overview of the lightweight Java-based RESTX framework.

本教程对基于Java的轻量级RESTX框架进行了概述。

The framework is still in development and there might be some rough edges using it. Check out the official documentation for more details.

该框架仍在开发中,使用它可能会有一些粗糙的边缘。请查看官方文档以了解更多细节。

The sample bootstrapped app is available in our GitHub repository.

在我们的GitHub资源库中可以找到引导的应用样本。