隨着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配置方式。
好啦,本期內容就此結束,請保持關注,期待下期繼續!