简单封装 HTTP 请求

2017-2-19 更新到第二版:

源码地址:http://git.oschina.net/sp42/ajaxjs/tree/master/ajaxjs-base/src/com/ajaxjs/net?dir=1&filepath=ajaxjs-base%2Fsrc%2Fcom%2Fajaxjs%2Fnet

和文件操作一样,其内部使用了链式风格的调用方式。

GET/HEAD 请求
GET 请求用法参见下面的测试用例,包括普通 GET 请求、获取 302 重定向调转地址、获取资源文件体积大小、是否 404 以及下载二进制文件等功能。

System.out.println(Client.GET("https://www.baidu.com/"));

// 获取资源文件体积大小
long size = Client.getFileSize("http://c.csdnimg.cn/jifen/images/xunzhang/xunzhang/bokezhuanjiamiddle.png");
assertEquals(size, 4102L);
// 获取 302 重定向跳转地址
System.out.println(Client.get302redirect("https://baidu.com"));
// 封装 head 请求检测是否 404
assertTrue(!Client.is404("http://c.csdnimg.cn/jifen/images/xunzhang/xunzhang/bokezhuanjiamiddle.png"));
// B 站强制 Gzip 返回,無論请求是否带有 GZIP 字段
System.out.println(Client.GET_Gzip("http://www.bilibili.com/video/av5178498/"));

POST 请求

String url = "http://localhost:8080/pachong/post.jsp";
String result = Client.POST(url, new HashMap<String, Object>() {
    private static final long serialVersionUID = 1L;
    {
        put("foo", "bar");
    }
});
System.out.println("Feedback:" + result);

2016-7-12 : 新增 GZip 请求和 Gzip 响应判断。

请求判断

if(request.isEnableGzip()) // 是否启动 GZip 请求
			connection.addRequestProperty("Accept-Encoding", "gzip, deflate"); 

一般情况下,请求头加入了上面的 Accept-Encoding 字段,服务器才会对内容进行 GZip 压缩,否则就不压缩,原文输出。但有些网站是不管有没有这种请求都一概返回 GZIP 的。如果有 GZIP,服务器会告诉我们的,在响应头中加入 Content-Encoding 的字段。响应判断:

// 是否启动 GZip 请求
			// 有些网站强制加入 Content-Encoding:gzip,而不管之前的是否有 GZip 的请求
			boolean isGzip = request.isEnableGzip() || "gzip".equals(connection.getHeaderField("Content-Encoding"));

如果不对 Gzip 的内容进行 GZipInputStream 处理会一段乱码。

测试用例:

@Test
public void testGZipGet() {
	Request request = new Request();
	request.setUrl("http://u.3gtv.net");
	request.setEnableGzip(true);

	RequestClient rc = new RequestClient(request);
	try {
		rc.connect();
	} catch (ConnectException e) {
		System.out.println("请求出错" + request.getUrl());
	}

	String html = request.getFeedback();
	System.out.println(html);
	assertNotNull(html);
}

// B 站强制 Gzip 返回,無論请求是否带有 GZIP
@Test
public void testForce_GZipGet() {
	String url = "http://www.bilibili.com/video/av5178498/";
	String html = Get.GET(url);
	System.out.println(html);
	assertNotNull(html);
}

-------------------------------------------------------------------------------------------------------------

简单封装 Java 类库里面的 HttpURLConnection 来完成日常的 HTTP 请求,如 GET、HEAD、POST 等的请求。GET 请求用法参见下面的测试用例,包括普通 GET 请求、获取 302 重定向调转地址、获取资源文件体积大小、是否 404 以及下载二进制文件等功能。

import static org.junit.Assert.*;
import org.junit.Test;

import java.io.IOException;

import com.ajaxjs.net.http.Get;
import com.ajaxjs.util.FileUtil;

public class TestGet {
	@Test
	public void testSimpleGET() {
		String url = "https://baidu.com";
		String html = Get.simpleGET(url);
		System.out.println(html);
		assertNotNull(html);
	}

	@Test
	public void testGet() {
		String url = "https://baidu.com";
		String html = Get.GET(url);
		System.out.println(html);
		assertNotNull(html);
	}

	@Test
	public void testGet302redirect() {
		String url = "http://baidu.com";
		String location = Get.get302redirect(url);
		System.out.println(location);
		assertNotNull(location);
	}

	@Test
	public void testIs404() {
		String url = "http://c.csdnimg.cn/jifen/images/xunzhang/xunzhang/bokezhuanjiamiddle.png";
		assertTrue(!Get.is404(url));
		assertTrue(Get.is404("http://www.qq.com/54543"));
	}

	@Test
	public void testGetFileSize() {
		String url = "http://c.csdnimg.cn/jifen/images/xunzhang/xunzhang/bokezhuanjiamiddle.png";
		long size = Get.getFileSize(url);
		assertEquals(size, 4102L);
	}
	
	@Test
	public void testDownload2disk() throws IOException {
		String url = "http://c.csdnimg.cn/jifen/images/xunzhang/xunzhang/bokezhuanjiamiddle.png";
		Get.download2disk(url, "c:/temp/dd.png");
		
		String saveTo = "c:/temp/js.js";
		Get.download2disk("http://bdimg.share.baidu.com/static/api/js/base/tangram.js?v=37768233.js", saveTo);
		assertNotNull(FileUtil.readFileAsText(saveTo));
	}

}

Get.simpleGET 和 Get.GET 用法一致,传入 url 参数,返回该网址的文本内容。具体不同可能要看看源码。simpleGET 是原始 API 版, 一句话完事的:new URL(url).openStream(),然后把字符流转为 text 即可。Get.GET 也是字符流转为 text,只是前期处理上不是调 API,为自己实现逻辑,遵循了 bean 方式的调用。这种方式在咱们这个 HTTP 库里面的是通用方式。首先 Request 类是一个 Java bean,定义了请求地址 HTTP Url、请求方法 HTTP Mehtod,还有若干常见的 HTTP 头,都做成了 getter/setter,使用者按照 Request 类的方法调用即可。其次 GET 请求和 POST 请求本身就差别不少,因此划分为 Get/Post 两个类。但实际发出请求的是 RequestClient 类。这个类是不管哪种请求的,都是围绕后期 HTTP 响应作处理,主要是流的处理,以及一些诸如 404、超时异常的处理。下面是 RequestClient 源码:

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownHostException;

import com.ajaxjs.util.FileUtil;
import com.ajaxjs.util.StringUtil;

import sun.misc.BASE64Encoder;

/**
 * 发起 HTTP 请求
 * @author frank
 *
 */
public class RequestClient {

	/**
	 * 请求目标地址
	 */
	private URL url;

	/**
	 * API 链接对象
	 */
	private HttpURLConnection connection;

	/**
	 * 携带请求信息的 Bean
	 */
	private Request request;

	/**
	 * 创建 HTTP 请求
	 * 
	 * @param request
	 *            请求信息对象
	 */
	public RequestClient(Request request) {
		this.request = request;
		try {
			url = new URL(request.getUrl());
			connection = (HttpURLConnection) url.openConnection();
			connection.setRequestMethod(request.getMethod());
		} catch (IOException e) {
			System.err.println("初始化连接出错!" + request.getUrl());
			e.printStackTrace();
		}
	}

	/**
	 * 内部初始化
	 */
	private void init() {
		connection.addRequestProperty("User-Agent", request.getUserAgent());
		connection.addRequestProperty("Referer", request.getUrl() == null ? url.getHost() : request.getUrl());
		connection.setConnectTimeout(request.getTimeout() * 1000);// 设置超时
		// connection.setReadTimeout(30000);

		if (request.getCookies() != null) {
			String cookieStr = StringUtil.HashJoin(request.getCookies(), ";");
			connection.setRequestProperty("Cookie", cookieStr);
		}

		if (request.getBasicAuthorization() != null) { // HTTP 用户认证
			String username = request.getBasicAuthorization()[0], password = request.getBasicAuthorization()[1];
			String encoding = new BASE64Encoder().encode((username + ":" + password).getBytes());
			connection.setRequestProperty("Authorization", "Basic " + encoding);
		}
	}

	/**
	 * 发起请求
	 * 
	 * @return true 表示为发起请求成功
	 * @throws ConnectException
	 */
	public boolean connect() throws ConnectException {
		init();

		// 写入数据(POST ONLY, GET 不需要)
		if (request.getWriteData() != null && !request.getMethod().equalsIgnoreCase("GET")) {
			// 写入 POST 数据
			try (OutputStream os = connection.getOutputStream()) {
				os.write(request.getWriteData());
				os.flush();
			} catch (IOException e) {
				e.printStackTrace();
				return false;
			}
		}

		// 接受响应
		try (InputStream is = connection.getInputStream();) {
			if (connection.getResponseCode() >= 400) {// 如果返回的结果是400以上,那么就说明出问题了
				ConnectException e = null;

				if (connection.getResponseCode() < 500) {
					e = new ConnectException(connection.getResponseCode() + ":客户端请求参数错误!");
				} else {
					e = new ConnectException(connection.getResponseCode() + ":抱歉!我们服务端出错了!");
				}

				String msg = FileUtil.readText(is);
				e.setFeedback(msg);

				if (request.isTextResponse())
					request.setFeedback(msg);
				throw e;
			}
			if (request.getCallback() != null) {
				request.getCallback().onDataLoad(is);
			}
			if (request.isTextResponse())
				request.setFeedback(FileUtil.readText(is));

		} catch (UnknownHostException e) {
			throw new ConnectException("未知地址!" + request.getUrl());
		} catch (FileNotFoundException e) {
			throw new ConnectException("404 地址!" + request.getUrl());
		} catch (SocketTimeoutException e) {
			throw new ConnectException("请求地址超时!" + request.getUrl());
		} catch (IOException e) {
			try {
				System.out.println(connection.getResponseCode());
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			throw new ConnectException("请求地址 IO 异常!" + request.getUrl());
		}

		return true;
	} 

	public URL getUrl() {
		return url;
	}

	public void setUrl(URL url) {
		this.url = url;
	}

	public Request getRequest() {
		return request;
	}

	public void setRequest(Request request) {
		this.request = request;
	}

	public HttpURLConnection getConnection() {
		return connection;
	}

	public void setConnection(HttpURLConnection connection) {
		this.connection = connection;
	}

}

其他源码就不张贴了,有兴趣的可以到这里看全部源码。POST 例子不多,先放一个:

String url = "http://localhost:8080/pachong/post.jsp";
Request request = Post.POST(url, new HashMap<String, Object>() {
	private static final long serialVersionUID = 1L;
	{
			put("foo", "bar");
	}
});
System.out.println("Feedback:" + request.getFeedback());
assertNotNull(request);
assertTrue(request.isDone());

代码比较简单,应该没有什么晦涩的地方。请大家多给给意见。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Qt是一个功能强大的跨平台开发框架,其中包括了丰富的网络模块,可以轻松地实现HTTP请求。对于HTTP请求封装,主要包括以下几个方面。 1. QNetworkAccessManager类 QNetworkAccessManager类是Qt网络模块的核心类之一,它负责管理所有的网络请求。具体而言,它能够发出发出http请求,接收响应数据,并负责处理中间操作例如网络重定向、HTTP认证、代理等。总的来说,使用该类可以轻松完成HTTP请求封装。 2. QNetworkRequest类 QNetworkRequest类封装了一个HTTP请求,包括URL、HTTP请求头、HTTP请求方法等。我们可以通过设置这些属性来制定HTTP请求。例如,构造一个GET请求,设置请求头: QNetworkRequest request(QUrl("http://www.baidu.com"));request.setHeader(QNetworkRequest::ContentTypeHead er, "application/x-www-form-urlencoded"); 3. QNetworkReply类 QNetworkReply类封装了一个HTTP响应,它可以提取包括HTTP响应码和响应头在内的所有与HTTP请求及响应相关的信息。可以通过这个类获取HTTP返回的结果。 总结:Qt提供了完善的网络模块,支持httphttps协议,可以轻松实现http请求封装,自定义请求头,处理响应信息等。本质上就是通过一个网络管理器,发出请求,获取响应,进而完成http请求封装。因此,使用Qt实现http请求是一种简洁有效的方案。 ### 回答2: Qt是一款跨平台的C++开发框架,它提供了用于网络通信的相关组件,其中也包括了HTTP请求封装类。在Qt中,可以使用QNetworkAccessManager类来封装HTTP请求,这个类简化了HTTP请求的发送和接收,并提供了一些有用的事件和回调函数。 使用QNetworkAccessManager类发送HTTP请求时,需要先创建一个QNetworkRequest对象,指定要发送的URL地址和请求方法。可以在请求中添加自定义的HTTP请求头和请求体。接着,使用QNetworkAccessManager的post()、get()、put()等方法发送请求。当响应结果返回时,可以在接收的数据中解析出HTTP响应头和响应体,QNetworkAccessManager也提供了相关的信号和槽函数。 QNetworkAccessManager使用异步请求方式,即发送请求后会立即返回,等待从远端接收响应结果。如果需要同步请求,可以使用QNetworkReply类的waitForFinished()方法,但不建议在主线程中使用。 在Qt中,也提供了第三方的HTTP库,例如QHttpEngine和Qhttplib,可以更简便地实现HTTP请求。但使用Qt自带的QNetworkAccessManager类是更加轻量和简单的方式,可以避免额外依赖,并且具有更好的可移植性和跨平台性。 ### 回答3: Qt是一种跨平台的C++应用程序框架,提供了许多丰富的库,其中包括Qt网络库(QtNetwork),可轻松地进行HTTP请求。Qt封装了许多网络协议,例如TCP、UDP、HTTP、FTP等,使得开发者可以轻松地进行网络操作。 Qt提供了QNetworkAccessManager类,用于处理网络请求。它支持GET、POST、PUT、DELETE等HTTP请求方式。可以通过QNetworkRequest类设置请求头和请求参数。QNetworkAccessManager在请求完成后会发出信号,开发者可以捕获信号并进行逻辑处理。 除了QNetworkAccessManager,Qt还提供了QHttp类,用于发送和接收HTTP请求和响应。QHttp类是基于底层网络套接字的,需要开发者自行处理协议和数据传输。但是,在Qt5中,已不推荐使用QHttp类,而是推荐使用QNetworkAccessManager。 总之,Qt提供了一种简便的方式,使得开发者可以轻松地进行HTTP请求。无论是使用QNetworkAccessManager还是QHttp类,都可以轻松地进行网络请求并处理响应。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sp42a

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值