之前的文章介绍的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&