Browse Source

http请求的开放API

remotes/1611766341912730171/master
richie 5 years ago
parent
commit
485f2f9d60
  1. 6
      pom.xml
  2. 677
      src/main/java/com/fanruan/api/net/http/HttpKit.java
  3. 54
      src/main/java/com/fanruan/api/net/http/rs/BaseHttpResponseHandle.java
  4. 215
      src/main/java/com/fanruan/api/net/http/rs/HttpRequest.java
  5. 114
      src/main/java/com/fanruan/api/net/http/rs/HttpRequestType.java
  6. 103
      src/main/java/com/fanruan/api/net/http/rs/HttpResponseType.java
  7. 55
      src/main/java/com/fanruan/api/net/http/rs/StreamResponseHandle.java
  8. 39
      src/main/java/com/fanruan/api/net/http/rs/TextResponseHandle.java
  9. 48
      src/main/java/com/fanruan/api/net/http/rs/UploadResponseHandle.java
  10. 153
      src/test/java/com/fanruan/api/net/http/HttpKitTest.java

6
pom.xml

@ -121,6 +121,12 @@
<version>1.7.1</version> <version>1.7.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>4.0.1</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
<finalName>${project.artifactId}-${version}-${maven.build.timestamp}</finalName> <finalName>${project.artifactId}-${version}-${maven.build.timestamp}</finalName>

677
src/main/java/com/fanruan/api/net/http/HttpKit.java

@ -0,0 +1,677 @@
package com.fanruan.api.net.http;
import com.fanruan.api.consts.EncodeConstantsKit;
import com.fanruan.api.log.LogKit;
import com.fanruan.api.net.http.rs.BaseHttpResponseHandle;
import com.fanruan.api.net.http.rs.HttpRequest;
import com.fanruan.api.net.http.rs.HttpRequestType;
import com.fanruan.api.net.http.rs.HttpResponseType;
import com.fanruan.api.net.http.rs.StreamResponseHandle;
import com.fanruan.api.net.http.rs.TextResponseHandle;
import com.fanruan.api.net.http.rs.UploadResponseHandle;
import com.fr.third.guava.collect.Maps;
import com.fr.third.org.apache.http.HttpEntity;
import com.fr.third.org.apache.http.HttpEntityEnclosingRequest;
import com.fr.third.org.apache.http.HttpHost;
import com.fr.third.org.apache.http.NameValuePair;
import com.fr.third.org.apache.http.NoHttpResponseException;
import com.fr.third.org.apache.http.client.HttpRequestRetryHandler;
import com.fr.third.org.apache.http.client.config.RequestConfig;
import com.fr.third.org.apache.http.client.entity.UrlEncodedFormEntity;
import com.fr.third.org.apache.http.client.methods.CloseableHttpResponse;
import com.fr.third.org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import com.fr.third.org.apache.http.client.methods.HttpRequestBase;
import com.fr.third.org.apache.http.client.protocol.HttpClientContext;
import com.fr.third.org.apache.http.client.utils.URIBuilder;
import com.fr.third.org.apache.http.config.Registry;
import com.fr.third.org.apache.http.config.RegistryBuilder;
import com.fr.third.org.apache.http.conn.routing.HttpRoute;
import com.fr.third.org.apache.http.conn.socket.ConnectionSocketFactory;
import com.fr.third.org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import com.fr.third.org.apache.http.conn.socket.PlainConnectionSocketFactory;
import com.fr.third.org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import com.fr.third.org.apache.http.entity.FileEntity;
import com.fr.third.org.apache.http.entity.mime.HttpMultipartMode;
import com.fr.third.org.apache.http.entity.mime.MultipartEntityBuilder;
import com.fr.third.org.apache.http.impl.client.CloseableHttpClient;
import com.fr.third.org.apache.http.impl.client.HttpClients;
import com.fr.third.org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import com.fr.third.org.apache.http.message.BasicNameValuePair;
import com.fr.third.org.apache.http.protocol.HttpContext;
import com.fr.third.org.apache.http.ssl.SSLContexts;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.fanruan.api.net.http.rs.HttpRequestType.POST;
/**
* @author richie
* @version 10.0
* Created by richie on 2019-08-29
* <p>
* http请求工具类封装了用于http请求的各种方法
* </p>
*/
public class HttpKit {
private static final int RETRY_TIMES = 5;
private static CloseableHttpClient httpClient = null;
private final static Object SYNC_LOCK = new Object();
/**
* 根据请求地址创建HttpClient对象
*
* @param url 请求地址
* @return HttpClient对象
*/
public static CloseableHttpClient getHttpClient(String url) {
String hostname = url.split("/")[2];
int port = 80;
if (hostname.contains(":")) {
String[] arr = hostname.split(":");
hostname = arr[0];
port = Integer.parseInt(arr[1]);
}
if (httpClient == null) {
synchronized (SYNC_LOCK) {
if (httpClient == null) {
httpClient = createHttpClient(hostname, port, SSLContexts.createDefault());
}
}
}
return httpClient;
}
public static CloseableHttpClient createHttpClient(String hostname, int port, SSLContext sslContext) {
return createHttpClient(200, 40, 100, hostname, port, sslContext);
}
private static CloseableHttpClient createHttpClient(int maxTotal,
int maxPerRoute,
int maxRoute,
String hostname,
int port,
SSLContext sslContext) {
ConnectionSocketFactory socketFactory = PlainConnectionSocketFactory.getSocketFactory();
LayeredConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext);
Registry<ConnectionSocketFactory> registry = RegistryBuilder
.<ConnectionSocketFactory>create()
.register("http", socketFactory)
.register("https", sslConnectionSocketFactory)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
// 将最大连接数增加
cm.setMaxTotal(maxTotal);
// 将每个路由基础的连接增加
cm.setDefaultMaxPerRoute(maxPerRoute);
HttpHost httpHost = new HttpHost(hostname, port);
// 将目标主机的最大连接数增加
cm.setMaxPerRoute(new HttpRoute(httpHost), maxRoute);
// 请求重试处理
HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() {
@Override
public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
if (executionCount >= RETRY_TIMES) {// 如果已经重试了5次,就放弃
return false;
}
if (exception instanceof NoHttpResponseException) {// 如果服务器丢掉了连接,那么就重试
return true;
}
if (exception instanceof SSLHandshakeException) {// 不要重试SSL握手异常
return false;
}
if (exception instanceof InterruptedIOException) {// 超时
return false;
}
if (exception instanceof UnknownHostException) {// 目标服务器不可达
return false;
}
if (exception instanceof SSLException) {// SSL握手异常
return false;
}
HttpClientContext clientContext = HttpClientContext.adapt(context);
com.fr.third.org.apache.http.HttpRequest request = clientContext.getRequest();
// 如果请求是幂等的,就再次尝试
return !(request instanceof HttpEntityEnclosingRequest);
}
};
return HttpClients.custom()
.setConnectionManager(cm)
.setRetryHandler(httpRequestRetryHandler)
.build();
}
/**
* 设置 httpEntity
*
* @param requestBase 请求体
* @param httpRequest 请求
*/
private static void setHttpEntity(@NotNull HttpEntityEnclosingRequestBase requestBase, @NotNull HttpRequest httpRequest) {
HttpEntity httpEntity = httpRequest.getHttpEntity();
if (httpEntity != null) {
// 如果存在 httpEntity 直接设置
requestBase.setEntity(httpEntity);
return;
}
Map<String, String> params = httpRequest.getParams();
if (params == null || params.isEmpty()) {
return;
}
List<NameValuePair> pairs = new ArrayList<NameValuePair>();
for (Map.Entry<String, String> entry : params.entrySet()) {
pairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
try {
requestBase.setEntity(new UrlEncodedFormEntity(pairs, httpRequest.getEncoding()));
} catch (UnsupportedEncodingException e) {
LogKit.error(e.getMessage(), e);
}
}
private static <V> Map<String, String> transformMap(Map<String, V> oldMap) {
if (oldMap == null) {
return null;
}
return Maps.transformEntries(oldMap, new Maps.EntryTransformer<String, V, String>() {
@Override
public String transformEntry(@Nullable String key, @Nullable V value) {
return value == null ? null : value.toString();
}
});
}
/**
* 发起POST请求并获取返回的文本
*
* @param url 响应请求的的服务器地址
* @param params POST请求的参数
* @return 服务器返回的文本内容
*/
public static <V> String post(String url, Map<String, V> params) throws IOException {
return executeAndParse(HttpRequest
.custom()
.url(url)
.post(transformMap(params))
.build());
}
/**
* 发起POST请求并获取返回的文本
*
* @param url 响应请求的的服务器地址
* @param params POST请求的参数
* @param responseType 返回类型
* @return 服务器返回的文本内容
* @see com.fanruan.api.net.http.HttpKit#execute(HttpRequest)
*/
@Deprecated
public static <T, V> T post(String url, Map<String, V> params, HttpResponseType<T> responseType) throws IOException {
CloseableHttpResponse response = execute(HttpRequest
.custom()
.url(url)
.post(transformMap(params))
.build());
return responseType.result(response, null);
}
/**
* 发起POST请求并获取返回的文本
*
* @param url 响应请求的的服务器地址
* @param params POST请求的参数
* @param headers 请求头
* @return 服务器返回的文本内容
* @see com.fanruan.api.net.http.HttpKit#executeAndParse(HttpRequest)
*/
@Deprecated
public static <V> String post(String url, Map<String, V> params, Map<String, String> headers) throws IOException {
return executeAndParse(HttpRequest
.custom()
.url(url)
.post(transformMap(params))
.headers(headers)
.build());
}
/**
* 发起POST请求并获取返回的文本
*
* @param url 响应请求的的服务器地址
* @param params POST请求的参数
* @param headers 请求头
* @param responseType 返回类型
* @return 服务器返回的文本内容
* @see com.fanruan.api.net.http.HttpKit#execute(HttpRequest)
*/
@Deprecated
public static <T, V> T post(String url, Map<String, V> params, Map<String, String> headers, HttpResponseType<T> responseType) throws IOException {
CloseableHttpResponse response = execute(HttpRequest
.custom()
.url(url)
.post(transformMap(params))
.headers(headers)
.build());
return responseType.result(response, null);
}
/**
* 发起POST请求并获取返回的文本
*
* @param url 响应请求的的服务器地址
* @param params POST请求的参数
* @param responseEncoding 响应的文本的编码
* @return 服务器返回的文本内容
* @see com.fanruan.api.net.http.HttpKit#executeAndParse(HttpRequest, BaseHttpResponseHandle)
*/
@Deprecated
public static <V> String post(String url, Map<String, V> params, String responseEncoding) throws IOException {
return executeAndParse(HttpRequest
.custom()
.url(url)
.post(transformMap(params))
.build(),
new TextResponseHandle(responseEncoding));
}
/**
* 发起POST请求并获取返回的文本
*
* @param url 响应请求的的服务器地址
* @param params POST请求的参数
* @param responseEncoding 响应的文本的编码
* @param headers 请求头
* @return 服务器返回的文本内容
* @see com.fanruan.api.net.http.HttpKit#executeAndParse(HttpRequest, BaseHttpResponseHandle)
*/
@Deprecated
public static <V> String post(String url, Map<String, V> params, String responseEncoding, Map<String, String> headers) throws IOException {
return executeAndParse(HttpRequest
.custom()
.url(url)
.post(transformMap(params))
.headers(headers)
.build(),
new TextResponseHandle(responseEncoding));
}
/**
* 发起POST请求并获取返回的文本
*
* @param url 响应请求的的服务器地址
* @param params POST请求的参数
* @param responseEncoding 响应的文本的编码
* @param paramsEncoding 参数编码
* @param headers 请求头
* @return 服务器返回的文本内容
* @see com.fanruan.api.net.http.HttpKit#executeAndParse(HttpRequest, BaseHttpResponseHandle)
*/
public static <V> String post(String url, Map<String, V> params, String responseEncoding, String paramsEncoding, Map<String, String> headers) throws IOException {
return executeAndParse(HttpRequest
.custom()
.url(url)
.post(transformMap(params))
.encoding(paramsEncoding)
.headers(headers)
.build(),
new TextResponseHandle(responseEncoding));
}
/**
* 发起POST请求并获取返回的文本
*
* @param url 响应请求的的服务器地址
* @param params POST请求的参数
* @param responseEncoding 响应的文本的编码
* @param paramsEncoding 参数编码
* @param headers 请求头
* @param responseType 返回值类型
* @return 服务器返回的文本内容
* @see com.fanruan.api.net.http.HttpKit#execute(HttpRequest)
*/
public static <T, V> T post(String url, Map<String, V> params, String responseEncoding, String paramsEncoding, Map<String, String> headers, HttpResponseType<T> responseType) throws IOException {
CloseableHttpResponse response = execute(HttpRequest
.custom()
.url(url)
.post(transformMap(params))
.encoding(paramsEncoding)
.headers(headers)
.build());
return responseType.result(response, responseEncoding);
}
/**
* 发起GET请求并获取返回的文本
*
* @param url 响应请求的的服务器地址
* @return 服务器返回的文本内容
*/
public static String get(String url) throws IOException {
return executeAndParse(HttpRequest.custom().url(url).build());
}
/**
* 发起GET请求并获取返回的文本
*
* @param url 响应请求的的服务器地址
* @param params 参数
* @return 服务器返回的文本内容
*/
public static String get(String url, Map<String, String> params) throws IOException {
return executeAndParse(HttpRequest.custom().url(url).params(params).build());
}
/**
* 发起GET请求并获取返回的文本
*
* @param url 响应请求的的服务器地址
* @param params 参数
* @param headers 请求头
* @return 服务器返回的文本内容
* @see com.fanruan.api.net.http.HttpKit#executeAndParse(HttpRequest, BaseHttpResponseHandle)
*/
public static String get(String url, Map<String, String> params, Map<String, String> headers) throws IOException {
return executeAndParse(HttpRequest.custom().url(url).params(params).headers(headers).build());
}
/**
* 发起GET请求并获取返回的文本
*
* @param url 响应请求的的服务器地址
* @param params 参数
* @param responseEncoding 返回的文本的编码
* @return 服务器返回的文本内容
* @see com.fanruan.api.net.http.HttpKit#executeAndParse(HttpRequest, BaseHttpResponseHandle)
*/
public static String get(String url, Map<String, String> params, String responseEncoding) throws IOException {
return executeAndParse(HttpRequest
.custom()
.url(url)
.params(params)
.build(),
new TextResponseHandle(responseEncoding));
}
/**
* 发起GET请求并获取返回的文本
*
* @param url 响应请求的的服务器地址
* @param params 参数
* @param responseEncoding 返回的文本的编码
* @return 服务器返回的文本内容
* @see com.fanruan.api.net.http.HttpKit#executeAndParse(HttpRequest, BaseHttpResponseHandle)
*/
public static String get(String url, Map<String, String> params, String responseEncoding, Map<String, String> headers) throws IOException {
return executeAndParse(HttpRequest
.custom()
.url(url)
.params(params)
.headers(headers)
.build(),
new TextResponseHandle(responseEncoding));
}
/**
* 从指定的地址下载文件
* @param url 文件下载地址
* @return 文件的字节流
* @throws IOException 下载过程中出现错误则抛出此异常
*/
public static ByteArrayInputStream download(String url) throws IOException {
return executeAndParse(HttpRequest.custom().url(url).build(), StreamResponseHandle.DEFAULT);
}
/**
* 从指定的地址下载文件
* @param url 文件下载地址
* @param params 参数对
* @param responseEncoding 响应的文件编码
* @param headers 请求头
* @return 文件的字节流
* @throws IOException 下载过程中出现错误则抛出此异常
*/
public static ByteArrayInputStream download(String url, Map<String, String> params, String responseEncoding, Map<String, String> headers) throws IOException {
return executeAndParse(HttpRequest
.custom()
.url(url)
.params(params)
.headers(headers)
.build(),
new StreamResponseHandle(responseEncoding));
}
/**
* 上传文件到指定的服务器
*
* @param url 接收文件的服务器地址
* @param file 要上传的文件默认的文件编码为utf-8
* @throws IOException 上传中出现错误则抛出此异常
*/
public static void upload(String url, File file) throws IOException {
upload(url, file, Charset.forName("utf-8"));
}
/**
* 上传文件到指定的服务器
*
* @param url 接收文件的服务器地址
* @param file 要上传的文件
* @param charset 文件的编码
* @throws IOException 上传中出现错误则抛出此异常
*/
public static void upload(String url, File file, Charset charset) throws IOException {
upload(url, new FileEntity(file), charset);
}
/**
* 上传文件到指定的服务器
*
* @param url 接收文件的服务器地址
* @param builder 附件构造器
* @param charset 文件的编码
* @throws IOException 上传中出现错误则抛出此异常
*/
public static void upload(String url, MultipartEntityBuilder builder, Charset charset) throws IOException {
upload(url, builder, charset, Collections.<String, String>emptyMap(), POST);
}
/**
* 上传文件到指定的服务器
*
* @param url 接收文件的服务器地址
* @param fileEntity 文件实体
* @param charset 文件的编码
* @throws IOException 上传中出现错误则抛出此异常
*/
public static void upload(String url, FileEntity fileEntity, Charset charset) throws IOException {
upload(url, fileEntity, charset, Collections.<String, String>emptyMap(), POST);
}
/**
* 上传多文件到指定的服务器
*
* @param url 接收文件的服务器地址
* @param builder 附件构造器
* @param charset 文件的编码
* @param headers 请求头
* @param httpRequestType 请求类型
* @throws IOException 上传中出现错误则抛出此异常
*/
public static void upload(String url, MultipartEntityBuilder builder, Charset charset, Map<String, String> headers, HttpRequestType httpRequestType) throws IOException {
// richie:采用浏览器模式,防止出现乱码
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
HttpEntity reqEntity = builder.setCharset(charset).build();
upload(url, reqEntity, charset, headers, httpRequestType);
}
/**
* 上传文件到指定的服务器
*
* @param url 接收文件的服务器地址
* @param reqEntity 请求实体
* @param charset 文件的编码
* @param headers 请求头
* @param httpRequestType 请求类型
* @throws IOException 上传中出现错误则抛出此异常
*/
public static void upload(String url, HttpEntity reqEntity, Charset charset, Map<String, String> headers, HttpRequestType httpRequestType) throws IOException {
executeAndParse(HttpRequest
.custom()
.url(url)
.headers(headers)
.method(httpRequestType)
.httpEntity(reqEntity)
.encoding(charset.toString())
.build(),
UploadResponseHandle.DEFAULT);
}
/**
* 请求资源或服务使用默认文本http解析器UTF-8编码
*
* @param httpRequest httpRequest
* @return 返回处理结果
*/
public static String executeAndParse(HttpRequest httpRequest) throws IOException {
return executeAndParse(httpRequest, TextResponseHandle.DEFAULT);
}
/**
* 请求资源或服务自请求参数并指定 http 响应处理器
*
* <pre>
* String res = HttpToolbox.executeAndParse(HttpRequest
* .custom()
* .url("")
* .build(),
* TextResponseHandle.DEFAULT);
* </pre>
*
* @param httpRequest httpRequest
* @param handle http 解析器
* @return 返回处理结果
*/
public static <T> T executeAndParse(HttpRequest httpRequest, BaseHttpResponseHandle<T> handle) throws IOException {
return handle.parse(execute(httpRequest));
}
/**
* 请求资源或服务传入请求参数
*
* @param httpRequest httpRequest
* @return 返回处理结果
*/
public static CloseableHttpResponse execute(HttpRequest httpRequest) throws IOException {
return execute(getHttpClient(httpRequest.getUrl()), httpRequest);
}
/**
* 请求资源或服务自定义client对象传入请求参数
*
* @param httpClient http客户端
* @param httpRequest httpRequest
* @return 返回处理结果
*/
public static CloseableHttpResponse execute(CloseableHttpClient httpClient, HttpRequest httpRequest) throws IOException {
String url = httpRequest.getUrl();
// 创建请求对象
HttpRequestBase httpRequestBase = httpRequest.getMethod().createHttpRequest(url);
// 设置header信息
httpRequestBase.setHeader("User-Agent", "Mozilla/5.0");
Map<String, String> headers = httpRequest.getHeaders();
if (headers != null && !headers.isEmpty()) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
httpRequestBase.setHeader(entry.getKey(), entry.getValue());
}
}
// 配置请求的设置
RequestConfig requestConfig = httpRequest.getConfig();
if (requestConfig != null) {
httpRequestBase.setConfig(requestConfig);
}
// 判断是否支持设置entity(仅HttpPost、HttpPut、HttpPatch支持)
if (HttpEntityEnclosingRequestBase.class.isAssignableFrom(httpRequestBase.getClass())) {
setHttpEntity((HttpEntityEnclosingRequestBase) httpRequestBase, httpRequest);
} else {
Map<String, String> params = httpRequest.getParams();
if (params != null && !params.isEmpty()) {
// 注意get等不支持设置entity需要更新拼接之后的URL,但是url变量没有更新
httpRequestBase.setURI(URI.create(buildUrl(url, params, httpRequest.getEncoding())));
}
}
return httpClient.execute(httpRequestBase);
}
/**
* 构建 Url
*
* @param url 请求地址
* @param params 参数
* @return 拼接之后的地址
*/
public static String buildUrl(String url, Map<String, String> params) {
try {
return buildUrl(url, params, EncodeConstantsKit.ENCODING_UTF_8);
} catch (UnsupportedEncodingException ignore) {
}
return url;
}
/**
* 构建 Url
*
* @param url 请求地址
* @param params 参数
* @return 拼接之后的地址
* @throws UnsupportedEncodingException 不支持的编码
*/
private static String buildUrl(String url, Map<String, String> params, String paramsEncoding) throws UnsupportedEncodingException {
if (params == null || params.isEmpty()) {
return url;
}
URIBuilder builder;
try {
builder = new URIBuilder(url);
for (Map.Entry<String, String> entry : params.entrySet()) {
String key = URLEncoder.encode(entry.getKey(), paramsEncoding);
String value = URLEncoder.encode(entry.getValue(), paramsEncoding);
builder.setParameter(key, value);
}
return builder.build().toString();
} catch (URISyntaxException e) {
LogKit.debug("Error to build url, please check the arguments.");
}
return url;
}
}

54
src/main/java/com/fanruan/api/net/http/rs/BaseHttpResponseHandle.java

@ -0,0 +1,54 @@
package com.fanruan.api.net.http.rs;
import com.fr.stable.EncodeConstants;
import com.fr.third.org.apache.http.client.methods.CloseableHttpResponse;
import java.io.IOException;
/**
* http 结果解析器
*
* @author vito
* @date 2019-07-14
*/
public abstract class BaseHttpResponseHandle<T> {
/**
* 解析编码默认为 UTF_8
*/
private String encoding = EncodeConstants.ENCODING_UTF_8;
public BaseHttpResponseHandle() {
}
public BaseHttpResponseHandle(String encoding) {
this.encoding = encoding;
}
/**
* 获取解析编码
*
* @return 解析编码
*/
public String getEncoding() {
return encoding;
}
/**
* 设置解析编码
*
* @param encoding 解析编码
*/
public void setEncoding(String encoding) {
this.encoding = encoding;
}
/**
* 解析响应结果
*
* @param response 响应
* @return 解析结果
* @throws IOException io异常
*/
public abstract T parse(CloseableHttpResponse response) throws IOException;
}

215
src/main/java/com/fanruan/api/net/http/rs/HttpRequest.java

@ -0,0 +1,215 @@
package com.fanruan.api.net.http.rs;
import com.fanruan.api.consts.EncodeConstantsKit;
import com.fr.third.org.apache.http.HttpEntity;
import com.fr.third.org.apache.http.client.config.RequestConfig;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.Map;
/**
* @author richie
* @version 10.0
* Created by richie on 2019-08-29
*/
public class HttpRequest {
private static final int TIME_OUT = 10 * 1000;
private static final RequestConfig DEFAULT = RequestConfig
.custom()
.setConnectionRequestTimeout(TIME_OUT)
.setConnectTimeout(TIME_OUT)
.setSocketTimeout(TIME_OUT)
.build();
/**
* 请求地址
*/
private String url;
/**
* 请求头
*/
private Map<String, String> headers;
/**
* 请求参数
*/
private Map<String, String> params;
/**
* 请求参数
*
* @see RequestConfig
*/
@Nullable
private RequestConfig config;
/**
* 请求参数
*
* @see HttpEntity
*/
@Nullable
private HttpEntity httpEntity;
/**
* 请求方法
*/
private HttpRequestType method;
/**
* 参数字符集
*/
private String encoding;
private HttpRequest(Builder builder) {
this.url = builder.url;
this.headers = builder.headers;
this.params = builder.params;
this.config = builder.config;
this.encoding = builder.encoding;
this.httpEntity = builder.httpEntity;
this.method = builder.method;
}
public String getUrl() {
return url;
}
public Map<String, String> getHeaders() {
return headers;
}
public Map<String, String> getParams() {
return params;
}
public RequestConfig getConfig() {
return config;
}
public String getEncoding() {
return encoding;
}
public HttpEntity getHttpEntity() {
return httpEntity;
}
public HttpRequestType getMethod() {
return method;
}
public static Builder custom() {
return new Builder();
}
public static final class Builder {
private String url;
private Map<String, String> headers = Collections.emptyMap();
private Map<String, String> params = Collections.emptyMap();
@Nullable
private RequestConfig config = DEFAULT;
@Nullable
private HttpEntity httpEntity;
private String encoding = EncodeConstantsKit.ENCODING_UTF_8;
private HttpRequestType method = HttpRequestType.GET;
private Builder() {
}
public HttpRequest build() {
if (this.url == null) {
throw new IllegalStateException("url == null");
}
return new HttpRequest(this);
}
public Builder url(@NotNull String url) {
if (url == null) {
throw new NullPointerException("url == null");
}
this.url = url;
return this;
}
public Builder headers(Map<String, String> headers) {
if (headers != null) {
this.headers = headers;
}
return this;
}
public Builder params(Map<String, String> params) {
if (params != null) {
this.params = params;
}
return this;
}
public Builder config(RequestConfig config) {
this.config = config;
return this;
}
public Builder get() {
this.method = HttpRequestType.GET;
return this;
}
public Builder post(HttpEntity httpEntity) {
this.method = HttpRequestType.POST;
this.httpEntity(httpEntity);
return this;
}
public Builder post(Map<String, String> params) {
this.method = HttpRequestType.POST;
this.params(params);
return this;
}
public Builder put(HttpEntity httpEntity) {
this.method = HttpRequestType.PUT;
this.httpEntity(httpEntity);
return this;
}
public Builder put(Map<String, String> params) {
this.method = HttpRequestType.PUT;
this.params(params);
return this;
}
public Builder delete() {
this.method = HttpRequestType.DELETE;
return this;
}
public Builder encoding(String encoding) {
if (encoding == null) {
throw new NullPointerException("httpEntity == null");
}
this.encoding = encoding;
return this;
}
public Builder httpEntity(HttpEntity httpEntity) {
this.httpEntity = httpEntity;
return this;
}
public Builder method(@NotNull HttpRequestType method) {
if (method == null) {
throw new NullPointerException("method == null");
}
this.method = method;
return this;
}
}
}

114
src/main/java/com/fanruan/api/net/http/rs/HttpRequestType.java

@ -0,0 +1,114 @@
package com.fanruan.api.net.http.rs;
import com.fr.third.org.apache.http.client.methods.HttpDelete;
import com.fr.third.org.apache.http.client.methods.HttpGet;
import com.fr.third.org.apache.http.client.methods.HttpHead;
import com.fr.third.org.apache.http.client.methods.HttpOptions;
import com.fr.third.org.apache.http.client.methods.HttpPatch;
import com.fr.third.org.apache.http.client.methods.HttpPost;
import com.fr.third.org.apache.http.client.methods.HttpPut;
import com.fr.third.org.apache.http.client.methods.HttpRequestBase;
import com.fr.third.org.apache.http.client.methods.HttpTrace;
/**
* @author richie
* @version 10.0
* Created by richie on 2019-08-29
*/
public enum HttpRequestType {
/**
* 求获取Request-URI所标识的资源
*/
GET("GET") {
@Override
public HttpRequestBase createHttpRequest(String url) {
return new HttpGet(url);
}
},
/**
* 向指定资源提交数据进行处理请求例如提交表单或者上传文件数据被包含在请求体中
* POST请求可能会导致新的资源的建立和/或已有资源的修改
*/
POST("POST") {
@Override
public HttpRequestBase createHttpRequest(String url) {
return new HttpPost(url);
}
},
/**
* 向服务器索要与GET请求相一致的响应只不过响应体将不会被返回
* 这一方法可以在不必传输整个响应内容的情况下就可以获取包含在响应消息头中的元信息
* 只获取响应信息报头
*/
HEAD("HEAD") {
@Override
public HttpRequestBase createHttpRequest(String url) {
return new HttpHead(url);
}
},
/**
* 向指定资源位置上传其最新内容全部更新操作幂等
*/
PUT("PUT") {
@Override
public HttpRequestBase createHttpRequest(String url) {
return new HttpPut(url);
}
},
/**
* 请求服务器删除Request-URI所标识的资源
*/
DELETE("DELETE") {
@Override
public HttpRequestBase createHttpRequest(String url) {
return new HttpDelete(url);
}
},
/**
* 请求服务器回送收到的请求信息主要用于测试或诊断
*/
TRACE("TRACE") {
@Override
public HttpRequestBase createHttpRequest(String url) {
return new HttpTrace(url);
}
},
/**
* 向指定资源位置上传其最新内容部分更新非幂等
*/
PATCH("PATCH") {
@Override
public HttpRequestBase createHttpRequest(String url) {
return new HttpPatch(url);
}
},
/**
* 返回服务器针对特定资源所支持的HTTP请求方法
* 也可以利用向Web服务器发送'*'的请求来测试服务器的功能性
*/
OPTIONS("OPTIONS") {
@Override
public HttpRequestBase createHttpRequest(String url) {
return new HttpOptions(url);
}
};
public abstract HttpRequestBase createHttpRequest(String url);
private String name;
HttpRequestType(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

103
src/main/java/com/fanruan/api/net/http/rs/HttpResponseType.java

@ -0,0 +1,103 @@
package com.fanruan.api.net.http.rs;
import com.fr.third.org.apache.http.HttpEntity;
import com.fr.third.org.apache.http.client.methods.CloseableHttpResponse;
import com.fr.third.org.apache.http.client.methods.HttpUriRequest;
import com.fr.third.org.apache.http.client.protocol.HttpClientContext;
import com.fr.third.org.apache.http.impl.client.CloseableHttpClient;
import com.fr.third.org.apache.http.util.EntityUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @author richie
* @version 10.0
* Created by richie on 2019-08-29
*/
public interface HttpResponseType<T> {
/**
* 处理http响应
*
* @param client 客户端
* @param url 地址
* @param request 请求
* @param charset 字符集
* @return 处理之后的响应
* @throws IOException 异常
*/
@Deprecated
T result(CloseableHttpClient client, String url, HttpUriRequest request, String charset) throws IOException;
/**
* 处理http响应
*
* @param response 响应
* @param charset 字符集
* @return 处理之后的响应
* @throws IOException 异常
*/
T result(CloseableHttpResponse response, String charset) throws IOException;
HttpResponseType<String> TEXT = new HttpResponseType<String>() {
@Override
public String result(CloseableHttpClient client, String url, HttpUriRequest request, String charset) throws IOException {
CloseableHttpResponse response = client.execute(request, HttpClientContext.create());
return result(response, charset);
}
@Override
public String result(CloseableHttpResponse response, String charset) throws IOException {
try {
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity, charset);
EntityUtils.consume(entity);
return result;
} finally {
if (response != null) {
response.close();
}
}
}
};
HttpResponseType<ByteArrayInputStream> STREAM = new HttpResponseType<ByteArrayInputStream>() {
@Override
public ByteArrayInputStream result(CloseableHttpClient client, String url, HttpUriRequest request, String charset) throws IOException {
CloseableHttpResponse response = client.execute(request, HttpClientContext.create());
return result(response, charset);
}
@Override
public ByteArrayInputStream result(CloseableHttpResponse response, String charset) throws IOException {
InputStream in = null;
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
in = entity.getContent();
byte[] buff = new byte[8000];
int bytesRead;
ByteArrayOutputStream bao = new ByteArrayOutputStream();
while ((bytesRead = in.read(buff)) != -1) {
bao.write(buff, 0, bytesRead);
}
byte[] data = bao.toByteArray();
return new ByteArrayInputStream(data);
}
return null;
} finally {
if (response != null) {
response.close();
}
if (in != null) {
in.close();
}
}
}
};
}

55
src/main/java/com/fanruan/api/net/http/rs/StreamResponseHandle.java

@ -0,0 +1,55 @@
package com.fanruan.api.net.http.rs;
import com.fr.third.org.apache.http.HttpEntity;
import com.fr.third.org.apache.http.client.methods.CloseableHttpResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 流响应解析器
*
* @author vito
* @date 2019-07-14
*/
public class StreamResponseHandle extends BaseHttpResponseHandle<ByteArrayInputStream> {
public static final StreamResponseHandle DEFAULT = new StreamResponseHandle();
private static final int BUFFER_LENGTH = 8000;
public StreamResponseHandle() {
}
public StreamResponseHandle(String encoding) {
super(encoding);
}
@Override
public ByteArrayInputStream parse(CloseableHttpResponse response) throws IOException {
InputStream in = null;
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
in = entity.getContent();
byte[] buff = new byte[BUFFER_LENGTH];
int bytesRead;
ByteArrayOutputStream bao = new ByteArrayOutputStream();
while ((bytesRead = in.read(buff)) != -1) {
bao.write(buff, 0, bytesRead);
}
byte[] data = bao.toByteArray();
return new ByteArrayInputStream(data);
}
return null;
} finally {
if (response != null) {
response.close();
}
if (in != null) {
in.close();
}
}
}
}

39
src/main/java/com/fanruan/api/net/http/rs/TextResponseHandle.java

@ -0,0 +1,39 @@
package com.fanruan.api.net.http.rs;
import com.fr.third.org.apache.http.HttpEntity;
import com.fr.third.org.apache.http.client.methods.CloseableHttpResponse;
import com.fr.third.org.apache.http.util.EntityUtils;
import java.io.IOException;
/**
* 文本响应解析器
*
* @author vito
* @date 2019-07-14
*/
public class TextResponseHandle extends BaseHttpResponseHandle<String> {
public static final TextResponseHandle DEFAULT = new TextResponseHandle();
public TextResponseHandle() {
}
public TextResponseHandle(String encoding) {
super(encoding);
}
@Override
public String parse(CloseableHttpResponse response) throws IOException {
try {
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity, getEncoding());
EntityUtils.consume(entity);
return result;
} finally {
if (response != null) {
response.close();
}
}
}
}

48
src/main/java/com/fanruan/api/net/http/rs/UploadResponseHandle.java

@ -0,0 +1,48 @@
package com.fanruan.api.net.http.rs;
import com.fr.third.org.apache.http.HttpEntity;
import com.fr.third.org.apache.http.HttpStatus;
import com.fr.third.org.apache.http.client.methods.CloseableHttpResponse;
import com.fr.third.org.apache.http.util.EntityUtils;
import java.io.IOException;
/**
* 上传响应解析器
*
* @author vito
* @date 2019-07-14
*/
public class UploadResponseHandle extends BaseHttpResponseHandle<Void> {
public static final UploadResponseHandle DEFAULT = new UploadResponseHandle();
public UploadResponseHandle() {
}
public UploadResponseHandle(String encoding) {
super(encoding);
}
@Override
public Void parse(CloseableHttpResponse response) throws IOException {
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
HttpEntity entity = response.getEntity();
if (entity != null) {
EntityUtils.consume(entity);
}
} else {
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity, getEncoding());
throw new IOException("Connect error, error code:" + statusCode + "; message:" + result);
}
} finally {
if (response != null) {
response.close();
}
}
return null;
}
}

153
src/test/java/com/fanruan/api/net/http/HttpKitTest.java

@ -0,0 +1,153 @@
package com.fanruan.api.net.http;
import com.fanruan.api.Prepare;
import com.fanruan.api.net.http.rs.HttpRequest;
import com.fanruan.api.net.http.rs.HttpResponseType;
import com.fanruan.api.net.http.rs.StreamResponseHandle;
import com.fanruan.api.util.IOKit;
import com.fr.json.JSONObject;
import okhttp3.HttpUrl;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.AfterClass;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
/**
* @author richie
* @version 10.0
* Created by richie on 2019-08-29
*/
public class HttpKitTest extends Prepare {
private static MockWebServer server = new MockWebServer();
@AfterClass
public static void tearDown() throws Exception {
server.shutdown();
}
@Test
public void testGet() {
String text = null;
try {
text = HttpKit.get("http://www.baidu.com");
} catch (IOException e) {
e.printStackTrace();
}
assertNotNull(text);
}
@Test
public void testPost() {
Map<String, String> map = new HashMap<String, String>();
map.put("key", "bbs");
try {
String resText = HttpKit.post("https://cloud.fanruan.com/site", map);
assertEquals("http://bbs.fanruan.com/", new JSONObject(resText).get("value"));
} catch (SocketTimeoutException ignore) {
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
@Test
public void testStream() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("key", "bbs");
try {
InputStream in = HttpKit.post("https://cloud.fanruan.com/site", map, HttpResponseType.STREAM);
String text = IOKit.inputStream2String(in, StandardCharsets.UTF_8);
assertEquals("{\"value\":\"http://bbs.fanruan.com/\"}", text);
} catch (SocketTimeoutException ignore) {
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
@Test
public void testStreamMock() {
server.enqueue(new MockResponse().setBody("{\"value\":\"http://bbs.fanruan.com/\"}"));
String url = server.url("/site").toString();
Map<String, String> map = new HashMap<String, String>();
map.put("key", "bbs");
try {
InputStream in = HttpKit.executeAndParse(HttpRequest
.custom()
.url(url)
.post(map)
.build(),
new StreamResponseHandle());
String text = IOKit.inputStream2String(in, StandardCharsets.UTF_8);
RecordedRequest takeRequest = server.takeRequest();
assertEquals("{\"value\":\"http://bbs.fanruan.com/\"}", text);
assertEquals("POST", takeRequest.getMethod());
assertEquals("key=bbs", takeRequest.getBody().readUtf8());
} catch (SocketTimeoutException ignore) {
} catch (Exception e) {
e.printStackTrace();
fail();
}
}
@Test
public void testMethod() throws Exception {
server.enqueue(new MockResponse().setBody("get"));
server.enqueue(new MockResponse().setBody("post"));
server.enqueue(new MockResponse().setBody("put"));
server.enqueue(new MockResponse().setBody("delete"));
String url = server.url("/v1/chat/").toString();
HttpKit.get(url);
assertEquals(server.takeRequest().getMethod(), "GET");
HttpKit.post(url, Collections.<String, String>emptyMap());
assertEquals(server.takeRequest().getMethod(), "POST");
HttpKit.executeAndParse(HttpRequest.custom().url(url).put(Collections.<String, String>emptyMap()).build());
assertEquals(server.takeRequest().getMethod(), "PUT");
HttpKit.executeAndParse(HttpRequest.custom().url(url).delete().build());
assertEquals(server.takeRequest().getMethod(), "DELETE");
}
@Test
public void testHeader() throws Exception {
server.enqueue(new MockResponse().setBody("hello, world!"));
HttpUrl baseUrl = server.url("/v1/chat/");
HashMap<String, String> headers = new HashMap<String, String>(1);
headers.put("Authorization", "abc");
String s = HttpKit.executeAndParse(HttpRequest.custom().url(baseUrl.toString()).post(Collections.<String, String>emptyMap()).headers(headers).build());
assertEquals("hello, world!", s);
// 测试请求头
RecordedRequest request = server.takeRequest();
assertEquals(request.getHeader("Authorization"), "abc");
assertEquals("POST /v1/chat/ HTTP/1.1", request.getRequestLine());
}
@Test
public void testParams() throws Exception {
server.enqueue(new MockResponse().setBody("hello, world!"));
HttpUrl baseUrl = server.url("/v1/chat/");
HashMap<String, String> params = new HashMap<String, String>(1);
params.put("key", "value");
String s = HttpKit.executeAndParse(HttpRequest.custom().url(baseUrl.toString()).post(params).build());
assertEquals("hello, world!", s);
// 测试参数
RecordedRequest request = server.takeRequest();
assertEquals("key=value", request.getBody().readUtf8());
assertEquals("POST /v1/chat/ HTTP/1.1", request.getRequestLine());
}
}
Loading…
Cancel
Save