Vert.x傳送 HTTP/HTTPS請求及重定向
應用場景
在應用系統中,經常會有類似於獲取天氣、傳送簡訊、處理影象、支付等需求,這些需求實現都非常複雜,或者受到監管的限制,不是任何一個公司都可以做到的。但有些應用為了提升使用者的體驗,需要用到這些功能,比如餓了麼會根據你所在的位置推薦附近的商家,線上商城需要線上支付,還有一些應用需要進行人臉識別等等。
Web客戶端應用場景
有需求就會有市場,於是就有很多的公司單獨對外提供某種服務,比如支付寶就對外提供支付的功能,百度地圖對外提供定位等功能。這些功能一般都是通過HTTP服務的方式對外提供的,那麼我們如果想要在應用中使用,就需要呼叫相關的服務,也就需要傳送HTTP請求,服務提供者處理我們的請求並響應給我們,我們接收到響應並處理響應結果,Vert.x對此提供了非常方便的API供我們使用。
服務端模擬
先來建立一個服務端,對外提供Web服務,也就是模擬第三方公司的發簡訊服務,支付服務等。Web服務有兩種協議,一種是HTTP,另外一種是使用ssl的HTTPS,請求的方式有五種,分別是get、post、put、delete、head。為了簡單,服務端主要實現對HTTP協議的get和post的請求處理。程式碼非常簡單,如下
1 @Override 2 public void start() throws Exception { 3 4 HttpServer server = vertx.createHttpServer(); 5 Router router = Router.router(vertx);6 7 // 處理get請求 8 router.get("/get").handler(request -> { 9 String username = request.request().getParam("username"); 10 String password = request.request().getParam("password"); 11 12 System.out.println(username + " " + password); 13 14 request.response().end("get request success");15 }); 16 17 // 處理post請求 18 router.post("/post").handler(request -> { 19 request.request().bodyHandler(body->{ 20 System.out.println(body.toJsonObject().toString()); 21 JsonObject responseData = new JsonObject() 22 .put("msg","success"); 23 request.response().end(responseData.toString()); 24 }); 25 }); 26 27 server.requestHandler(router::accept); 28 server.listen(80); 29 30 }
上面的程式碼就是建立了一個Web服務,監聽/get和/post地址,分別處理get請求和post請求。如果對上面的程式碼不理解的朋友可以參考《建立HTTP服務》 和 《Web開發 - 路由》
傳送HTTP GET請求
我們平時訪問網頁,最多的就是GET請求,但在應用中使用GET方式請求遠端服務一般不多,因為GET請求的引數都是附帶到URL後面的,對於一些敏感資訊非常容易洩露。
GET請求比較簡單,這裡以GET請求方式入門,後面的POST請求方式和使用GET方式類似,傳送一個GET請求的程式碼如下:
1 @Override 2 public void start() throws Exception { 3 // 建立WebClient,用於傳送HTTP或者HTTPS請求 4 WebClient webClient = WebClient.create(vertx); 5 // 以get方式請求遠端地址 6 webClient.getAbs("http://localhost/get").send(handle -> { 7 // 處理響應的結果 8 if (handle.succeeded()) { 9 // 這裡拿到的結果就是一個HTML文字,直接打印出來 10 System.out.println(handle.result().bodyAsString()); 11 } 12 }); 13 }
這是最簡單的一種請求方式,在上面的程式碼中,首先建立了WebClient物件,通過WebClient可以方便的傳送HTTP請求。有朋友可能會注意到,在Vert.x提供的核心包中有HttpClient,可以通過Vert.x例項獲取
HttpClient httpClient = vertx.createHttpClient();
HTTPClient這個介面也可以傳送HTTP請求,只是它比較低階,如果在請求中帶複雜資料,附帶請求頭,封裝響應結果都需要自己來處理。因此,Vert.x提供了vertx-web-client的支援。
WebClient的API非常簡單,傳送GET請求,可以使用getAbs。Abs是絕對地址的意思,除了使用絕對地址,還可以使用域名+埠號+請求地址的方式,程式碼如下:
1 webClient.get(80, "localhost", "/get").send(handle -> { 2 // 處理響應的結果 3 if (handle.succeeded()) { 4 // 這裡拿到的結果就是一個HTML文字,直接打印出來 5 System.out.println(handle.result().bodyAsString()); 6 } 7 });
第一個引數是埠號,第二個引數是域名,第三個引數是請求地址。
傳送HTTP請求 請求頭
HTTP協議包含三部分,請求行、請求頭和請求體。一般來講,不建議在GET請求的請求體中帶資料,但請求頭資料是每個請求都有的,比如使用User-Agent指定裝置的型別以及作業系統等等。我們在請求遠端服務的時候,如何設定請求頭呢,非常簡單,程式碼如下
1 webClient.get(80, "localhost", "/get") 2 .putHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0") 3 .send(handle -> { 4 // 處理響應的結果 5 if (handle.succeeded()) { 6 // 這裡拿到的結果就是一個HTML文字,直接打印出來 7 System.out.println(handle.result().bodyAsString()); 8 } 9 });
設定超時時間
HTTP請求是一個耗時操作,受到網路等因素的影響,很多時候我們要限制一個最大的請求時間,超過這個時間就不去處理了,可以通過WebClientOptions設定
@Override public void start() throws Exception { // 建立WebClient,用於傳送HTTP或者HTTPS請求 WebClientOptions webClientOptions = new WebClientOptions() .setConnectTimeout(500); // ms WebClient webClient = WebClient.create(vertx, webClientOptions); // 以get方式請求遠端地址 webClient.getAbs("http://localhost/get").send(handle -> { // 處理響應的結果 if (handle.succeeded()) { // 這裡拿到的結果就是一個HTML文字,直接打印出來 System.out.println(handle.result().bodyAsString()); } }); }
除了設定超時時間以外,還可以設定是否重定向到頁面以及HTTPS的證書等等。
傳送HTTP GET請求 帶資料
很多的情況下在請求服務端的時候都需要帶一些必要的資料,GET請求帶資料有兩種方式,第一種就是在請求的URL後直接拼接引數 ?username=xxx&password=xxx的形式。還有一種方式是通過addQueryParam的方式新增請求引數,如下所示
1 @Override 2 public void start() throws Exception { 3 // 建立WebClient,用於傳送HTTP或者HTTPS請求 4 WebClient webClient = WebClient.create(vertx); 5 // 以get方式請求遠端地址 6 webClient 7 .getAbs("http://localhost/get") 8 // 通過這種方式傳送資料 9 .addQueryParam("username", "admin") 10 .addQueryParam("password", "admin123") 11 .send(handle -> { 12 // 處理響應的結果 13 if (handle.succeeded()) { 14 // 這裡拿到的結果就是一個HTML文字,直接打印出來 15 System.out.println(handle.result().bodyAsString()); 16 } 17 }); 18 }
上面的程式碼就是向服務端傳送兩個引數,一個是username一個是password。這兩個引數在網路上都是通過明文進行傳輸的,因此如果真的是要做登陸,一定不要使用get方式。這裡我們並不需要去思考服務端如何接收這兩個引數,客戶端只要能夠保證引數能夠傳送出去就可以了。
傳送POST請求
GET方式在瀏覽器中使用的還是比較多,但在服務呼叫中一般比較少,在服務呼叫中一般會選擇使用POST方式,下面看POST方式請求遠端服務的案例,程式碼如下
1 @Override 2 public void start() throws Exception { 3 // 建立WebClient,用於傳送HTTP或者HTTPS請求 4 WebClient webClient = WebClient.create(vertx); 5 // 以get方式請求遠端地址 6 webClient.postAbs("http://localhost/post") 7 .send(handle -> { 8 // 處理響應的結果 9 if (handle.succeeded()) { 10 // 這裡拿到的結果就是一個HTML文字,直接打印出來 11 System.out.println(handle.result().bodyAsString()); 12 } 13 }); 14 }
POST方式和GET方式請求在API呼叫上非常類似,上面的程式碼中,只是呼叫了postAbs方法而已,底層通訊協議是如何封裝的開發者完全不用關注,getAbs和postAbs在底層處理方式是完全不同的。
傳送POST請求 帶資料
當然了,一般進行服務呼叫的時候也都需要附帶一些資料,比如我們要呼叫發簡訊服務,那麼就至少需要附帶手機號和簡訊內容過去當然還需要一些認證資訊,服務提供方傳送簡訊成功之後需要告訴我們已經受理成功,或者僅僅是收到我們的請求。在POST中帶資料的方式比較多,資料型別也多樣化,主要有以下三種類型
1. form表單
2. json字串
3. xml字串
第一種資料型別是前後端互動時經常使用的方式,第二、三種方式是服務呼叫常用的序列化方式。下面以傳送JSON字串的形式來呼叫遠端服務,程式碼如下:
1 @Override 2 public void start() throws Exception { 3 // 建立WebClient,用於傳送HTTP或者HTTPS請求 4 WebClient webClient = WebClient.create(vertx); 5 6 // 構造請求的資料 7 JsonObject data = new JsonObject() 8 .put("username", "admin") 9 .put("password", "admin123"); 10 11 // 以get方式請求遠端地址 12 webClient.postAbs("http://localhost/post") 13 .sendJsonObject(data, handle -> { 14 // 處理響應的結果 15 if (handle.succeeded()) { 16 // 這裡拿到的結果就是一個HTML文字,直接打印出來 17 // handle.result().bodyAsJsonObject() 可以解析Json資料 18 System.out.println(handle.result().bodyAsString()); 19 } 20 }); 21 }
在上面的程式碼中,我們先建立了一個JsonObject物件,和GSON和fastjson中的JsonObject類似,但感覺Vert.x提供的JsonObject更為強大一些,通過JsonObject可以非常方便的構造一個Json字串。當然除了JsonObject以外,Vert.x還提供了JsonArray可以構造一個json陣列。
構造一個json物件以後,在請求遠端服務的時候可以通過sendJsonObject方法,將構造好的json物件傳入,Vert.x底層就會給我們把json物件格式化為json字串,傳送給服務提供者。服務提供者就會收到如下請求資料:
{ "username": "admin", "password": "admin123" }
服務端收到這些資料也可以使用JsonObject物件將字串轉為json物件,方便的讀寫資料。當然了,服務端收到我們的資料之後進行處理,最後還需要給客戶端響應資料,服務端也可以給客戶端響應json字串,客戶端同樣可以對json字串進行處理。
這種案例非常多,可以參考微信支付掃碼介面文件
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
請求HTTPS服務
使用HTTP協議,不管是get請求還是post請求,資料傳輸都是明文傳輸的,因此敏感資訊通過HTTP方式都有暴露的危險。現在越來越多的網站都已經升級為HTTPS協議,通過加密的方式進行傳輸,保證敏感資訊的安全。
HTTP請求安全簡單來講有兩點,第一個是資料被篡改,第二個是敏感資料被竊取。解決資料篡改的手段是對訊息進行簽名,解決資料竊取的手段是加密,HTTPS實際上就是為了解決敏感資訊被竊取的問題的。
對於HTTPS的原理這裡不做過多的介紹,感興趣的朋友可以在網路上搜索相關文章,在Vert.x中請求HTTPS也非常簡單,程式碼如下:
1 @Override 2 public void start() throws Exception { 3 // 建立WebClient,用於傳送HTTP或者HTTPS請求 4 WebClient webClient = WebClient.create(vertx); 5 // 以get方式請求遠端地址 6 webClient.getAbs("https://www.sina.com") 7 .ssl(true) 8 .send(handle -> { 9 // 處理響應的結果 10 if (handle.succeeded()) { 11 // 這裡拿到的結果就是一個HTML文字,直接打印出來 12 System.out.println(handle.result().bodyAsString()); 13 } 14 }); 15 }
上面的程式碼是我要請求新浪的主頁,可以看到,只需要在呼叫send方法之前,呼叫ssl方法,並指定為true即可。
重定向
1 context.response().putHeader("location", "/").setStatusCode(302).end()