随着Vert.x进化到3.5.0,本系列也迎来了新篇章。前端
对于CORS,搞Web开发(不论你是前端,仍是后端)的同志应该不陌生,尤为是现在微服务盛行的时代,CORS更是最经常使用的配置项之一。倘若你对此还有一点点的疑问,不用问,你已经落伍了!java
Vert.x很早就支持了CORS,但到了3.5.0,出于安全性上的考虑,它增长了一个限制:git
当allowedOriginPattern为“*”时,allowCredentials不容许为“true”。
这个限制其实起源自协议的限制:github
The string "*" cannot be used for a resource that supports credentials.The string "
*
" cannot be used for a resource that
supports credentials.
即:Access-Control-Allow-Origin为“*”时,Access-Control-Allow-Credentials会被忽略,Credentials信息(通常是Cookie)也不会被user-agent发送。你们能够参考这篇文章(英文原版)了解如何安全地设置各个header。数据库
在3.5.0时,当allowedOriginPattern为“*”和allowCredentials为“true”时,Vert.x会产生一个异常,若不处理会致使Verticle不能正常启动。在以前的版本中,不会出现这种状况。json
断路器是个好东东,强烈建议在框架设计上就考虑它,以免粗心的开发者写的代码扰乱其余好代码。在我写dgate和dfx时,对全部对外暴露的服务也都强加了这个限制。后端
既然CB这么好,是否是动过将其应用到普通Handler上的念头?不过因为Vert.x CB的文档示例中给出的都是清一色的HTTP请求的例子,要想将CB推而广之应用到普通的Handler上还得费点气力。安全
public static void withCircuitBreaker(Vertx vertx, CircuitBreaker circuitBreaker , Accessible accessible, Map params, Handler<Map> successHandler, Handler<Throwable> failureHandler) { circuitBreaker.execute(future -> vertx.executeBlocking(f -> { try { f.complete(accessible.invoke(params)); } catch (Throwable throwable) { f.fail(throwable); } } , result -> { if (result.succeeded()) { future.complete(result.result()); } else { future.fail(result.cause()); } }) ).setHandler(result -> { if (result.succeeded()) { successHandler.handle((Map) result.result()); } else { logger.error("CB[{}] execution failed, cause: ", circuitBreaker.name(), result.cause()); failureHandler.handle(result.cause()); } }); }
它的使用很简单:框架
Utils.withCircuitBreaker(vertx, circuitBreaker, accessible, body.getMap() , result -> Utils.fireJsonResponse(response, 200, result) , throwable -> Utils.fireSingleMessageResponse(response, 500, throwable.getMessage()));
各位可依葫芦画瓢将其改成本身的形式,其中的重点在于以“vertx.executeBlocking”方式来执行Handler的代码。
在稍微正规的队伍中,自动化测试应该已是一个标准实践了,而且Vert.x对于测试也提供了支持。
不过,我不喜欢。缘由有几个:
为了证实我所言非虚,你们能够去看看dgate和dfx的测试代码。这里给出两个例子:
都是基于Spock写的,各位能够体验其酸爽度。
在本系列第一篇里,我就提出了一个钟意的工程结构组成,但里面没有提到“运行时外部配置文件”这一常见的实践。
通过若干项目的锤炼以后,目前对于这种运行时的外部配置文件,我基本造成了一个固定套路:Groovy DSL + Groovy ConfigSlurper。它俩简直是完成这一任务的绝配,比起Vert.x的提供的json配置文件要爽太多。
看看Gradle的build文件,你就能够知道这种DSL的灵活度能够到什么程度,更况且Groovy的语法对于Java开发者极其友好。
或许有人会以为Groovy不酷,甚至有点鄙夷,言必称Scala、Clojure、Kotlin、Go或者Rust。对此,哥只想说:做为开发者,最让人鄙夷的是交不出活,客户才不关心你用什么语言呢!
dgate是我写的一个基于vertx的轻量级网关,全部的配置所有经过配置文件来定义,无需数据库。关于它的详细介绍,能够参见其文档。为了说明上一节采用Groovy DSL的灵活度,这里展现几个dgate的配置例子。
利用dgate的mock功能,分离开的先后端开发人员能够并行工做,只需将mock响应配置到dgate的配置文件中就好。
"/summary" { expected { statusCode = 200 payload { eqLocations = [] opRateInLast30Days = [] myOrgs = [ ["name": "org1", "admin": false] ] } } }
动态Mock为那些返回动态结果(如某些状况下成功,某些状况下失败)的URL模拟提供了便利。
"/login" { required = ["sub", "password"] methods = [HttpMethod.GET, HttpMethod.POST] expected { statusCode = 200 payload = { JWTAuth jwtAuth = Utils.createAuthProvider(Vertx.vertx()) JWTTokenGenerator tokenGenerator = new JWTTokenGenerator(jwtAuth) [token: tokenGenerator.token(["sub": "……", "name": "……", "role": "normal"], 200)] } } }
上例将模拟一个实际的动态JWT,这样的好处在于,方便前端/移动端开发直接去完成刷新token或从新登陆的过程,而不须要再针对此场景作其余特殊处理。
请注意,为了产生动态结果,此处的payload使用的是闭包,而不像前一例用的是Map。此时,闭包的返回值为Mock响应的结果。
以上的例子已经充分展示了将Groovy DSL做为运行时外部配置的能力,能够说是完胜Vert.x自带的json配置方式。
好啦,本期内容就此结束,请保持关注,期待下期继续!