231419585
在第一篇文章中開發了一個很是簡單的Vert.x 3應用程序,還包括怎麼測試、打包和執行。在第二篇文章中對端口進行了可變配置。html
這篇文章中,開發一個CRUD(增刪改查)應用,發佈一個HTML頁面,經過REST API與後臺進行交互。RESTfull形式的API不簡單,這篇文章中就不涉及了。java
接下來,能看到:git
這篇文章開發的代碼放在GitHub上,是從第二篇文章的代碼基礎上進行的。github
若是你看了前面的文章,使用Vert.x Core來處理複雜的HTTP應用仍是很麻煩的,因此就有了Vert.x Web,它可使Vert.x開發一個web應用更加簡單,並且不會改變Vert.x的思想。web
更新pom.xml文件,添加下面的依賴:數據庫
<dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web</artifactId> <version>3.0.0</version> </dependency>
這就是使用Vert.x Web的惟一前提。json
還記得在上一篇文章中,當請求http://localhost:8080
時,返回一個Hello World消息,使用Vert.x Web完成一樣的事情,打開name.quanke.study.vertx.first.MyFirstVerticle.java
類,修改start
方法:後端
@Override public void start(Future<Void> fut) { // Create a router object. Router router = Router.router(vertx); // Bind "/" to our hello message - so we are still compatible. router.route("/").handler(routingContext -> { HttpServerResponse response = routingContext.response(); response .putHeader("content-type", "text/html") .end("<h1>Hello from my first Vert.x 3 application</h1>"); }); // Create the HTTP server and pass the "accept" method to the request handler. vertx .createHttpServer() .requestHandler(router::accept) .listen( // Retrieve the port from the configuration, // default to 8080. config().getInteger("http.port", 8080), result -> { if (result.succeeded()) { fut.complete(); } else { fut.fail(result.cause()); } } ); }
在開始start方法裏建立了一個Router
對象。router是Vert.x Web的基礎,負責分發HTTP請求到handler(處理器),在Vert.x Web中還有兩個很重要的概念。api
若是明白了這3個概念(Router、Routes、Handlers),就明白了Vert.x Web的全部了。數組
仔細看看下面這段代碼:
router.route("/").handler(routingContext -> { HttpServerResponse response = routingContext.response(); response .putHeader("content-type", "text/html") .end("<h1>Hello from my first Vert.x 3 application</h1>"); });
將訪問"/"(http://localhost:8080/
)的請求「路由」到指定的handler。Handlers接收RoutingContext對象。這個handler的方法和咱們以前的代碼很像,他們操做的是同一個HttpServerResponse類型的對象。
讓咱們來看看剩下的代碼:
vertx .createHttpServer() .requestHandler(router::accept) .listen( // Retrieve the port from the configuration, // default to 8080. config().getInteger("http.port", 8080), result -> { if (result.succeeded()) { fut.complete(); } else { fut.fail(result.cause()); } } ); }
除了改變了request handler,基本和以前的代碼同樣。傳router::accept
給handler。你可能對這個符號不太熟悉。它表示引用一個方法(這裏是引用router
的accept
方法)。換句話說,當接收到一個請求的時候,告訴vert.x從router
裏調用accept
方法。
讓咱們來看下它是怎麼工做的:
mvn clean package java -jar target/my-first-app-1.0-SNAPSHOT-fat.jar
瀏覽器打開http://localhost:8080
,你會看到Hello的消息。
如今有了第一個使用Vert.x Web開發的應用。先在寫增長一個index.html頁面(靜態資源)。
這個HTML頁面將會是咱們應用的入口。在src/main/resources/assets目錄下,index.html文件在github上。此文不涉及這個文件的細節。
基本上,就是一個簡單的CRUD
的UI界面,actions
是由經過AJAX
調用的REST API
執行的。
建立完了頁面後,編輯name.quanke.study.vertx.first.MyFirstVerticle類,並修改start方法:
@Override public void start(Future<Void> fut) { Router router = Router.router(vertx); router.route("/").handler(routingContext -> { HttpServerResponse response = routingContext.response(); response .putHeader("content-type", "text/html") .end("<h1>Hello from my first Vert.x 3 application</h1>"); }); // Serve static resources from the /assets directory // 將訪問「/assets/*」的請求route到「assets」目錄下的資源 router.route("/assets/*").handler(StaticHandler.create("assets")); vertx .createHttpServer() .requestHandler(router::accept) .listen( // Retrieve the port from the configuration, // default to 8080. config().getInteger("http.port", 8080), result -> { if (result.succeeded()) { fut.complete(); } else { fut.fail(result.cause()); } } ); }
就這段代碼和前面的不一樣:
router.route("/assets/*").handler(StaticHandler.create("assets"));
這一行是什麼意思?挺簡單的。將訪問「/assets/*」的請求route到「assets」目錄下的資源。如今能夠經過http://localhost:8080/assets/index.html
來訪問index.html了。
測試以前,咱們花一些時間來看一下handler的建立。全部的處理請求動做在Vert.x Web裏都實現成handler。而建立一個handler須要調用create
方法。
編譯、運行:
mvn clean package java -jar target/my-first-app-1.0-SNAPSHOT-fat.jar
瀏覽器,輸入http://localhost:8080/assets/index.html
。
如今這個table是空的。那是由於咱們尚未實現REST的API。如今讓咱們來開始吧。
Vert.x Web實現REST API很簡單。看下面:
在實現REST API以前,須要建立Whisky的數據模型。使用下面的內容建立src/main/java/quanke/name/study/vertx/first/Whisky.java
:
package name.quanke.study.vertx.first; import java.util.concurrent.atomic.AtomicInteger; public class Whisky { private static final AtomicInteger COUNTER = new AtomicInteger(); private final int id; private String name; private String origin; public Whisky(String name, String origin) { this.id = COUNTER.getAndIncrement(); this.name = name; this.origin = origin; } public Whisky() { this.id = COUNTER.getAndIncrement(); } public String getName() { return name; } public String getOrigin() { return origin; } public int getId() { return id; } public void setName(String name) { this.name = name; } public void setOrigin(String origin) { this.origin = origin; } }
這是一個很簡單的bean類。由於Vert.x依賴Jackson來處理JSON格式,Jackson可以自動序列化和反序列化bean類,讓代碼變得更簡單,因此選擇這樣的格式。
如今,建立幾瓶威士忌。在MyFirstVerticle類中,添加下面的代碼:
// Store our product // 存儲產品 private Map<Integer, Whisky> products = new LinkedHashMap<>(); // Create some product // 建立一些產品 private void createSomeData() { Whisky bowmore = new Whisky("Bowmore 15 Years Laimrig", "Scotland, Islay"); products.put(bowmore.getId(), bowmore); Whisky talisker = new Whisky("Talisker 57° North", "Scotland, Island"); products.put(talisker.getId(), talisker); }
而後,在start
方法裏,調用createSomeData
方法:
@Override public void start(Future<Void> fut) { createSomeData(); // Create a router object. Router router = Router.router(vertx); // Rest of the method }
在這裏並無一個後臺數據庫。僅使用一個map,將數據存儲在內存中。添加後端數據庫的介紹我準備放在另外一篇文章中講。
GET /api/whiskies
,JSON數組中返回產品列表。
在start
方法裏,添加下面這行(static handler):
router.get("/api/whiskies").handler(this::getAll);
告訴router
調用getAll
方法來處理"/api/whiskies"
的GET請求。代碼能夠寫在handler
裏,可是爲了讓代碼更加清晰,另外建立一個方法:
private void getAll(RoutingContext routingContext) { routingContext.response() .putHeader("content-type", "application/json; charset=utf-8") .end(Json.encodePrettily(products.values())); }
每個handler(好比:請看上面的代碼)都會接受一個RoutingContext
參數。經過設置content-type
和一些內容來填充response
。由於內容可能會碰到特殊的字符,因此強制使用UTF-8的格式。建立內容的時候,並不須要本身去處理JSON格式的字符串。Vert.x有處理Json的API。使用Json.encodePrettily(products.values())處理JSON字符串。本應使用Json.encodePrettily(products),可是爲了讓JavaScript代碼更簡單,咱們僅返回威士忌(產品)的數據集合,並無返回包含Id=>Bottle
的鍵值對。
打包運行:
mvn clean package java -jar target/my-first-app-1.0-SNAPSHOT-fat.jar
瀏覽器訪問http://localhost:8080/assets/index.html
,而後你將會看到下面這個頁面。
很好奇,想看一下REST API到底返回了什麼。打開瀏覽器,訪問http://localhost:8080/api/whiskies。你會看到下面這樣的信息:
[ { "id" : 0, "name" : "Bowmore 15 Years Laimrig", "origin" : "Scotland, Islay" }, { "id" : 1, "name" : "Talisker 57° North", "origin" : "Scotland, Island" } ]
能獲取到威士忌(產品)了,如今須要建立一個產品。不像以前的REST API,這一次,須要讀取request
的body
。由於性能的緣由,它應該被顯式地啓用。不要怕,這也僅僅是一個handler
而已。
在start
方法中,添加下面的內容到getAll
的後面:
router.route("/api/whiskies*").handler(BodyHandler.create()); router.post("/api/whiskies").handler(this::addOne);
第一行容許"/api/whiskies"
下的全部route
讀取請求的body
。經過使用router.route().handler(BodyHandler.create())
,能讓它在全局生效。
第二行將對/api/whiskies
的POST請求映射到addOne
方法。讓咱們來建立這個方法:
private void addOne(RoutingContext routingContext) { final Whisky whisky = Json.decodeValue(routingContext.getBodyAsString(), Whisky.class); products.put(whisky.getId(), whisky); routingContext.response() .setStatusCode(201) .putHeader("content-type", "application/json; charset=utf-8") .end(Json.encodePrettily(whisky)); }
開始從請求的body
中取出Whisky
對象。只是將body讀成一個字符串並將它傳入到Json.decodeValue方法裏。Whisky這個對象一旦建立好,將被添加到後臺的map中,並以JSON的格式返回。
從新編譯而且運行:
mvn clean package java -jar target/my-first-app-1.0-SNAPSHOT-fat.jar
刷新HTML頁面,點擊Add a new bottle
按鈕。輸入數據,如:「quanke」
做爲名字, 「quanke.name」 做爲產地 ,就OK了。
狀態碼 201 ? CREATED
和在REST API中建立一個entity時,response的狀態碼爲201。。默認的vert.x web設置一個200的狀態碼錶明OK。
在start
方法裏,添加:
router.delete("/api/whiskies/:id").handler(this::deleteOne);
在URL
裏,參數爲::id
。在處理一個相匹配的請求的時候,Vert.x提取路徑中與這個參數對應的一段,可以在handler中得到。例如,/api/whiskies/0
將id
映射爲0
。
看一下在handler
方法中這個參數是怎樣被使用的。建立一個deleteOne
方法。
private void deleteOne(RoutingContext routingContext) { String id = routingContext.request().getParam("id"); if (id == null) { routingContext.response().setStatusCode(400).end(); } else { Integer idAsInteger = Integer.valueOf(id); products.remove(idAsInteger); } routingContext.response().setStatusCode(204).end(); }
狀態碼 204 ? 狀態碼爲204 - NO CONTENT。HTTP delete動做一般都是無返回內容的。
實現getOne和updateOne很簡單,和上面的差很少,此文再也不詳細介紹。源碼在github上
此文介紹瞭如何用Vert.x web輕鬆的實現一個REST API,如何訪問靜態資源。比之前的文章複雜些,但仍然仍是很簡單。