您的位置:首页 > 其它

Vert.x Web模块(二)

2016-09-27 10:24 218 查看


上下文数据

可以用RoutingContext维护和使用上下文数据,此数据将在请求生命周期的所有处理器之间共享。这是一个例子描述了,在某个处理器里向上下文中设置了数据,在接下来的处理器中接收数据。可用RoutingContext中的put方法添加一些对象,get方法获取一些对象。发送到/some/path/other路径的请求将匹配两个路由。

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

 

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

 routingContext.next();

 

});

 

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

 

 
Stringbar=routingContext.get("foo");

 
//Dosomethingwithbar

 routingContext.response().end();

 

});

可用data方法访问完整的上下文数据映射表。

 

重新路由

到现在,所有的路由机制允许你顺序的处理请求,然而有的时候,可能要进行路由回顾。因为上下文不暴露任何前一个或者后一个处理器的任何信息,大多是因为此信息是动态的,所以用一个方法重启从当前路由器的启动到现在的所有路由。

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并且向上下文添加一个值,然后执行下一个处理器然,然后将请求重新路由到/some/path/B,在/some/path/B的处理器中中止请求。

可以基于新路径或者新路么新方法进行重新路由。注意基于方法的重路么可能会引入安全问题,例如通常一个安全的GET请求会变成DELETE请求。

 

子路由器

有时在有很多处理器情况下,将处理器分到多个路由器是很有意义的。以对于在不同的应用中重用处理器集合,植根于不同的根路径很有用。这样做,可以将一个路由器挂接到另外一个路由器的挂载点上。被挂载的路由器称之为子路由器。子路由器可以挂接其他子路由器,只要你喜欢,可以有多个级别的子路由器。

让我们来看一个简单的子路由器挂接到其他路由器的例子。

子路由器维护一个处理器的集合,这此处理器对简单的虚拟的RESTAPI作出响应。我们在将来某个时候,将此子路由器挂接到其他路由器(意思可以重用处理器集合)。完整的RESTAPI实现将不显示。

这是一个子路由:

RouterrestAPI=Router.router(vertx);

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

 
//TODOHandlethelookupoftheproduct....

 
rc.response().write(productJSON);

 

});

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

 
//TODOAddanewproduct...

 rc.response().end();

});

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

 
//TODOdeletetheproduct...

 rc.response().end();

 

});

如果此路由器作为顶级路由器使用,对URL/products/product1234的不同方法,如GET/PUT/DELETE请求,将调用不同的API。

 

然而,假设有一个网站已经有另外一个路由器进行了描述:

RoutermainRouter=Router.router(vertx);

 

//Handlestaticresources

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

 

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

我们可以将此路由挂接到主路由器的挂接点,此例中的/producetsAPI

mainRouter.mountSubRouter("/productsAPI",restAPI);

这样RESTAPI现在能通过路径/productsAPI/products/product1234进行访问了。

本地化

Vert.xWeb解析Accept-Language头并提供一些帮助方法,说明客户优选哪个Local或者哪个是优选的首选地区的排序规则。

Routeroute=router.get(
"/localized"
).handler(rc->{
[/code]
 
//althoughitmightseemstrangebyrunningaloopwithaswitchwe
[/code]
 
//makesurethatthelocaleorderofpreferenceispreservedwhen
[/code]
 
//replyingintheuserslanguage.
[/code]
 
for
(Localelocale:rc.acceptableLocales()){
[/code]
   
switch
(locale.language()){
[/code]
     
case
"en"
:
[/code]
       rc.response().end(
"Hello!"
);
[/code]
       
return
;
[/code]
     
case
"fr"
:
[/code]
       rc.response().end(
"Bonjour!"
);
[/code]
       
return
;
[/code]
     
case
"pt"
:
[/code]
       rc.response().end(
"Olá!"
);
[/code]
       
return
;
[/code]
     
case
"es"
:
[/code]
       rc.response().end(
"Hola!"
);
[/code]
       
return
;
[/code]
   }

 }

 
//wedonotknowtheuserlanguagesoletsjustinformthatback:
[/code]
 rc.response().end(
"Sorrywedon'tspeak:"
+rc.preferredLocale());
[/code]
});


主要方法acceptableLocales会返回一个用户能理解Locale排序列表。如果仅是对用户优选的Local感兴趣,用preferredLocale方法返回列表的第一个元素,如果用户没有提供Locale,将返回null。

 

默认的404处理器

如果没有路由匹配的特定请求,Vert.x-Web会产生一个404错误。这也能有自定义的错误处理器处理。或者扩展错误处理器提供使用支持,如果没有提供错误处理器,Vert.x-Web将回发一个基本的404响应。

 

错误处理

只要能设置了处理请求的处理器,就能设置路由中处理失败的处理器。失败处理器有着与正常处理器一样能用于精确路由构建配条件。例如,可以指定一个失败处理器,用来处理特定路径上的失败,或者特定HTTP方法上的失败。也能在应用的不同部分设置不同的失败处理器。这里有一个失败处理器的例子,在路由GET请求到/somepath/开始的路径时产生失败时将调用此处理器。

Routeroute=router.get(
"/somepath/*"
);
[/code]
 

route.failureHandler(frc->{

 

 
//Thiswillbecalledforfailuresthatoccur
[/code]
 
//whenroutingrequeststopathsstartingwith
[/code]
 
//'/somepath/'
[/code]
 

});


如果处理器抛出异常将出现路由失败,或者在处理器中调用fail方法指定一个HTTP状态编码显示通知失败时也会出现路由失败。

如果从处理器上获取一个异常,这将导致一个500编码的失败。在处理失败时,RoutingContext将会传递给失败处理器,所以可能从上下文中获取失败或失败码,失败处理器可用上下文产生失败响应。

Routeroute1=router.get(
"/somepath/path1/"
);
[/code]
route1.handler(routingContext->{

 
//Let'ssaythisthrowsaRuntimeException
[/code]
 
throw
new
RuntimeException(
"somethinghappened!"
);
[/code]
 

});

Routeroute2=router.get(
"/somepath/path2"
);
[/code]
route2.handler(routingContext->{

 
//Thisonedeliberatelyfailstherequestpassinginthestatuscode
[/code]
 
//E.g.403-Forbidden
[/code]
 routingContext.fail(
403
);
[/code]
 

});

//Defineafailurehandler[code]
[/code]
//Thiswillgetcalledforanyfailuresintheabovehandlers[code]
[/code]
Routeroute3=router.get(
"/somepath/*"
);
[/code]
route3.failureHandler(failureRoutingContext->{

 
int
statusCode=failureRoutingContext.statusCode();
[/code]
 

 
//Statuscodewillbe500fortheRuntimeExceptionor403fortheotherfailure
[/code]
 HttpServerResponseresponse=failureRoutingContext.response();

 response.setStatusCode(statusCode).end(
"Sorry!Nottoday"
);
[/code]
 

});


在运行错误处理器时,如在状态消息头中放入字符可能会产生错误,然后原始的状态消息会改变根据错误码生成默认的消息。这是在对突如其来的宕机和socket关闭时不能正常完成协议的请况一种保持HTTP协议语义完敕整的一处权衡措施。

 

处理请求体

BodyHandler作用是让程序接收请求体,限制请求体大小和处理文件上传。必须确保请求体处理器与需要此功能的请求的路由相匹配。此处理器的用法是尽可能的在路由器中使,因为路由在执行任何异步请求调用之前,必须安装此处理器用于处理HTTP请求体。

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

获取请求体

如果知道请求体是JSON,然后可用getBodyAsJson方法用于获取请求的JSON数据。如知道请求体是字符串,用getBodyAsString获取,或者使用getBody获取。

 

限制请求体大小

为了限制请求体大小,创建请求体处理器用setBodyLimit方法设置请求体大小,单位是字节。这可以避免因非常大的请求体而造成内存溢出。

如果偿试发送超过请求体限制的大小,413HTTP状态码-(请求体过大)将会发磅。默认不限制请求体。

 

合并表单属性

默认,请求体处理器会合并任何表单属性到请求参数。如果不想这样做,可调用setMergeFormAttributes方法禁用。

 

处理文件上传

表单处理器也可用于处理多部分文件上传。如果请求处理器与请求路由匹配,任何文件上传会自动经流的方式上传到指定目录,此目录默认是file-uploads,每个文件会给定一个自动生成的文件名,并且上传的文件可以在路由上下文中用fileUploads获取。这是一个例子:

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

 

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

 

 Set<FileUpload>uploads=routingContext.fileUploads();

 
//Dosomethingwithuploads....

 

});

每个文件使用一个FileUpload进行描述,FileUpload有多个属性可供访问,如名称,文件名,文件大小。

处理Cookie

Vert.x-Web使用CookieHandler支持cookies。必须确保cookie处理器与需要此功能的请求的路由相匹配。

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

 

操作Cookies

向getCookie方法传入cookies名称获取cookie,或者用cookies方法获取所有cookie集合。调用removeCookie方法移除cookie。调用addCookie方法添加一个cookie。在响应头被写回时,Cookies集合也被自动写回响应,所以浏览器可以存储cookie。Cookies用Cookie类进行描述。可通过Cookie类获取cookie的名称,值,域名,路径和其他正常的cookie属性。这是一个查询和添加cookie的例子:

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

 

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

 

 
CookiesomeCookie=routingContext.getCookie("mycookie");

 
StringcookieValue=someCookie.getValue();

 

 
//Dosomethingwithcookie...

 

 
//Addacookie-thiswillgetwrittenbackintheresponseautomatically

 routingContext.addCookie(Cookie.cookie("othercookie","somevalue"));

});

 

会话处理

Vert.x-Web为会话支持实现了创新。服务端会话在HTTP请求间共享与浏览器端会话持续时间一样长,并提供了存储会话范围信息的能力,如购物车。Vert.x-Web用会话cookies标识一个服务器端会话。此会话cookie是临时的在浏览器关闭后会被删除。Vert.x-Web不会在会话cookie中放入实际会话数据——cookie简单使用一个在服务器端查询实际会话的标识符。此标识符是一个由安会随机器产生的随机UUID,因此不会被容易猜到。

 

cookies通过网络在HTTP请求和响应之间传输,在会活被使用时,cookie总是明智的确保你在使用HTTPS。如果试图直接在HTTP上使用会话,Vert.x将会警告你。

 

为了在你的应用中启用会话,你必须在开始你的应用逻辑之前在匹配的路由上设置一个会话处理器(SessionHandler)。会话处理器处理会话cookie的创建和查询会话,所以不必自己亲自处理。

 

会话存储

为了创建一个会话处理器,需要有一个会话存储实例。会话存储是应用中持有实际会话的对象。

Vert.x-Web提供了两个会话存储的实现,如果你愿意,也可以自己编写。

 

本地会话存储

此存储,会话被存储在本地内存,只能在此实例获取。如果在你的应用中仅有一个Vert.x实例会使用粘滞会话,并且配置负载均衡器总是将HTTP请求路由到相同的Vert.x实例时,使用本地会话存储是适合的。如果不确定所有请求是否会在同一服务器被终止,请不要使用此存储,因为你的请求可能会在另外的服务器上结束,但服务器并不知道你的会话。本地会话存储用共享的本地map实现,并且会在会话超时时会被清除。可用LocalSessionStore.create方法配置过期会话清除器的执行的时间间隔。这有一些创建LocalSessionStore的例子:

SessionStorestore1=LocalSessionStore.create(vertx);

 

//Createalocalsessionstorespecifyingthelocalsharedmapnametouse

//Thismightbeusefulifyouhavemorethanoneapplicationinthesame

//Vert.xinstanceandwanttousedifferentmapsfordifferentapplications

SessionStorestore2=LocalSessionStore.create(vertx,"myapp3.sessionmap");

 

//Createalocalsessionstorespecifyingthelocalsharedmapnametouseand

//settingthereaperintervalforexpiredsessionsto10seconds

SessionStorestore3=LocalSessionStore.create(vertx,"myapp3.sessionmap",10000);

 

集群会话存储

对于此存储,会话被存储进分布式map中,在整个Vert.x集群中可以访问。如果你不使用粘滞会话此会话存储是适合的,例如负载均衡器将同一个浏览器的不同请求分发到不同服务器。在集群中的任何节点,会话都可以访问此存储。为了使用集群的会话存储,确保你的Vert.x处理集群中。这是创建ClusteredSessionStore的例子:

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

 

 
Vertxvertx=res.result();

 

 
//Createaclusteredsessionstoreusingdefaults

 
SessionStorestore1=ClusteredSessionStore.create(vertx);

 

 
//Createaclusteredsessionstorespecifyingthedistributedmapnametouse

 
//Thismightbeusefulifyouhavemorethanoneapplicationinthecluster

 
//andwanttousedifferentmapsfordifferentapplications

 
SessionStorestore2=ClusteredSessionStore.create(vertx,"myclusteredapp3.sessionmap");

});

 

创建会话处理器

一旦你已经创建了会话存储,就可以创建会话处理器了,并将其添加到一个路由。在应用处理器之前,必须确保会话处理器被路由到一个路由。也需要一个CookieHandler作为会话处理器用于根据cookie查找会话。在路由时,此cookie处理器会在会话处理器之前路由。这是一个例子:

Routerrouter=Router.router(vertx);


 

//Weneedacookiehandlerfirst

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

 

//Createaclusteredsessionstoreusingdefaults

SessionStorestore=ClusteredSessionStore.create(vertx);

 

SessionHandlersessionHandler=SessionHandler.create(store);

 

//Makesureallrequestsareroutedthroughthesessionhandlertoo

router.route().handler(sessionHandler);

 

//Nowyourapplicationhandlers

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

 

 
Sessionsession=routingContext.session();

 
session.put("foo","bar");

 
//etc

 

});

会话处理器确保,在获取应用处理器之前,在会话存储中自动查找会话(或者在不存在会话时创建)并设置路由上下文。

 

使用会话

在请求处理器中可以用session方法访问会话。使用put方法向会话中存放数据,用get方法从会话中获取数据,用remove方法从会话中移除数据。会话中条目的键不总是字符串。在本地会话存储中键可以是任何类型,在集群会话中存储中,键可以是一些基本类型,或者Buffer,JsonObject,JsonArray或者可序列化对象,值必须在整个集群中序列化。这是一个操作会话数据的例子:

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

router.route().handler(sessionHandler);

 

//Nowyourapplicationhandlers

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

 

 
Sessionsession=routingContext.session();

 

 
//Putsomedatafromthesession

 session.put("foo","bar");

 

 
//Retrievesomedatafromasession

 
intage=session.get("age");

 

 
//Removesomedatafromasession

 
JsonObjectobj=session.remove("myobj");

 

});

在响应完成后,会话会自动写回到存储。可用destroy方法手动销毁一个会话。这将从上下文和会话存储中移除会话。在下一个来自浏览器的请求被路由并通过会话处理器时,如果没有会话对象,一个新的会话会被自动创建。

 

会话超时

如果不访的时间超过超时时间,会话会自动超时。当一个会话超时,会话将会从会话存储中删除。在请求到达并且找到会话,同时响应完成和会话被存回会话存储时,会话会自动标识为被访问过。也可以用setAccessed手动标识一个会话被访问过。在创建会话处理器时,会话超时可以配置。默认超时是30分钟。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息