客户端http协议传输类库。HttpClient被用来发送和接受Http消息。HttpClient不会处理Http消息的内容,不会进行Javascript解析,不会关心ContentType,如果没有明确设置,HttpClient也不会对请求进行格式化、重定向url,或者其他任何和http消息传输相关的功能。
HttpClient核心接口
org.apache.http.HttpMessage
:HTTP 消息由客户端到服务器的请求和服务器到客户端的响应组成。
org.apache.http.HttpRequest
:从客户端到服务器的请求消息在该消息的第一行中包括要应用于资源的方法、资源的标识符和正在使用的协议版本。
org.apache.http.client.HttpClient
:这个接口只代表了 HTTP 请求执行的最基本的合约。它对请求执行过程没有施加任何限制或特定细节,并将状态管理、身份验证和重定向处理的细节留给单独的实现。
org.apache.http.HttpResponse
:在接收并解释请求消息后,服务器以 HTTP 响应消息进行响应。
核心依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
HttpClient入参
实体入参的请求需要继承HttpEntityEnclosingRequestBase
,官方HttpDelete/HttpPatch/HttpPost/HttpPut均支持实体入参
public abstract class HttpEntityEnclosingRequestBase
extends HttpRequestBase implements HttpEntityEnclosingRequest {
private HttpEntity entity;
public HttpEntityEnclosingRequestBase() {
super();
}
@Override
public HttpEntity getEntity() {
return this.entity;
}
@Override
public void setEntity(final HttpEntity entity) {
this.entity = entity;
}
@Override
public boolean expectContinue() {
final Header expect = getFirstHeader(HTTP.EXPECT_DIRECTIVE);
return expect != null && HTTP.EXPECT_CONTINUE.equalsIgnoreCase(expect.getValue());
}
@Override
public Object clone() throws CloneNotSupportedException {
final HttpEntityEnclosingRequestBase clone =
(HttpEntityEnclosingRequestBase) super.clone();
if (this.entity != null) {
clone.entity = CloneUtils.cloneObject(this.entity);
}
return clone;
}
}
无需实体入参的请求可以直接继承HttpRequestBase
,官方HttpGet/HttpHead/HttpOptions/HttpTrace均不支持实体入参,实在有需要可以自行重写,直接继承HttpEntityEnclosingRequestBase
即可
public abstract class HttpRequestBase extends AbstractExecutionAwareRequest
implements HttpUriRequest, Configurable {
private ProtocolVersion version;
private URI uri;
private RequestConfig config;
@Override
public abstract String getMethod();
/**
* @since 4.3
*/
public void setProtocolVersion(final ProtocolVersion version) {
this.version = version;
}
@Override
public ProtocolVersion getProtocolVersion() {
return version != null ? version : HttpProtocolParams.getVersion(getParams());
}
@Override
public URI getURI() {
return this.uri;
}
@Override
public RequestLine getRequestLine() {
final String method = getMethod();
final ProtocolVersion ver = getProtocolVersion();
final URI uriCopy = getURI(); // avoids possible window where URI could be changed
String uritext = null;
if (uriCopy != null) {
uritext = uriCopy.toASCIIString();
}
if (uritext == null || uritext.isEmpty()) {
uritext = "/";
}
return new BasicRequestLine(method, uritext, ver);
}
@Override
public RequestConfig getConfig() {
return config;
}
public void setConfig(final RequestConfig config) {
this.config = config;
}
public void setURI(final URI uri) {
this.uri = uri;
}
public void started() {
}
public void releaseConnection() {
reset();
}
@Override
public String toString() {
return getMethod() + " " + getURI() + " " + getProtocolVersion();
}
}
HttpEntity:HTTP 消息发送或接收的实体,HttpEntityEnclosingRequestBase
中的属性
public interface HttpEntity {
//实体是否能够多次生成其数据。可重复实体的 getContent() 和 writeTo(OutputStream) 方法可以多次调用,而不可重复实体则不能。
boolean isRepeatable();
//实体的分块编码。HTTP1.0不支持分块。此方法的主要目的是指示在发送实体时是否应使用分块编码。对于接收到的实体,它还可以指示是否使用分块编码接收到实体。
boolean isChunked();
//内容的长度
long getContentLength();
//获取 Content-Type 标头。这是发送实体时应使用的标头,或者与实体一起接收的标头。它可以包含一个字符集属性。
Header getContentType();
//获取 Content-Encoding 标头。这是发送实体时应使用的标头,或者与实体一起接收的标头。修改内容编码的包装实体应相应地调整此标头。
Header getContentEncoding();
//实体的内容流。可Repeatable实体应为每次调用此方法创建一个新的InputStream实例,因此可以多次使用。不可repeatable的实体应返回相同的InputStream实例,因此不得多次使用。
InputStream getContent() throws IOException, UnsupportedOperationException;
//实体内容写入输出流。重要提示:请注意,所有实体实现必须确保在此方法返回时正确释放所有分配的资源。
void writeTo(OutputStream outStream) throws IOException;
//实体是否依赖于基础流。直接从套接字读取数据的流式实体应返回true 。自包含实体应返回false 。包装实体应将此调用委托给被包装实体。
boolean isStreaming(); // don't expect an exception here
//自 4.1 版起已弃用。调用该方法表示不再需要该实体的内容。由于此方法调用,所有实体实现都应释放所有分配的资源。
@Deprecated
void consumeContent() throws IOException;
}
AbstractHttpEntity:实体的抽象基类。为HttpEntity的流式和自包含实现提供常用属性。
public abstract class AbstractHttpEntity implements HttpEntity {
protected static final int OUTPUT_BUFFER_SIZE = 4096;
protected Header contentType;
protected Header contentEncoding;
protected boolean chunked;
protected AbstractHttpEntity() {
super();
}
@Override
public Header getContentType() {
return this.contentType;
}
@Override
public Header getContentEncoding() {
return this.contentEncoding;
}
@Override
public boolean isChunked() {
return this.chunked;
}
public void setContentType(final Header contentType) {
this.contentType = contentType;
}
public void setContentType(final String ctString) {
Header h = null;
if (ctString != null) {
h = new BasicHeader(HTTP.CONTENT_TYPE, ctString);
}
setContentType(h);
}
public void setContentEncoding(final Header contentEncoding) {
this.contentEncoding = contentEncoding;
}
public void setContentEncoding(final String ceString) {
Header h = null;
if (ceString != null) {
h = new BasicHeader(HTTP.CONTENT_ENCODING, ceString);
}
setContentEncoding(h);
}
public void setChunked(final boolean b) {
this.chunked = b;
}
@Override
@Deprecated
public void consumeContent() throws IOException {
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append('[');
if (contentType != null) {
sb.append("Content-Type: ");
sb.append(contentType.getValue());
sb.append(',');
}
if (contentEncoding != null) {
sb.append("Content-Encoding: ");
sb.append(contentEncoding.getValue());
sb.append(',');
}
final long len = getContentLength();
if (len >= 0) {
sb.append("Content-Length: ");
sb.append(len);
sb.append(',');
}
sb.append("Chunked: ");
sb.append(chunked);
sb.append(']');
return sb.toString();
}
}
AbstractHttpEntity下常用实体
BasicHttpEntity
:从InputStream获取其内容的通用流式、不可重复实体
ByteArrayEntity
:一个自包含的、可重复的实体,它从字节数组中获取其内容。
EntityTemplate
:将内容生成过程委托给ContentProducer的实体。
FileEntity
:从文件中获取其内容的自包含、可重复的实体。
InputStreamEntity
:从InputStream获取其内容的流式、不可重复的实体。
SerializableEntity
:从Serializable获取其内容的流式实体。从Serializable实例获得的内容可以选择缓冲在字节数组中,以使实体自包含和可重复。
StringEntity
:一个自包含、可重复的实体,从String获取其内容。
UrlEncodedFormEntity
:由 url 编码对列表组成的实体。这在发送 HTTP POST 请求时通常很有用。
HttpEntityWrapper:用于包装实体的基类。保留一个wrappedEntity并将所有调用委托给它。包装实体的实现可以从此类派生,并且只需要覆盖那些不应委托给包装实体的方法。
public class HttpEntityWrapper implements HttpEntity {
protected HttpEntity wrappedEntity;
public HttpEntityWrapper(final HttpEntity wrappedEntity) {
super();
this.wrappedEntity = Args.notNull(wrappedEntity, "Wrapped entity");
}
@Override
public boolean isRepeatable() {
return wrappedEntity.isRepeatable();
}
@Override
public boolean isChunked() {
return wrappedEntity.isChunked();
}
@Override
public long getContentLength() {
return wrappedEntity.getContentLength();
}
@Override
public Header getContentType() {
return wrappedEntity.getContentType();
}
@Override
public Header getContentEncoding() {
return wrappedEntity.getContentEncoding();
}
@Override
public InputStream getContent()
throws IOException {
return wrappedEntity.getContent();
}
@Override
public void writeTo(final OutputStream outStream)
throws IOException {
wrappedEntity.writeTo(outStream);
}
@Override
public boolean isStreaming() {
return wrappedEntity.isStreaming();
}
@Override
@Deprecated
public void consumeContent() throws IOException {
wrappedEntity.consumeContent();
}
}
HttpEntityWrapper下常用实体
BufferedHttpEntity
:必要时缓冲其内容的包装实体。缓冲实体始终是可重复的。如果被包装的实体本身是可重复的,则调用被传递。如果包装的实体不可重复,则将内容一次读入缓冲区并根据需要从那里提供。
DecompressingEntity
:用于解压HttpEntity实现的通用基类,其子类有支持Deflate的DeflateDecompressingEntity
和支持GZip的GzipDecompressingEntity
GzipCompressingEntity
:writing时压缩内容的包装实体。
ResponseEntityProxy
:包含在响应消息中的HttpEntity的包装器类。
HttpClient发起请求
HTTP 请求执行的基本类HttpClient
目前最常用的实现类的是CloseableHttpClient
,以前的DefaultHttpClient
在自4.3版本之后作废了。
@Contract(threading = ThreadingBehavior.SAFE)
public abstract class CloseableHttpClient implements HttpClient, Closeable {
private final Log log = LogFactory.getLog(getClass());
protected abstract CloseableHttpResponse doExecute(HttpHost target, HttpRequest request,
HttpContext context) throws IOException, ClientProtocolException;
//使用默认上下文执行 HTTP 请求。
@Override
public CloseableHttpResponse execute(
final HttpHost target,
final HttpRequest request,
final HttpContext context) throws IOException, ClientProtocolException {
return doExecute(target, request, context);
}
@Override
public CloseableHttpResponse execute(
final HttpUriRequest request,
final HttpContext context) throws IOException, ClientProtocolException {
Args.notNull(request, "HTTP request");
return doExecute(determineTarget(request), request, context);
}
private static HttpHost determineTarget(final HttpUriRequest request) throws ClientProtocolException {
HttpHost target = null;
final URI requestURI = request.getURI();
if (requestURI.isAbsolute()) {
target = URIUtils.extractHost(requestURI);
if (target == null) {
throw new ClientProtocolException("URI does not specify a valid host name: "
+ requestURI);
}
}
return target;
}
@Override
public CloseableHttpResponse execute(
final HttpUriRequest request) throws IOException, ClientProtocolException {
return execute(request, (HttpContext) null);
}
@Override
public CloseableHttpResponse execute(
final HttpHost target,
final HttpRequest request) throws IOException, ClientProtocolException {
return doExecute(target, request, null);
}
@Override
public <T> T execute(final HttpUriRequest request,
final ResponseHandler<? extends T> responseHandler) throws IOException,
ClientProtocolException {
return execute(request, responseHandler, null);
}
@Override
public <T> T execute(final HttpUriRequest request,
final ResponseHandler<? extends T> responseHandler, final HttpContext context)
throws IOException, ClientProtocolException {
final HttpHost target = determineTarget(request);
return execute(target, request, responseHandler, context);
}
@Override
public <T> T execute(final HttpHost target, final HttpRequest request,
final ResponseHandler<? extends T> responseHandler) throws IOException,
ClientProtocolException {
return execute(target, request, responseHandler, null);
}
@Override
public <T> T execute(final HttpHost target, final HttpRequest request,
final ResponseHandler<? extends T> responseHandler, final HttpContext context)
throws IOException, ClientProtocolException {
Args.notNull(responseHandler, "Response handler");
final CloseableHttpResponse response = execute(target, request, context);
try {
final T result = responseHandler.handleResponse(response);
final HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
return result;
} catch (final ClientProtocolException t) {
// Try to salvage the underlying connection in case of a protocol exception
final HttpEntity entity = response.getEntity();
try {
EntityUtils.consume(entity);
} catch (final Exception t2) {
// Log this exception. The original exception is more
// important and will be thrown to the caller.
this.log.warn("Error consuming content after an exception.", t2);
}
throw t;
} finally {
response.close();
}
}
}
target(HttpHost)
:请求的目标主机。如果实现仍然可以确定路由,例如到默认目标或通过检查请求,则可以接受null 。
request
:要执行的请求,支持HttpRequest
和HttpUriRequest
responseHandler(ResponseHandler)
:响应处理程序
context(HttpContext)
:用于执行的上下文,或null使用默认上下文
HttpClients
是CloseableHttpClient
实例的工厂方法。
public class HttpClients {
private HttpClients() {
super();
}
//创建构建器对象以构建自定义CloseableHttpClient实例。
public static HttpClientBuilder custom() {
return HttpClientBuilder.create();
}
//使用默认配置创建CloseableHttpClient实例。
public static CloseableHttpClient createDefault() {
return HttpClientBuilder.create().build();
}
//基于系统属性创建具有默认配置的CloseableHttpClient实例。
public static CloseableHttpClient createSystem() {
return HttpClientBuilder.create().useSystemProperties().build();
}
//创建实现最基本 HTTP 协议支持的CloseableHttpClient实例。
public static CloseableHttpClient createMinimal() {
return new MinimalHttpClient(new PoolingHttpClientConnectionManager());
}
//创建实现最基本 HTTP 协议支持的CloseableHttpClient实例。
public static CloseableHttpClient createMinimal(final HttpClientConnectionManager connManager) {
return new MinimalHttpClient(connManager);
}
}
HttpClient响应
目前最常用的是CloseableHttpResponse
public interface CloseableHttpResponse extends HttpResponse, Closeable {
}
HttpResponse
public interface HttpResponse extends HttpMessage {
//获取此响应的状态行。可以使用setStatusLine方法之一设置状态行,也可以在构造函数中对其进行初始化。
StatusLine getStatusLine();
//设置此响应的状态行。
void setStatusLine(StatusLine statusline);
//设置此响应的状态行。原因短语将根据当前locale确定。
void setStatusLine(ProtocolVersion ver, int code);
void setStatusLine(ProtocolVersion ver, int code, String reason);
//使用新的状态代码更新此响应的状态行。
void setStatusCode(int code)
throws IllegalStateException;
//使用新的原因短语更新此响应的状态行。
void setReasonPhrase(String reason)
throws IllegalStateException;
//获取此响应的消息实体(如果有)。该实体是通过调用setEntity来提供的。
HttpEntity getEntity();
//将响应实体与此响应相关联。
void setEntity(HttpEntity entity);
//更改此响应的语言环境。
void setLocale(Locale loc);
}
HttpClient进行POST请求例子:
private static String sendPOST(String url) throws IOException {
String result = "";
HttpPost post = new HttpPost(url);
List<NameValuePair> urlParameters = new ArrayList<>();
urlParameters.add(new BasicNameValuePair("username", "abc"));
urlParameters.add(new BasicNameValuePair("password", "123"));
post.setEntity(new UrlEncodedFormEntity(urlParameters));
try (CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = httpClient.execute(post)){
result = EntityUtils.toString(response.getEntity());
}
return result;
}
HttpURLConnection相关博客推荐
上传文件:https://lsqingfeng.blog.csdn.net/article/details/90611686
下载文件:https://blog.csdn.net/WxQ92222/article/details/79896489
调用链路源码分析可以参考:
https://blog.csdn.net/u012504392/article/details/109432686