之前的文章介绍的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
- 引入
io.vertx:vertx-web-templ-jade:3.2.1
- 创建实例
io.vertx.ext.web.templ.JadeTemplateEngine#create()
- 以
.jade
后缀结尾 - 例子:
!!! 5
html
head
title= context.get('foo') + context.request().path()
body
详细使用请参考:https://github.com/neuland/jade4j
Handlebars template engine
- 引入
io.vertx:vertx-web-templ-handlebars:3.2.1
- 创建实例
io.vertx.ext.web.templ.HandlebarsTemplateEngine#create()
- 以
.hbs
后缀结尾
详细使用请参考:https://github.com/jknack/handlebars.java
Thymeleaf template engine
- 引入
io.vertx:vertx-web-templ-thymeleaf:3.2.1
- 创建实例
io.vertx.ext.web.templ.ThymeleafTemplateEngine#create()
- 以
.html
后缀结尾 - 例子
[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