Vert.x 3学习笔记---14( Web篇 )

之前的文章介绍的vertx-core的内容。我们可以看到core中的东西就像是一个异步的工具集。我们可以在这个工具集的基础上面进行很多的操作。比如:异步的文件操作、异步的socket等等。有一些操作,我们可以看着是‘很底层的’操作,vertx在这些基础的操作上有做了一些‘封装’,比如Vertx-Web,就是web的一些封装,我们可以使用Vertx-Web来开发Web项目。

Vert.x-Web

Vert.x-Web is a set of building blocks for building web applications with Vert.x.
Vert.x-Web是一套使用Vertx构建网络应用的积木。

Vert.x-Web is designed to be powerful, un-opionated and fully embeddable. You just use the parts you want and nothing more. Vert.x-Web is not a container.

You can use Vert.x-Web to create classic server-side web applications, RESTful web applications, ‘real-time’ (server push) web applications, or any other kind of web application you can think of. Vert.x-Web doesn’t care. It’s up to you to chose the type of app you prefer, not Vert.x-Web.

Vert.x-Web is a great fit for writing RESTful HTTP micro-services, but we don’t force you to write apps like that.

Using Vert.x Web

maven:

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-web</artifactId>
  <version>3.2.1</version>
</dependency>

Gradle:

dependencies { compile 'io.vertx:vertx-web:3.2.1' }

Vertx-Web使用Vertx-Core的API构建web项目,因此建议返回去看看Vertx-Core中的Http的内容,如果还没有看过的话。
使用Vertx-Core的API创建一个helloworld的web项目,其代码如下:

HttpServer server = vertx.createHttpServer();

server.requestHandler(request -> {

  // 每一次请求时,都会调用这个Handler
  HttpServerResponse response = request.response();
  response.putHeader("content-type", "text/plain");

  // 向响应中写入内容,并发送到前端
  response.end("Hello World!");
});

server.listen(8080);

Vertx-Web基础

Router(路由器)是Vertx-Web的核心概念。一个Router包含了0个或者多个Routes(路由)对象。
Router(路由器)接收请求,并查找第一个匹配的路由,将请求交由这个路由去操作。Router(路由器)通过一个handler来完成这个功能的。

HttpServer server = vertx.createHttpServer();

Router router = Router.router(vertx);

router.route().handler(routingContext -> {

  // This handler will be called for every request
  HttpServerResponse response = routingContext.response();
  response.putHeader("content-type", "text/plain");

  // Write to the response and end it
  response.end("Hello World from Vert.x-Web!");
});

server.requestHandler(router::accept).listen(8080);

如上例,我们创建了一个http server,然后又创建了一个Router 。这种情况下,我们没有指定路由的规则,所有的请求都会通过这个路由来解析。
对于每一个被路由的请求,有一个独特的路由上下文实例,并且同一实例被传递给该请求的所有处理程序。

处理请求并调用下一个处理程序

当一个Route(路由)处理一个请求时,其参数为RoutingContext。如果不想结束一个response,则需要调用next方法去匹配下一个handler处理程序。
再handler结束执行之前,没有调用next方法,可以使用如下代码:

Route route1 = router.route("/some/path/").handler(routingContext -> {

  HttpServerResponse response = routingContext.response();
  // enable chunked responses because we will be adding data as
  // we execute over other handlers. This is only required once and
  // only if several handlers do output.
  response.setChunked(true);

  response.write("route1\n");

  // Call the next matching route after a 5 second delay
  routingContext.vertx().setTimer(5000, tid -> routingContext.next());
});

Route route2 = router.route("/some/path/").handler(routingContext -> {

  HttpServerResponse response = routingContext.response();
  response.write("route2\n");

  // Call the next matching route after a 5 second delay
  routingContext.vertx().setTimer(5000, tid ->  routingContext.next());
});

Route route3 = router.route("/some/path/").handler(routingContext -> {

  HttpServerResponse response = routingContext.response();
  response.write("route3");

  // Now end the response
  routingContext.response().end();
});

Note, all this happens without any thread blocking.

Using blocking handlers

使用阻塞的handler(处理程序)

有时候,我们不得不做一些阻塞操作。如果需要做阻塞的操作,我们不能使用普通的handler,需要使用阻塞的handler。

router.route().blockingHandler(routingContext -> {

  // Do something that might take some time synchronously
  service.doSomethingThatBlocks();

  // Now call the next handler
  routingContext.next();

});

默认情况下,所有在相同的上下文中执行的的阻塞请求都是有顺序的。这也以为着,后一个阻塞的handler不会执行,直到前一个阻塞的handler执行完毕。
如果你不关心阻塞handler执行的顺序,或者不介意并行的执行handler,可以设置ordered 为false(在使用blockingHandler方法时指定参数)。

Routing by exact path

确定的路径路由

Route route = router.route().path("/some/path/");

route.handler(routingContext -> { // This handler will be called for the following request paths: // `/some/path` // `/some/path/` // `/some/path//` // // but not: // `/some/path/subdir` });

Routing by paths that begin with something

以前缀开始的路径路由

Route route = router.route().path("/some/path/*");

route.handler(routingContext -> {
  // This handler will be called for any path that starts with
  // `/some/path/`, e.g.

  // `/some/path`   // `/some/path/`   // `/some/path/subdir`   // `/some/path/subdir/blah.html`   // `/some/path/foo.html and `/some/path/otherdir/blah.css would both match.
  //
 // but not:
  // `/some/bath` });

Capturing path parameters

捕获路径参数

Route route = router.route(HttpMethod.POST, "/catalogue/products/:productype/:productid/");

route.handler(routingContext -> {

  String productType = routingContext.request().getParam("producttype");
  String productID = routingContext.request().getParam("productid");

  // Do something with them...
});

占位符由 :和参数名称组成。参数名称由any alphabetic character, numeric character or underscore组成。
按上例,如果一个post的请求/catalogue/products/tools/drill123/,那么producttype=tools,productid=drill123

Routing with regular expressions

正则表达式路由

Route route = router.route().pathRegex(".*foo");

route.handler(routingContext -> {

  // This handler will be called for:

  // /some/path/foo
  // /foo
  // /foo/bar/wibble/foo
  // /foo/bar

  // But not:
  // /bar/wibble
});

更简单的调用:

Route route = router.routeWithRegex(".*foo");

route.handler(routingContext -> { // This handler will be called same as previous example });

Capturing path parameters with regular expressions

正则表达式捕获路径参数

Route route = router.routeWithRegex(".*foo");

// This regular expression matches paths that start with something like: // "/foo/bar" - where the "foo" is captured into param0 and the "bar" is captured into // param1 route.pathRegex("\\/([^\\/]+)\\/([^\\/]+)").handler(routingContext -> { String productType = routingContext.request().getParam("param0");
  String productID = routingContext.request().getParam("param1");

  // Do something with them... });

Routing by HTTP method

按HTTP方式路由

HTTP方法有Post、get、put、delete等。

Route order

路由的顺序默认是加入到路由器中的顺序。
当一个请求过来时,第一个匹配的路由会被执行,如果调用了next,则下一个匹配的路由会被执行,如果没有调用next,则会结束这一次请求。

Route route1 = router.route("/some/path/").handler(routingContext -> {

  HttpServerResponse response = routingContext.response();
  response.write("route1\n");

  // Now call the next matching route
  routingContext.next();
});

Route route2 = router.route("/some/path/").handler(routingContext -> {

  HttpServerResponse response = routingContext.response();
  // enable chunked responses because we will be adding data as
  // we execute over other handlers. This is only required once and
  // only if several handlers do output.
  response.setChunked(true);

  response.write("route2\n");

  // Now call the next matching route
  routingContext.next();
});

Route route3 = router.route("/some/path/").handler(routingContext -> {

  HttpServerResponse response = routingContext.response();
  response.write("route3");

  // Now end the response
  routingContext.response().end();
});

输出:

route1
route2
route3

如果我们希望改变执行的顺序,则需要调用order方法。如下:

// Change the order of route2 so it runs before route1
route2.order(-1);

输出:

route2
route1
route3

也可以使用last方法指定一个结尾的路由。

Routing based on MIME type of request

基于请求的MIME类型路由

精确的MIME类型:

router.route().consumes("text/html").handler(routingContext -> { // This handler will be called for any request with // content-type header set to `text/html` });

复杂的MIME类型:

router.route().consumes("text/html").consumes("text/plain").handler(routingContext -> { // This handler will be called for any request with // content-type header set to `text/html` or `text/plain`. });

使用匹配符号:

router.route().consumes("text/*").handler(routingContext -> {

  // This handler will be called for any request with top level type `text`   // e.g. content-type header set to `text/html` or `text/plain` will both match

});
router.route().consumes("*/json").handler(routingContext -> { // This handler will be called for any request with sub-type json // e.g. content-type header set to `text/json` or `application/json` will both match });

Routing based on MIME types acceptable by the client

基于客户端接收的MIME类型路由

头部:Accept: text/plain

router.route().produces("application/json").handler(routingContext -> {

  HttpServerResponse response = routingContext.response();
  response.putHeader("content-type", "application/json");
  response.write(someJSON).end();

});

In this case the route will match with any request with an accept header that matches application/json

router.route().produces("application/json").produces("text/html").handler(routingContext -> {

  HttpServerResponse response = routingContext.response();

  // Get the actual MIME type acceptable
  String acceptableContentType = routingContext.getAcceptableContentType();

  response.putHeader("content-type", acceptableContentType);
  response.write(whatever).end();
});

Combining routing criteria

复合使用路由规则

Route route = router.route(HttpMethod.PUT, "myapi/orders")
                    .consumes("application/json")
                    .produces("application/json");

route.handler(routingContext -> { // This would be match for any PUT method to paths starting with "myapi/orders" with a // content-type of "application/json" // and an accept header matching "application/json" });

Enabling and disabling routes

使用enable和disable来开启和关闭路由。

Context data

在同一个请求中,我们可以使用RoutingContext对象中的上下文数据。

router.get("/some/path").handler(routingContext -> {

  routingContext.put("foo", "bar");//保存数据
  routingContext.next();

});

router.get("/some/path/other").handler(routingContext -> {

  String bar = routingContext.get("foo");//获取数据
  // Do something with bar
  routingContext.response().end();

});

Reroute

重路由

之前介绍的所有路由机制允许你以顺序的方法去处理请求,但是也许你有时候会想回到过去。上下文(context)中没有任何下一个或者上一个handler的信息,这些消息都是动态的。有一种方法可以从当前路由器开始重新启动整个路由。

router.get("/some/path").handler(routingContext -> {

  routingContext.put("foo", "bar");
  routingContext.next();

});

router.get("/some/path/B").handler(routingContext -> {
  routingContext.response().end();
});

router.get("/some/path").handler(routingContext -> {
  routingContext.reroute("/some/path/B");//将请求重路由至‘/some/path/B’ handler
});

Sub-routers

子路由器

有时候,你可能会需要大量的handler处理程序,把它们拆分成多个路由器是有意义的。这对于我们重复利用一些handler,也是非常有用处的。
你可以通过在一个路由的点上装上另一个路由,装上的这个路由就叫子路由。

Router restAPI = Router.router(vertx);

restAPI.get("/products/:productID").handler(rc -> {

  // TODO Handle the lookup of the product....
  rc.response().write(productJSON);

});

restAPI.put("/products/:productID").handler(rc -> {

  // TODO Add a new product...
  rc.response().end();

});

restAPI.delete("/products/:productID").handler(rc -> {

  // TODO delete the product...
  rc.response().end();

});

如上例中,我们做了几个顶级的路由,当GET/PUT/DELETE的request到达时就会执行上面的代码。我们也可以为这个应用创建另外的路由器:

Router mainRouter = Router.router(vertx);

// Handle static resources
mainRouter.route("/static/*").handler(myStaticHandler);

mainRouter.route(".*\\.templ").handler(myTemplateHandler);

像‘/productsAPI/products/product1234’这样的请求就可以被restRounter拦截了。

Localization

本地化

vertx-web可以解析Accept-Language头部,然后配合使用一些方法来处理本地化的信息:

Route route = router.get("/localized").handler( rc -> { // although it might seem strange by running a loop with a switch we // make sure that the locale order of preference is preserved when // replying in the users language. for (Locale locale : rc.acceptableLocales()) { switch (locale.language()) { case "en": rc.response().end("Hello!");
        return;
      case "fr":
        rc.response().end("Bonjour!");
        return;
      case "pt":
        rc.response().end("Olá!");
        return;
      case "es":
        rc.response().end("Hola!");
        return;
    }
  }
  // we do not know the user language so lets just inform that back: rc.response().end("Sorry we don't speak: " + rc.preferredLocale());
});

acceptableLocales方法返回的是一个列表,如果你只关心优先的,你可以通过preferredLocale来,获取第一个local。

Default 404 Handling

404处理程序

如果没有路由匹配请求路径,那么就会Vertx-Web就会发送一个404事件。这可以被你自定义的处理程序来捕获。

Error handling

错误处理程序

Route route = router.get("/somepath/*");

route.failureHandler(frc -> {

  // This will be called for failures that occur
  // when routing requests to paths starting with
  // '/somepath/'
  //"/somepath/*"请求发生错误时,这个handler会被调用。

});

当发生异常(会触发500事件)者调用fail方法时,失败的处理函数会被调用。

Route route1 = router.get("/somepath/path1/");

route1.handler(routingContext -> {

  // Let's say this throws a RuntimeException
  // 抛出一个运行中异常
  throw new RuntimeException("something happened!");

});

Route route2 = router.get("/somepath/path2");

route2.handler(routingContext -> {

  // This one deliberately fails the request passing in the status code
  // 我们故意将请求的状态码传递为不合格的状态
  // E.g. 403 - Forbidden
  routingContext.fail(403);

});

// Define a failure handler 定义一个失败的处理程序
// This will get called for any failures in the above handlers
// 当上面的任何一个发生时,此段程序都会被调用
Route route3 = router.get("/somepath/*");

route3.failureHandler(failureRoutingContext -> {

  int statusCode = failureRoutingContext.statusCode();

  // Status code will be 500 for the RuntimeException or 403 for the other failure
  // 如果是抛出的异常,状态码就会为500;如果是fail(403),状态码则为403
  HttpServerResponse response = failureRoutingContext.response();
  response.setStatusCode(statusCode).end("Sorry! Not today");

});

Request body handling

BodyHandler 可以让你处理请求的body, 限制body的大小 或者 处理文件上传。
You should make sure a body handler is on a matching route for any requests that require this functionality.
你需要确认BodyHandler能够匹配上相应的路由。

由于需要安装程序使用HTTP请求正文和必须执行的任何异步调用完成之前,这个处理器的使用要求是尽快安装在路由器上。

router.route().handler(BodyHandler.create());

Getting the request body

If you know the request body is JSON, then you can use getBodyAsJson, if you know it’s a string you can use getBodyAsString, or to retrieve it as a buffer use getBody.

Limiting body size

To limit the size of a request body, create the body handler then use setBodyLimit to specifying the maximum body size, in bytes. This is useful to avoid running out of memory with very large bodies.

If an attempt to send a body greater than the maximum size is made, an HTTP status code of 413 - Request Entity Too Large, will be sent.

There is no body limit by default.

Merging form attributes

By default, the body handler will merge any form attributes into the request parameters. If you don’t want this behaviour you can use disable it with setMergeFormAttributes.

Handling file uploads

Body handler is also used to handle multi-part file uploads.
If a body handler is on a matching route for the request, any file uploads will be automatically streamed to the uploads directory, which is file-uploads by default.

Each file will be given an automatically generated file name, and the file uploads will be available on the routing context with fileUploads.

router.route().handler(BodyHandler.create());

router.post("/some/path/uploads").handler(routingContext -> { Set<FileUpload> uploads = routingContext.fileUploads();
  // Do something with uploads.... });

Each file upload is described by a FileUpload instance, which allows various properties such as the name, file-name and size to be accessed.

Handling cookies

Vert.x-Web has cookies support using the CookieHandler.

You should make sure a cookie handler is on a matching route for any requests that require this functionality.

router.route().handler(CookieHandler.create());

Manipulating cookies

操作cookies

使用getCookie方法获取cookie,使用cookies来获取cookie集合。
removeCookie—删除一个cookie
addCookie—添加一个cookie

这些cookies都会随着响应被一起写入到前端浏览器中。

router.route().handler(CookieHandler.create());

router.route("some/path/").handler(routingContext -> {

  Cookie someCookie = routingContext.getCookie("mycookie");
  String cookieValue = someCookie.getValue();

  // Do something with cookie...

  // Add a cookie - this will get written back in the response automatically
  routingContext.addCookie(Cookie.cookie("othercookie", "somevalue"));
});

Handling sessions

Vert.x-Web provides out of the box support for sessions.
Vertx-Web提供了对session的额外支持。

使用cookie来存储session的id标识。

To enable sessions in your application you must have a SessionHandler on a matching route before your application logic.
开启sessions,需要使用SessionHandler

Session stores

创建SessionHandler 之前,需要先创建一个存储session的地方(session store instance)。
Vertx-Web提供了两个session store的实例,也可以自己实现session store 的实例:

Local session store
With this store, sessions are stored locally in memory and only available in this instance.
本地化存储 —- 会话存储在本地内存中,在这个实例中。

这是合适的,如果你只有一个V的实例,或者你在你的应用中使用粘性会话和配置你的负载平衡器总是HTTP请求路由到相同的V实例。

SessionStore store1 = LocalSessionStore.create(vertx);

// Create a local session store specifying the local shared map name to use // This might be useful if you have more than one application in the same // Vert.x instance and want to use different maps for different applications SessionStore store2 = LocalSessionStore.create(vertx, "myapp3.sessionmap");

// Create a local session store specifying the local shared map name to use and // setting the reaper interval for expired sessions to 10 seconds SessionStore store3 = LocalSessionStore.create(vertx, "myapp3.sessionmap", 10000);

Clustered session store
With this store, sessions are stored in a distributed map which is accessible across the Vert.x cluster.
分布式存储 —- 会话存储在分布式map中,Vertx集群中的。

Vertx.clusteredVertx(new VertxOptions().setClustered(true), res -> {

  Vertx vertx = res.result();

  // Create a clustered session store using defaults
  SessionStore store1 = ClusteredSessionStore.create(vertx);

  // Create a clustered session store specifying the distributed map name to use
  // This might be useful if you have more than one application in the cluster
  // and want to use different maps for different applications
  SessionStore store2 = ClusteredSessionStore.create(vertx, "myclusteredapp3.sessionmap");
});

Creating the session handler

你需要使用CookieHandler来配合。

Router router = Router.router(vertx);

// We need a cookie handler first router.route().handler(CookieHandler.create());

// Create a clustered session store using defaults SessionStore store = ClusteredSessionStore.create(vertx);

SessionHandler sessionHandler = SessionHandler.create(store);

// Make sure all requests are routed through the session handler too router.route().handler(sessionHandler);

// Now your application handlers
router.route("/somepath/blah/").handler(routingContext -> { Session session = routingContext.session();
  session.put("foo", "bar");
  // etc

});

Using the session

在handler中,你可以使用session方法来获取session的实例。

router.route().handler(CookieHandler.create());
router.route().handler(sessionHandler);

// Now your application handlers
router.route("/somepath/blah").handler(routingContext -> {

  Session session = routingContext.session();

  // Put some data from the session
  session.put("foo", "bar");

  // Retrieve some data from a session
  int age = session.get("age");

  // Remove some data from a session
  JsonObject obj = session.remove("myobj");

});

在响应完成后,session将会自动的保存到session store中。

Session timeout

session可以设置超时时间,当session间隔一段时间后就会失效。当session失效后,session store就会删除这个session。默认的超时时间为30分钟。

Authentication / authorisation

认证/授权

vertx-web提供了额外的handler来支持认证/授权。

Creating an auth handler

创建auth handler时需要一个AuthProvider实例。认证/授权实际上是通过AuthProvider实例来完成的。

router.route().handler(CookieHandler.create());
router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));

AuthHandler basicAuthHandler = BasicAuthHandler.create(authProvider);

Handling auth in your application

如果你希望以/private/开头的所有请求都需要认证的话,你可以这样做:

router.route().handler(CookieHandler.create());
router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));
router.route().handler(UserSessionHandler.create(authProvider));

AuthHandler basicAuthHandler = BasicAuthHandler.create(authProvider);

// All requests to paths starting with '/private/' will be protected
router.route("/private/*").handler(basicAuthHandler); router.route("/someotherpath").handler(routingContext -> { // This will be public access - no login required }); router.route("/private/somepath").handler(routingContext -> { // This will require a login // This will have the value true boolean isAuthenticated = routingContext.user() != null; });

如果认证通过的话,就会注入user对象到RoutingContext中。因此我们可以通过user方法来获取对象,并判断是否已认证通过。

HTTP Basic Authentication

基础的认证

证书明码发送(在http请求头中)。因此,你需要使用https,而不能使用http。
当请求一个需要此认证的资源时,将会向浏览器返回401状态码(WWW-Authenticate,告诉浏览器需要用户名密码登陆)。
当输入用户名和密码时,浏览器在头部Authorization中存放用户名和密码(Base64编码),再请求一次此资源。
然后AuthProvider就会处理这个认证,如果认证不能通过就返回403状态码(拒绝访问)。

Redirect auth handler

重定向认证程序

此认证handler的工作原理为:当请求一个认证资源时,会跳转到一个login页面,提交完表单后,如果认证成功就能访问资源。

router.route().handler(CookieHandler.create());
router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));
router.route().handler(UserSessionHandler.create(authProvider));

AuthHandler redirectAuthHandler = RedirectAuthHandler.create(authProvider);

// All requests to paths starting with '/private/' will be protected
router.route("/private/*").handler(redirectAuthHandler);

// Handle the actual login
router.route("/login").handler(FormLoginHandler.create(authProvider));

// Set a static server to serve static resources, e.g. the login page
router.route().handler(StaticHandler.create());

router.route("/someotherpath").handler(routingContext -> {
  // This will be public access - no login required
});

router.route("/private/somepath").handler(routingContext -> {

  // This will require a login

  // This will have the value true
  boolean isAuthenticated = routingContext.user() != null;

});

JWT authorisation

JWT授权

使用此类授权,意味着如果没有足够的权利被拒绝访问。
To use this handler there are 2 steps involved:

  • Setup an handler to issue tokens (or rely on a 3rd party)
  • Setup the handler to filter the requests

Please note that these 2 handlers should be only available on HTTPS, not doing so allows sniffing the tokens in transit which leads to session hijacking attacks.
请注意,这2者需要使用HTTPS,不这样做的话在传输过程中(例如会话劫持攻击)会使得token被探测到。

Router router = Router.router(vertx);

JsonObject authConfig = new JsonObject().put("keyStore", new JsonObject()
    .put("type", "jceks")
    .put("path", "keystore.jceks")
    .put("password", "secret"));

JWTAuth authProvider = JWTAuth.create(vertx, authConfig);

router.route("/login").handler(ctx -> {
  // this is an example, authentication should be done with another provider...
  if ("paulo".equals(ctx.request().getParam("username")) && "secret".equals(ctx.request().getParam("password"))) {
    ctx.response().end(authProvider.generateToken(new JsonObject().put("sub", "paulo"), new JWTOptions()));
  } else {
    ctx.fail(401);
  }
});

Configuring required authorities

AuthHandler listProductsAuthHandler = RedirectAuthHandler.create(authProvider);
listProductsAuthHandler.addAuthority("list_products");

// Need "list_products" authority to list products
router.route("/listproducts/*").handler(listProductsAuthHandler);

AuthHandler settingsAuthHandler = RedirectAuthHandler.create(authProvider);
settingsAuthHandler.addAuthority("role:admin");

// Only "admin" has access to /private/settings
router.route("/private/settings/*").handler(settingsAuthHandler);

例子后面是基于role/permission来认证的。

Serving static resources

静态资源包括css/js/html等文件。

In the following example all requests to paths starting with /static/ will get served from the directory webroot:

router.route("/static/*").handler(StaticHandler.create());

For example, if there was a request with path /static/css/mystyles.css the static serve will look for a file in the directory webroot/static/css/mystyle.css.

Configuring caching

头部 : cache-control,last-modified, date.

if-modified-since 304
如果静态文件没有变化,服务器端可以返回304告诉浏览器此文件没有变化。

Configuring the index page

通过setIndexPage来配置首页

Changing the web root

通过setWebRoot方法来配置静态资源的主目录。默认为webroot

Serving hidden files

默认情况下,隐藏的静态文件也是可以看见的。如果不希望用户看见隐藏文件,可以使用setIncludeHidden方法。

Directory listing

服务器还可以执行目录列表,默认情况下这个功能是关闭的。可以通过setDirectoryListing来开启。

目录列表启用时,返回的内容取决于接收头中的内容类型。
对于text/html的头部类型来说,可以通过setDirectoryTemplate来渲染目录列表下的page。

Disabling file caching on disk

默认情况下,Vertx-Web会缓存文件,缓存的目录为.vertx。
关闭此功能,可以使用vertx.disableFileCaching(true)方法。

CORS handling

跨服务请求处理程序

允许请求同一个域下,不同服务的资源。

router.route().handler(CorsHandler.create("vertx\\.io").allowedMethod(HttpMethod.GET));

router.route().handler(routingContext -> {

  // Your app handlers

});

Templates

html模板

使用代码如下:

TemplateEngine engine = HandlebarsTemplateEngine.create();
TemplateHandler handler = TemplateHandler.create(engine);

// This will route all GET requests starting with /dynamic/ to the template handler // E.g. /dynamic/graph.hbs will look for a template in /templates/dynamic/graph.hbs router.get("/dynamic/").handler(handler);

// Route all GET requests for resource ending in .hbs to the template handler router.getWithRegex(".+\\.hbs").handler(handler);

MVEL template engine

为了使用此模板,
1. 需要引入io.vertx:vertx-web-templ-mvel:3.2.1
2. 创建实例io.vertx.ext.web.templ.MVELTemplateEngine#create()
3. 文件以.templ后缀结尾。
4. 例子:

The request path is @{context.request().path()}

The variable 'foo' from the session is @{context.session().get('foo')}

The value 'bar' from the context data is @{context.get('bar')}

详细使用请参考:http://mvel.codehaus.org/MVEL+2.0+Templating+Guide

Jade template engine

  1. 引入io.vertx:vertx-web-templ-jade:3.2.1
  2. 创建实例io.vertx.ext.web.templ.JadeTemplateEngine#create()
  3. .jade后缀结尾
  4. 例子:
!!! 5
html
  head
    title= context.get('foo') + context.request().path()
  body

详细使用请参考:https://github.com/neuland/jade4j

Handlebars template engine

  1. 引入io.vertx:vertx-web-templ-handlebars:3.2.1
  2. 创建实例io.vertx.ext.web.templ.HandlebarsTemplateEngine#create()
  3. .hbs后缀结尾

详细使用请参考:https://github.com/jknack/handlebars.java

Thymeleaf template engine

  1. 引入io.vertx:vertx-web-templ-thymeleaf:3.2.1
  2. 创建实例io.vertx.ext.web.templ.ThymeleafTemplateEngine#create()
  3. .html后缀结尾
  4. 例子
[snip]
<p th:text="${context.get('foo')}"></p>
<p th:text="${context.get('bar')}"></p>
<p th:text="${context.normalisedPath()}"></p>
<p th:text="${context.request().params().get('param1')}"></p>
<p th:text="${context.request().params().get('param2')}"></p>
[snip]

详细使用请参考:http://www.thymeleaf.org/

Error handler

You can render your own errors using a template handler or otherwise but Vert.x-Web also includes an out of the boxy “pretty” error handler that can render error pages for you.

The handler is ErrorHandler. To use the error handler just set it as a failure handler for any paths that you want covered.

Request logger

Vert.x-Web includes a handler LoggerHandler that you can use to log HTTP requests.

By default requests are logged to the Vert.x logger which can be configured to use JUL logging, log4j or SLF4J.

See LoggerFormat.

Serving favicons

Vert.x-Web includes the handler FaviconHandler especially for serving favicons.

Favicons can be specified using a path to the filesystem, or by default Vert.x-Web will look for a file on the classpath with the name favicon.ico. This means you bundle the favicon in the jar of your application.

Timeout handler

Vert.x-Web includes a timeout handler that you can use to timeout requests if they take too long to process.

This is configured using an instance of TimeoutHandler.

If a request times out before the response is written a 408 response will be returned to the client.

Here’s an example of using a timeout handler which will timeout all requests to paths starting with /foo after 5 seconds:

router.route("/foo/").handler(TimeoutHandler.create(5000));

Response time handler

This handler sets the header x-response-time response header containing the time from when the request was received to when the response headers were written, in ms., e.g.:

x-response-time: 1456ms

你可能感兴趣的:(Web,nodejs,vert.x)