配置緩存
默認情況下,靜態處理程序將設置緩存標頭以使瀏覽器能夠有效地緩存文件。
Vert.x的Web設置標題cache-control
,last-modified
和date
。
cache-control
max-age=86400
默認設置為。這相當於一天。setMaxAgeSeconds
如果需要,可以配置它 。
如果瀏覽器發送帶有if-modified-since
標頭的GET或HEAD請求,並且該資源自該日期起未被修改,304
則返回狀態,告知瀏覽器使用其本地緩存的資源。
如果不需要處理緩存頭,則可以禁用它setCachingEnabled
。
啟用緩存處理后,Vert.x-Web將緩存內存中資源的最后修改日期,這樣可以避免磁盤命中每次都檢查實際的上次修改日期。
緩存中的條目具有到期時間,在此之后,將再次檢查磁盤上的文件並更新緩存條目。
如果您知道您的文件永遠不會在磁盤上更改,那么緩存條目將永遠不會過期。這是默認值。
如果您知道在服務器運行時您的文件可能在磁盤上發生更改,那么您可以將只讀文件設置為false setFilesReadOnly
。
要在任何時候啟用可以在內存中緩存的最大條目數,您可以使用 setMaxCacheSize
。
要配置可以使用的緩存條目的到期時間setCacheEntryTimeout
。
配置索引頁面
對根路徑的任何請求/
都將導致索引頁面被提供。默認情況下,索引頁面是index.html
。這可以配置setIndexPage
。
更改Web根目錄
默認情況下,將從目錄提供靜態資源webroot
。配置此用途 setWebRoot
。
目錄列表
服務器還可以執行目錄列表。默認情況下,禁用目錄列表。要啟用它setDirectoryListing
。
啟用目錄列表時,返回的內容取決於accept
標頭中的內容類型。
對於text/html
目錄列表,可以使用用於呈現目錄列表頁面的模板進行配置setDirectoryTemplate
。
禁用磁盤上的文件緩存
默認情況下,Vert.x會將從類路徑提供的文件緩存到磁盤上的文件中,該文件位於.vertx
當前工作目錄中調用的目錄的子目錄中。這在將服務部署為生產中的fatjars時非常有用,每次從類路徑提供文件都很慢。
在開發過程中,這可能會導致問題,就像在服務器運行時更新靜態內容一樣,緩存文件將不會提供更新的文件。
要禁用文件緩存可以提供您vert.x選項的屬性fileResolverCachingEnabled
來false
。為了向后兼容,它還會將該值默認為系統屬性vertx.disableFileCaching
。例如,您可以在IDE中設置運行配置,以便在運行主類時進行設置。
CORS處理
跨源資源共享是一種安全機制,允許從一個域請求資源並從另一個域提供資源。
Vert.x-Web包含一個處理CorsHandler
CORS協議的處理程序。
這是一個例子:
router.route().handler(CorsHandler.create("vertx\\.io").allowedMethod(HttpMethod.GET)); router.route().handler(routingContext -> { // Your app handlers });
模板
Vert.x-Web包括動態頁面生成功能,包括對幾個流行模板引擎的開箱即用支持。您也可以輕松添加自己的。
模板引擎由描述TemplateEngine
。為了渲染模板 render
,使用了。
使用模板最簡單的方法不是直接調用模板引擎而是使用模板引擎 TemplateHandler
。此處理程序根據HTTP請求中的路徑為您調用模板引擎。
默認情況下,模板處理程序將在名為的目錄中查找模板templates
。這可以配置。
處理程序將返回具有text/html
默認內容類型的呈現結果。這也可以配置。
創建模板處理程序時,您將傳入所需模板引擎的實例。模板引擎未嵌入到vertx-web中,因此您需要配置項目以訪問它們。為每個模板引擎提供配置。
這里有些例子:
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/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模板引擎
要使用MVEL,您需要將以下依賴項添加到項目中: io.vertx:vertx-web-templ-mvel:3.8.0
。使用以下命令創建MVEL模板引擎的實例:io.vertx.ext.web.templ.MVELTemplateEngine#create()
使用MVEL模板引擎時,.templ
如果文件名中未指定擴展名,它將默認查找帶擴展名的模板。
路由上下文RoutingContext
在MVEL模板中可用作context
變量,這意味着您可以基於上下文中的任何內容(包括請求,響應,會話或上下文數據)來呈現模板。
這里有些例子:
請求路徑是@ {context.request()。path()} 會話中的變量'foo'是@ {context.session()。get('foo')} 上下文數據中的值“bar”是@ {context.get('bar')}
玉模板引擎
要使用Jade模板引擎,您需要將以下依賴項添加到項目中: io.vertx:vertx-web-templ-jade:3.8.0
。使用以下方法創建Jade模板引擎的實例:io.vertx.ext.web.templ.JadeTemplateEngine#create()
。
使用Jade模板引擎時,.jade
如果文件名中未指定擴展名,它將默認查找帶擴展名的模板。
路由上下文RoutingContext
在Jade模板中可用作context
變量,這意味着您可以基於上下文中的任何內容(包括請求,響應,會話或上下文數據)來呈現模板。
這里有些例子:
!五 HTML 頭 title = context.get('foo')+ context.request()。path() 身體
把手模板引擎
要使用Handlebars,您需要將以下依賴項添加到項目中: io.vertx:vertx-web-templ-handlebars:3.8.0
。使用以下方法創建Handlebars模板引擎的實例:io.vertx.ext.web.templ.HandlebarsTemplateEngine#create()
。
使用Handlebars模板引擎時,.hbs
如果文件名中未指定擴展名,它將默認查找帶擴展名的模板。
Handlebars模板無法調用對象中的任意方法,因此我們不能將路由上下文傳遞給模板,讓模板像我們可以使用其他模板引擎一樣內省它。
相反,上下文data
在模板中可用。
如果要訪問其他數據(如請求路徑,請求參數或會話數據),則應在模板處理程序之前將其添加到處理程序中的上下文數據中。例如:
TemplateHandler handler = TemplateHandler.create(engine); router.get("/dynamic").handler(routingContext -> { routingContext.put("request_path", routingContext.request().path()); routingContext.put("session_data", routingContext.session().data()); routingContext.next(); }); router.get("/dynamic/").handler(handler);
有關如何編寫把手模板的信息,請參閱Handlebars Java端口文檔。
Thymeleaf模板引擎
要使用Thymeleaf,您需要為項目添加以下依賴項: io.vertx:vertx-web-templ-thymeleaf:3.8.0
。使用以下方法創建Thymeleaf模板引擎的實例:io.vertx.ext.web.templ.ThymeleafTemplateEngine#create()
。
使用Thymeleaf模板引擎時,.html
如果文件名中未指定擴展名,它將默認查找帶擴展名的模板。
路由上下文RoutingContext
在Thymeleaf模板中可用作context
變量,這意味着您可以基於上下文中的任何內容(包括請求,響應,會話或上下文數據)來呈現模板。
這里有些例子:
[剪斷] <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> [剪斷]
有關如何編寫Thymeleaf模板的信息,請參閱Thymeleaf文檔。
Apache FreeMarker模板引擎
要使用Apache FreeMarker,您需要將以下依賴項添加到項目中: io.vertx:vertx-web-templ-freemarker:3.8.0
。使用以下方法創建Apache FreeMarker模板引擎的實例:io.vertx.ext.web.templ.Engine#create()
。
使用Apache FreeMarker模板引擎時,.ftl
如果文件名中未指定擴展名,它將默認查找帶擴展名的模板。
路由上下文RoutingContext
在Apache FreeMarker模板中作為context
變量提供,這意味着您可以基於上下文中的任何內容(包括請求,響應,會話或上下文數據)來呈現模板。
這里有些例子:
[剪斷] <p th:text =“$ {context.foo}”> </ p> <p th:text =“$ {context.bar}”> </ p> <p th:text =“$ {context.normalisedPath()}”> </ p> <p th:text =“$ {context.request()。params()。param1}”> </ p> <p th:text =“$ {context.request()。params()。param2}”> </ p> [剪斷]
有關如何編寫Apache FreeMarker模板的信息,請參閱Apache FreeMarker文檔。
卵石模板引擎
要使用Pebble,您需要為項目添加以下依賴項: io.vertx:vertx-web-templ-pebble:3.8.0
。使用以下方法創建Pebble模板引擎的實例:io.vertx.ext.web.templ.PebbleTemplateEngine#create(vertx)
。
使用Pebble模板引擎時,.peb
如果文件名中未指定擴展名,它將默認查找帶擴展名的模板。
路由上下文RoutingContext
在Pebble模板中可用作context
變量,這意味着您可以基於上下文中的任何內容(包括請求,響應,會話或上下文數據)來呈現模板。
這里有些例子:
[剪斷] <p th:text =“{{context.foo}}”> </ p> <p th:text =“{{context.bar}}”> </ p> <p th:text =“{{context.normalisedPath()}}”> </ p> <p th:text =“{{context.request()。params()。param1}}”> </ p> <p th:text =“{{context.request()。params()。param2}}”> </ p> [剪斷]
搖桿模板引擎
要使用Rocker,請將其io.vertx:vertx-web-templ-rocker:3.8.0
作為依賴項添加到項目中。然后,您可以使用創建Rocker模板引擎實例io.vertx.ext.web.templ.rocker#create()
。
然后,傳遞給render
方法的JSON上下文對象的值將作為模板參數公開。鑒於:
[剪斷] final JsonObject context = new JsonObject() .put(“foo”,“badger”) .put(“bar”,“fox”) .put(“context”,new JsonObject()。put(“path”,“/ foo / bar”)); engine.render(context,“somedir / TestRockerTemplate2”,render - > { //(...) }); [剪斷]
然后模板可以作為以下somedir/TestRockerTemplate2.rocker.html
資源文件:
@import io.vertx.core.json.JsonObject @args(JsonObject context,String foo,String bar) 你好@foo和@bar 請求路徑是@ context.getString(“path”)
禁用緩存
在開發期間,您可能希望禁用模板緩存,以便在每個請求上重新評估模板。為此,您需要設置系統屬性:io.vertx.ext.web.TemplateEngine.disableCache
to true
。
默認情況下,它將為false。因此始終啟用緩存。
錯誤處理程序
您可以使用模板處理程序或其他方式呈現自己的錯誤,但Vert.x-Web還包含一個可以為您呈現錯誤頁面的四四方方的“漂亮”錯誤處理程序。
處理程序是ErrorHandler
。要使用錯誤處理程序,只需將其設置為您想要覆蓋的任何路徑的失敗處理程序。
請求記錄器
Vert.x-Web包含一個LoggerHandler
可用於記錄HTTP請求的處理程序。您應該在任何可能失敗的處理程序之前安裝此處理程序RoutingContext
默認情況下,請求會記錄到Vert.x記錄器,該記錄器可以配置為使用JUL日志記錄,log4j或SLF4J。
提供favicon
Vert.x-Web包含FaviconHandler
特別用於服務favicons 的處理程序。
可以使用文件系統的路徑指定Favicons,或者默認情況下,Vert.x-Web將使用名稱在類路徑中查找文件favicon.ico
。這意味着您將favicon捆綁在應用程序的jar中。
超時處理程序
Vert.x-Web包含一個超時處理程序,如果處理時間過長,您可以使用它來超時請求。
這是使用的實例配置的TimeoutHandler
。
如果請求在寫入503
響應之前超時,則響應將返回給客戶端。
下面是一個使用超時處理程序的示例,該處理程序將/foo
超過5秒后開始的所有路徑請求:
router.route("/foo/").handler(TimeoutHandler.create(5000));
響應時間處理程序
此處理程序設置標頭x-response-time
響應標頭,其中包含從接收請求到寫入響應標頭的時間(以毫秒為單位),例如:
x響應時間:1456ms
內容類型處理程序
該ResponseContentTypeHandler
可以設置Content-Type
自動報頭。假設我們正在構建一個RESTful Web應用程序。我們需要在所有處理程序中設置內容類型:
router.get("/api/books").produces("application/json").handler(rc -> findBooks(ar -> { if (ar.succeeded()) { rc.response().putHeader("Content-Type", "application/json").end(toJson(ar.result())); } else { rc.fail(ar.cause()); } }));
如果API表面變得非常大,則設置內容類型會變得很麻煩。要避免這種情況,請添加ResponseContentTypeHandler
到相應的路由:
router.route("/api/*").handler(ResponseContentTypeHandler.create()); router.get("/api/books").produces("application/json").handler(rc -> findBooks(ar -> { if (ar.succeeded()) { rc.response().end(toJson(ar.result())); } else { rc.fail(ar.cause()); } }));
處理程序從中獲取適當的內容類型getAcceptableContentType
。因此,您可以輕松共享同一個處理程序以生成不同類型的數據:
router.route("/api/*").handler(ResponseContentTypeHandler.create()); router.get("/api/books").produces("text/xml").produces("application/json").handler(rc -> findBooks(ar -> { if (ar.succeeded()) { if (rc.getAcceptableContentType().equals("text/xml")) { rc.response().end(toXML(ar.result())); } else { rc.response().end(toJson(ar.result())); } } else { rc.fail(ar.cause()); } }));
SockJS
SockJS是一個客戶端JavaScript庫和協議,它提供了一個簡單的類似WebSocket的接口,允許您連接到SockJS服務器,而不管實際的瀏覽器或網絡是否允許真正的WebSockets。
它通過支持瀏覽器和服務器之間的各種不同傳輸,並根據瀏覽器和網絡功能在運行時選擇一個來實現這一點。
所有這些對您來說都是透明的 - 您只需使用類似WebSocket的界面即可。
SockJS處理程序
Vert.x提供了一個開箱即用的處理程序SockJSHandler
,在Vert.x-Web應用程序中使用SockJS。
您應該使用每個SockJS應用程序創建一個處理程序SockJSHandler.create
。您還可以在創建實例時指定配置選項。配置選項用實例描述SockJSHandlerOptions
。
Router router = Router.router(vertx); SockJSHandlerOptions options = new SockJSHandlerOptions().setHeartbeatInterval(2000); SockJSHandler sockJSHandler = SockJSHandler.create(vertx, options); router.route("/myapp/*").handler(sockJSHandler);
處理SockJS套接字
在服務器端,您在SockJS處理程序上設置了一個處理程序,每次從客戶端建立SockJS連接時都會調用它:
傳遞給處理程序的對象是SockJSSocket
。這有一個熟悉的類似套接字的接口,你可以讀取和寫入類似於a NetSocket
或a WebSocket
。它還實現了ReadStream
, WriteStream
因此您可以將其與其他讀寫流相連。
下面是一個簡單的SockJS處理程序的示例,該處理程序只返回它讀取的任何數據:
Router router = Router.router(vertx);
SockJSHandlerOptions options = new SockJSHandlerOptions().setHeartbeatInterval(2000); SockJSHandler sockJSHandler = SockJSHandler.create(vertx, options); sockJSHandler.socketHandler(sockJSSocket -> { // Just echo the data back sockJSSocket.handler(sockJSSocket::write); }); router.route("/myapp/*").handler(sockJSHandler);
客戶端
在客戶端JavaScript中,您使用SockJS客戶端庫進行連接。
你可以在這里找到。
有關使用SockJS JavaScript客戶端的完整詳細信息,請訪問SockJS網站,但總結一下,您可以使用以下內容:
var sock = new SockJS('http://mydomain.com/myapp'); sock.onopen = function(){ 的console.log( '開放'); }; sock.onmessage = function(e){ console.log('message',e.data); }; sock.onclose = function(){ 的console.log( '關閉'); }; sock.send( '試驗'); sock.close();
配置SockJS處理程序
可以使用各種選項配置處理程序SockJSHandlerOptions
。
-
insertJSESSIONID
-
插入JSESSIONID cookie,以便負載均衡器確保對特定SockJS會話的請求始終路由到正確的服務器。默認是
true
。 -
sessionTimeout
-
close
當一段時間沒有看到接收連接的客戶端時,服務器發送事件。此延遲由此設置配置。默認情況下,在close
5秒內未看到接收連接時將發出事件。 -
heartbeatInterval
-
為了防止代理和負載均衡器關閉長時間運行的http請求,我們需要假裝連接處於活動狀態並偶爾發送心跳包。此設置控制此操作的頻率。默認情況下,每25秒發送一次心跳包。
-
maxBytesStreaming
-
大多數流傳輸在客戶端保存響應,並且不釋放傳遞的消息使用的內存。這種運輸需要偶爾進行垃圾收集。
max_bytes_streaming
設置在關閉之前可通過單個HTTP流請求發送的最小字節數。之后客戶端需要打開新請求。將此值設置為1可以有效地禁用流式傳輸,並使流式傳輸的行為類似於輪詢傳輸。默認值為128K。 -
libraryURL
-
不支持跨域通信的傳輸('eventsource'到名稱之一)使用iframe技巧。一個簡單的頁面從SockJS服務器(使用其外部域)提供,並放置在一個不可見的iframe中。從這個iframe運行的代碼不需要擔心跨域問題,因為它從域本地運行到SockJS服務器。這個iframe也需要加載SockJS javascript客戶端庫,這個選項允許你指定它的url(如果你不確定,請指向最新的縮小的SockJS客戶端版本,這是默認值)。默認值為
http://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js
-
disabledTransports
-
這是您要禁用的傳輸列表。可能的值為WEBSOCKET,EVENT_SOURCE,HTML_FILE,JSON_P,XHR。
SockJS事件總線橋
Vert.x-Web附帶一個稱為事件總線橋的內置SockJS套接字處理程序,它有效地將服務器端Vert.x事件總線擴展到客戶端JavaScript。
這將創建一個分布式事件總線,它不僅跨越服務器端的多個Vert.x實例,還包括在瀏覽器中運行的客戶端JavaScript。
因此,我們可以創建一個包含許多瀏覽器和服務器的龐大分布式總線。只要連接服務器,瀏覽器就不必連接到同一台服務器。
這是通過提供一個簡單的客戶端JavaScript庫來實現的,該庫vertx-eventbus.js
提供了一個非常類似於服務器端Vert.x事件總線API的API,它允許您向事件總線發送和發布消息並注冊處理程序以接收消息。
此JavaScript庫使用JavaScript SockJS客戶端通過終止於SockJSHandler
服務器端的SockJS連接來隧道傳輸事件總線流量。
然后在其SockJSHandler
上安裝一個特殊的SockJS套接字處理程序,它處理SockJS數據並將其與服務器端事件總線橋接。
要激活網橋,只需調用 bridge
SockJS處理程序即可。
Router router = Router.router(vertx); SockJSHandler sockJSHandler = SockJSHandler.create(vertx); BridgeOptions options = new BridgeOptions(); sockJSHandler.bridge(options); router.route("/eventbus/*").handler(sockJSHandler);
在客戶端JavaScript中,您使用'vertx-eventbus.js`庫來創建與事件總線的連接以及發送和接收消息:
<script src="http://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"></script> <script src='vertx-eventbus.js'></script> <script> var eb = new EventBus('http://localhost:8080/eventbus'); eb.onopen = function() { // set a handler to receive a message eb.registerHandler('some-address', function(error, message) { console.log('received a message: ' + JSON.stringify(message)); }); // send a message eb.send('some-address', {name: 'tim', age: 587}); } </script>
該示例的第一件事是創建事件總線的實例
var eb = new EventBus('http://localhost:8080/eventbus');
構造函數的參數是連接到事件總線的URI。由於我們使用前綴創建橋,eventbus
我們將在那里連接。
在打開連接之前,您無法對連接執行任何操作。當它打開時,onopen
將調用處理程序。
該橋支持自動重新連接,具有可配置的延遲和退避選項。
var eb = new EventBus('http://localhost:8080/eventbus'); eb.enableReconnect(true); eb.onopen = function() {}; // Set up handlers here, will be called on initial connection and all reconnections eb.onreconnect = function() {}; // Optional, will only be called on reconnections // Alternatively, pass in an options object var options = { vertxbus_reconnect_attempts_max: Infinity, // Max reconnect attempts vertxbus_reconnect_delay_min: 1000, // Initial delay (in ms) before first reconnect attempt vertxbus_reconnect_delay_max: 5000, // Max delay (in ms) between reconnect attempts vertxbus_reconnect_exponent: 2, // Exponential backoff factor vertxbus_randomization_factor: 0.5 // Randomization factor between 0 and 1 }; var eb2 = new EventBus('http://localhost:8080/eventbus', options); eb2.enableReconnect(true); // Set up handlers...
您可以使用依賴項管理器檢索客戶端庫:
-
Maven(在你的
pom.xml
):
<dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web</artifactId> <version>3.8.0</version> <classifier>client</classifier> <type>js</type> </dependency>
-
Gradle(在您的
build.gradle
文件中):
compile 'io.vertx:vertx-web:3.8.0:client'
該圖書館也可用於:
請注意,API已在3.0.0和3.1.0版本之間進行了更改。請檢查更改日志。以前的客戶端仍然兼容,仍然可以使用,但新客戶端提供更多功能,並且更接近vert.x事件總線API。
保護橋梁
如果您在沒有保護它的情況下啟動了上述示例中的橋接器,並嘗試通過它發送消息,您會發現消息神秘地消失了。他們發生了什么?
對於大多數應用程序,您可能不希望客戶端JavaScript能夠向服務器端的任何處理程序或所有其他瀏覽器發送任何消息。
例如,您可能在事件總線上有一個服務,允許訪問或刪除數據。我們不希望行為不端或惡意的客戶端能夠刪除數據庫中的所有數據!
此外,我們不一定希望任何客戶端能夠監聽任何事件總線地址。
為了解決這個問題,SockJS橋將默認拒絕通過任何消息。您可以告訴橋接器哪些消息可以通過。(對於總是允許通過的回復消息,有一個例外)。
換句話說,網橋就像一種具有默認拒絕所有策略的防火牆。
配置網橋告訴它應該通過哪些消息很容易。
您可以使用在調用bridge時傳入的內容來指定要允許入站和出站流量的 匹配項BridgeOptions
。
每個匹配都是一個PermittedOptions
對象: