Apache HttpClient 是著名的 HTTP 客戶端請求工具——現在我們模擬它打造一套簡單小巧的請求工具庫, 封裝 Java 類庫里面的 HttpURLConnection 對象來完成日常的 HTTP 請求,諸如 GET、HEAD、POST 等等,并嘗試應用 Java 8 函數式風格來制定 API。
<dependency> <groupid>com.ajaxjs</groupid> <artifactid>ajaxjs-net</artifactid> <version>1.0.2</version> </dependency>
組件源碼在:https://gitee.com/sp42_admin/ajaxjs/tree/master/aj-backend/aj-framework/aj-net。
發(fā)送 HTTP GET 請求
發(fā)送 HTTP GET 請求如下代碼所示。
// 請求百度網站,返回網站的 HTML 內容 String html = Get.get("https://www.baidu.com").toString();
框架約定,所有 HTTP 請求返回特定的結果:ResponseEntity,包含了關于請求相關的設置和響應內容。典型地
toString()
返回響應內容的文本格式。ResponseEntity 是一個 Java Bean,字段如下。public class ResponseEntity { /** * 返回響應文本結果 */ @Override public String toString() { return ResponseHandler.stream2Str(this).getResponseText(); } /** * 連接對象 */ private HttpURLConnection connection; /** * 請求地址 */ private String url; /** * 請求方法 */ private String httpMethod; /** * 請求參數 */ private Map params; /** * 是否成功(http 200 即表示成功,4xx/500x 表示不成功) */ private boolean isOk; /** * 程序異常,發(fā)生時間比 HTTP 請求靠前,例如非法網址,或者 dns 不存在的 UnknownHostException */ private Exception ex; /** * HTTP 狀態(tài)碼 */ private Integer httpCode; /** * 響應消息字符串 */ private String responseText; /** * 結果的流 */ private InputStream in; }
有時候我們需要獲取響應的 HTTP 狀態(tài)碼,那么讀取這個
ResponseEntity.getHttpCode()
就可以了。有的時候甚至不要讀取內容,例如獲取重定向地址,例如 HTTP HEAD 請求(下面會講)。配置 HTTP 請求
一般情況下,需要在請求發(fā)起之前進行諸多的配置,除了 HTTP 方法、URL、參數這些之外,請求框架還應該提供對 HTTP Header 的配置。在 Java 中實際是圍繞 HttpURLConnection 對象進行配置。怎么才可以靈活地配置呢?此處我們引入函數接口
Consumer<HttpURLConnection>
,即可添加頭字段。重載的get()
方法如下。/** * GET 請求,返回文本內容 * * @param url 請求目標地址 * @param fn 自定義 HTTP 頭的時候可設置,可選的 * @return 響應的內容 */ public static ResponseEntity get(String url, Consumer fn); // 例子 ResponseEntity result = Get.get("http://abc.com", conn -> { conn.setRequestProperty("Content-Type", "application/json"); conn.setRequestProperty("Authorization", "Bearer " + admin.getAccessToken()); }); 框架提供常用的配置的 lambda,如指定 From POST 等等,參見 SetConnection 類。 /** * 為初始化 HTTP Connection 所準備的函數。該類不能創(chuàng)建實例 * * @author Frank Cheung * */ public abstract class SetConnection { /** * 設置 POST 方式 */ public final static Consumer SET_FORM_POST = conn -> conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded;charset=utf-8"); /** * 設置響應 JSON */ public final static Consumer SET_JSON = conn -> conn.setRequestProperty("Content-type", "application/json"); /** * 設置啟動 GZip 請求 */ public final static Consumer SET_GIZPREQUEST = conn -> conn.addRequestProperty("Accept-Encoding", "gzip, deflate"); …… }
為了額外傳參數來實施控制,還有其他靜態(tài)方法返回
Consumer<HttpURLConnection>
。實際這些是Consumer<HttpURLConnection>
的“高階函數”。例如:/** * 設置超時 (單位:秒) */ public final static Consumer setTimeout(int timeout) { return conn -> conn.setConnectTimeout(timeout * 1000); } /** * 請求來源 */ public final static Consumer setReferer(String url) { return conn -> conn.addRequestProperty("Referer", url); // httpUrl.getHost()? } /** * 設置 cookies */ public final static Consumer setCookies(Map""> map) { return conn -> conn.addRequestProperty("Cookie", MapTool.join(map, ";")); }
只要都是
Consumer<HttpURLConnection>
類型則可以鏈式調用,如SET_FORM_POST.andThen(SET_JSON)
。GZip
多數 HTTP 資源允許 GZip 壓縮,那樣傳輸效率更高。這時請求頭須帶上 GZip 打開的標識才可以,也就是說請求頭加入了上面的
Accept-Encoding
字段,一般情況下服務器才會對內容進行 GZip 壓縮,否則就不壓縮,原文輸出。 但有些網站是不管有沒有這種請求都一概返回 GZip 的。如果有 GZip,服務器會在響應頭中加入Content-Encoding
的字段告訴我們的。處理響應結果
HTTP 是文本協(xié)議,于是一般情況下
toString()
即可返回響應的文本內容。 針對常見 Restful API 返回 JSON,框架提供api()
的方法返回 JSON。apiXml()
則返回 XML 內容。無論結果 JSON 還是 XML 都使用Map<String, Object>
作為容器。/** * GET API,返回 JSON * * @param url 請求目標地址 * @param fn 自定義 HTTP 頭的時候可設置,可選的 * @return 響應的 JSON,Map 格式 */ public static Map<String, Object> api(String url, Consumer fn); public static Map<String, Object> api(String url); public static Map<String, String> apiXML(String url, Consumer fn); public static Map<String, String> apiXML(String url);
如果 API 返回 JSON 數組則返回
List<Map<String, String>>
,請使用:public static List<Map<String, Object>> apiList(String url, Consumer fn); public static List<Map<String, Object>> apiList(String url);
文件下載
HTTP 響應內容在底層處理中被視為輸入流 InputStream。文本操作只是轉換流的形式之一 我們還可以把流轉換為文件,即“遠程文件下載到本地磁盤”的功能。ResponseHandler 是這么一個處理 InputStream 的類。
/ * 下載二進制文件 * @param url 請求目標地址 * @param saveDir 保存的目錄 * @param newFileName 是否有新的文件名,如無請傳 null * @return 下載文件的完整磁盤路徑 */ public static String download(String url, String saveDir, String newFileName); // 用法 String url = "https://bbsimage.res.meizu.com/forum/2019/01/23/153122zrz85kuvubbiibbs.jpg"; assertNotNull(Get.download(url, "c:/temp", null));
發(fā)送 HTTP HEAD 請求
HEAD 方法也是 HTTP 標準方法之一。像獲取資源體積大小、獲取 302 重定向調轉地址、資源是否 404 那樣的請求,著實無須 GET 方法,使用 HEAD 即可。各種 HEAD 請求的用法已封裝,參見下面的測試用例。
// 獲取資源文件體積大小 long size = Head.getFileSize("http://c.yssmx.com/jifen/images/xunzhang/xunzhang/bokezhuanjiamiddle.png"); assertEquals(size, 4102L); // 獲取 302 重定向跳轉地址 System.out.println(Head.get302redirect("https://baidu.com")); // 請求檢測是否 404 assertTrue(!Head.is404("http://c.yssmx.com/jifen/images/xunzhang/xunzhang/bokezhuanjiamiddle.png"));
發(fā)送 HTTP POST/PUT 請求
關于 POST 的配置和用法大體和 GET 相似,比較不同的是一般要指定 POST 出去的數據,即 POST 參數,可以為String
、Map<String, Object>
、byte[]
類型。
// POST Map 參數
String result = Post.post("http://localhost:8080/post.jsp", new HashMap<>() {
private static final long serialVersionUID = 1L;
{
put("foo", "bar");
}
});
// POST 字符串參數
result = Post.post("http://localhost:8080/post", "a=1&b=2&c=3");
// api
Post.api(...);
Post.apiXML(...);
PUT 方法為Post.put()/Post.putApi()
。
發(fā)送 DELETE 請求
比較簡單,暫且不提。文章來源:http://www.zghlxwxcb.cn/news/detail-789456.html
原理分析
有關原理的分析,請移步至博客文章:https://blog.csdn.net/zhangxin09/article/details/86668854、https://blog.csdn.net/zhangxin09/article/details/51836563。文章來源地址http://www.zghlxwxcb.cn/news/detail-789456.html
到了這里,關于輕量級 HTTP 請求組件的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!