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。
发送 HTTP GET 请求
发送 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; /** * 程序异常,发生时间比 HTTP 请求靠前,例如非法网址,或者 dns 不存在的 UnknownHostException */ private Exception ex; /** * HTTP 状态码 */ private Integer httpCode; /** * 响应消息字符串 */ private String responseText; /** * 结果的流 */ private InputStream in; }
有时候我们需要获取响应的 HTTP 状态码,那么读取这个
ResponseEntity.getHttpCode()
就可以了。有的时候甚至不要读取内容,例如获取重定向地址,例如 HTTP HEAD 请求(下面会讲)。配置 HTTP 请求
一般情况下,需要在请求发起之前进行诸多的配置,除了 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 所准备的函数。该类不能创建实例 * * @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"); …… }
为了额外传参数来实施控制,还有其他静态方法返回
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 是文本协议,于是一般情况下
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));
发送 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"));
发送 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()
。
发送 DELETE 请求
比较简单,暂且不提。文章来源:https://www.toymoban.com/news/detail-789456.html
原理分析
有关原理的分析,请移步至博客文章:https://blog.csdn.net/zhangxin09/article/details/86668854、https://blog.csdn.net/zhangxin09/article/details/51836563。文章来源地址https://www.toymoban.com/news/detail-789456.html
到了这里,关于轻量级 HTTP 请求组件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!