576 lines
19 KiB
Java
576 lines
19 KiB
Java
|
package top.octopusyan.common.http;
|
|||
|
|
|||
|
import okhttp3.*;
|
|||
|
import top.octopusyan.common.http.annotation.HttpHeader;
|
|||
|
import top.octopusyan.common.http.annotation.HttpIgnore;
|
|||
|
import top.octopusyan.common.http.annotation.HttpRename;
|
|||
|
import top.octopusyan.common.http.api.NotParamApi;
|
|||
|
import top.octopusyan.common.http.api.ParamApi;
|
|||
|
import top.octopusyan.common.http.api.PathParamApi;
|
|||
|
import top.octopusyan.common.http.api.RequestApi;
|
|||
|
import top.octopusyan.common.http.callback.NormalCallback;
|
|||
|
import top.octopusyan.common.http.listener.OnHttpListener;
|
|||
|
import top.octopusyan.common.http.model.CallProxy;
|
|||
|
import top.octopusyan.common.http.model.HttpHeaders;
|
|||
|
import top.octopusyan.common.http.model.HttpParams;
|
|||
|
import top.octopusyan.common.http.model.JsonBody;
|
|||
|
import top.octopusyan.common.http.config.BodyType;
|
|||
|
import top.octopusyan.common.http.config.HttpConstant;
|
|||
|
import top.octopusyan.common.http.request.IRequestHandler;
|
|||
|
import top.octopusyan.common.http.model.ResponseClass;
|
|||
|
|
|||
|
import java.io.File;
|
|||
|
import java.io.InputStream;
|
|||
|
import java.lang.reflect.Field;
|
|||
|
import java.text.MessageFormat;
|
|||
|
import java.util.ArrayList;
|
|||
|
import java.util.Arrays;
|
|||
|
import java.util.List;
|
|||
|
import java.util.Map;
|
|||
|
|
|||
|
/**
|
|||
|
* @author : octopus yan
|
|||
|
* @email : octopus_yan@foxmail.com
|
|||
|
* @description : http 请求工具
|
|||
|
* @create : 2022-4-1 15:14
|
|||
|
*/
|
|||
|
public class EasyHttp<Param, Result> {
|
|||
|
|
|||
|
/** 请求处理策略 */
|
|||
|
private final IRequestHandler mHandler = HttpConfig.getInstance().getHandler();
|
|||
|
|
|||
|
/** 请求执行代理类 */
|
|||
|
private CallProxy mCallProxy;
|
|||
|
|
|||
|
/** 请求标记 */
|
|||
|
private String mTag;
|
|||
|
|
|||
|
/** 请求延迟 */
|
|||
|
private long mDelayMillis;
|
|||
|
|
|||
|
/** 请求服务地址 */
|
|||
|
private final String server = HttpConfig.getInstance().getServerPath();
|
|||
|
|
|||
|
/** 请求接口 */
|
|||
|
private RequestApi<Param, Result> mRequestApi;
|
|||
|
|
|||
|
/** 请求参数 */
|
|||
|
private Param param;
|
|||
|
|
|||
|
public static class Builder {
|
|||
|
private String method;
|
|||
|
private String apiPath;
|
|||
|
private BodyType bodyType;
|
|||
|
|
|||
|
public <Param, Result> ParamRequest<Param, Result> api(ParamApi<Param, Result> api) {
|
|||
|
return new ParamRequest<Param, Result>().api(api);
|
|||
|
}
|
|||
|
|
|||
|
public <Result> UrlRequest<Result> api(NotParamApi<Result> api) {
|
|||
|
return new UrlRequest<Result>().api(api);
|
|||
|
}
|
|||
|
|
|||
|
public <Result> RestfulRequest<Result> api(PathParamApi<Result> api) {
|
|||
|
return new RestfulRequest<Result>().api(api);
|
|||
|
}
|
|||
|
|
|||
|
public <Param, Result> EasyHttp<Param, Result> api(RequestApi<Param, Result> api) {
|
|||
|
return new EasyHttp<Param, Result>().api(api);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 无参数请求
|
|||
|
*/
|
|||
|
public <Result> UrlRequest<Result> build(ResponseClass<Result> result) {
|
|||
|
if(apiPath == null) throw new IllegalArgumentException("请求接口地址为空!");
|
|||
|
|
|||
|
NotParamApi<Result> requestApi = new NotParamApi<>(apiPath, method, bodyType);
|
|||
|
|
|||
|
return new UrlRequest<Result>().api(requestApi);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 带参请求
|
|||
|
* @param param 请求参数
|
|||
|
*/
|
|||
|
public <Param, Result> ParamRequest<Param, Result> build(Param param, ResponseClass<Result> result) {
|
|||
|
ParamApi<Param, Result> requestApi = new ParamApi<>(apiPath, method, bodyType);
|
|||
|
return new ParamRequest<Param, Result>().api(requestApi).param(param);
|
|||
|
}
|
|||
|
|
|||
|
public Builder setMethod(String method) {
|
|||
|
this.method = method;
|
|||
|
return this;
|
|||
|
}
|
|||
|
|
|||
|
public Builder setApiPath(String apiPath) {
|
|||
|
this.apiPath = apiPath;
|
|||
|
return this;
|
|||
|
}
|
|||
|
|
|||
|
public Builder setBodyType(BodyType bodyType) {
|
|||
|
this.bodyType = bodyType;
|
|||
|
return this;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static Builder builder(){
|
|||
|
return new Builder();
|
|||
|
}
|
|||
|
|
|||
|
protected EasyHttp<Param, Result> api(RequestApi<Param, Result> api) {
|
|||
|
this.mRequestApi = api;
|
|||
|
return this;
|
|||
|
}
|
|||
|
|
|||
|
protected EasyHttp<Param, Result> param(Param param) {
|
|||
|
this.param = param;
|
|||
|
return this;
|
|||
|
}
|
|||
|
|
|||
|
private RequestApi<Param, Result> getRequestApi() {
|
|||
|
return mRequestApi;
|
|||
|
}
|
|||
|
|
|||
|
private IRequestHandler getRequestHandler() {
|
|||
|
return mHandler;
|
|||
|
}
|
|||
|
|
|||
|
protected void path(String path) {
|
|||
|
this.mRequestApi.setApi(path);
|
|||
|
}
|
|||
|
|
|||
|
protected String getPath(){
|
|||
|
return this.mRequestApi.getApi();
|
|||
|
}
|
|||
|
|
|||
|
public String getTag() {
|
|||
|
return mTag;
|
|||
|
}
|
|||
|
|
|||
|
public void setTag(String mTag) {
|
|||
|
this.mTag = mTag;
|
|||
|
}
|
|||
|
|
|||
|
public long getDelayMillis() {
|
|||
|
return mDelayMillis;
|
|||
|
}
|
|||
|
|
|||
|
public void setDelayMillis(long mDelayMillis) {
|
|||
|
this.mDelayMillis = mDelayMillis;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 执行异步请求
|
|||
|
*/
|
|||
|
public void request(OnHttpListener<Result> listener) {
|
|||
|
if (mDelayMillis > 0) {
|
|||
|
// 打印请求延迟时间
|
|||
|
HttpLog.print("RequestDelay", String.valueOf(mDelayMillis));
|
|||
|
}
|
|||
|
|
|||
|
StackTraceElement[] stackTrace = new Throwable().getStackTrace();
|
|||
|
EasyUtils.postDelayed(() -> {
|
|||
|
HttpLog.print(stackTrace);
|
|||
|
mCallProxy = new CallProxy(createCall());
|
|||
|
mCallProxy.enqueue(new NormalCallback(mCallProxy, mRequestApi, mHandler, listener));
|
|||
|
|
|||
|
}, mDelayMillis);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 执行同步请求
|
|||
|
* @param responseClass 需要解析泛型的对象
|
|||
|
* @return 返回解析完成的对象
|
|||
|
* @throws Exception 如果请求失败或者解析失败则抛出异常
|
|||
|
*/
|
|||
|
public Result execute(ResponseClass<Result> responseClass) throws Exception {
|
|||
|
if (mDelayMillis > 0) {
|
|||
|
// 打印请求延迟时间
|
|||
|
HttpLog.print("RequestDelay", String.valueOf(mDelayMillis));
|
|||
|
Thread.sleep(mDelayMillis);
|
|||
|
}
|
|||
|
|
|||
|
HttpLog.print(new Throwable().getStackTrace());
|
|||
|
try {
|
|||
|
mCallProxy = new CallProxy(createCall());
|
|||
|
Response response = mCallProxy.execute();
|
|||
|
return (Result) mHandler.requestSucceed(getRequestApi(), response, EasyUtils.getReflectType(responseClass));
|
|||
|
} catch (Exception e) {
|
|||
|
throw mHandler.requestFail(getRequestApi(), e);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 创建请求回调
|
|||
|
*/
|
|||
|
private Call createCall() {
|
|||
|
String url = server + mRequestApi.getApi();
|
|||
|
HttpParams params = new HttpParams();
|
|||
|
HttpHeaders headers = new HttpHeaders();
|
|||
|
|
|||
|
BodyType type = mRequestApi.getBodyType();
|
|||
|
|
|||
|
if(param != null) setParam(params, headers, type);
|
|||
|
|
|||
|
return HttpConfig.getInstance().getClient().newCall(createRequest(url, mTag, params, headers, type));
|
|||
|
}
|
|||
|
|
|||
|
private void setParam(HttpParams params, HttpHeaders headers, BodyType type){
|
|||
|
|
|||
|
List<Field> fields = new ArrayList<>();
|
|||
|
|
|||
|
Class<?> clazz = param.getClass();
|
|||
|
do {
|
|||
|
Field[] declaredFields = clazz.getDeclaredFields();
|
|||
|
fields.addAll(0, Arrays.asList(declaredFields));
|
|||
|
// 遍历获取父类的字段
|
|||
|
clazz = clazz.getSuperclass();
|
|||
|
} while (clazz != null && !Object.class.equals(clazz));
|
|||
|
|
|||
|
// 当前请求是否存在流参数
|
|||
|
params.setMultipart(EasyUtils.isMultipart(fields));
|
|||
|
|
|||
|
// 如果参数中包含流参数并且当前请求方式不是表单的话
|
|||
|
if (params.isMultipart() && type != BodyType.FORM) {
|
|||
|
// 就强制设置成以表单形式提交参数
|
|||
|
type = BodyType.FORM;
|
|||
|
}
|
|||
|
|
|||
|
for (Field field : fields) {
|
|||
|
// 允许访问私有字段
|
|||
|
field.setAccessible(true);
|
|||
|
|
|||
|
try {
|
|||
|
// 获取字段的对象
|
|||
|
Object value = field.get(param);
|
|||
|
|
|||
|
// 获取字段的名称
|
|||
|
String key;
|
|||
|
HttpRename annotation = field.getAnnotation(HttpRename.class);
|
|||
|
if (annotation != null) {
|
|||
|
key = annotation.value();
|
|||
|
} else {
|
|||
|
key = field.getName();
|
|||
|
// 如果是内部类则会出现一个字段名为 this$0 的外部类对象,会导致无限递归,这里要忽略掉,如果使用静态内部类则不会出现这个问题
|
|||
|
// 和规避 Kotlin 自动生成的伴生对象:https://github.com/getActivity/EasyHttp/issues/15
|
|||
|
if (key.matches("this\\$\\d+") || "Companion".equals(key)) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 如果这个字段需要忽略,则进行忽略
|
|||
|
if (field.isAnnotationPresent(HttpIgnore.class)) {
|
|||
|
if (field.isAnnotationPresent(HttpHeader.class)) {
|
|||
|
headers.remove(key);
|
|||
|
} else {
|
|||
|
params.remove(key);
|
|||
|
}
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
// 前提是这个字段值不能为空(基本数据类型有默认的值,而对象默认的值为 null)
|
|||
|
if (EasyUtils.isEmpty(value)) {
|
|||
|
// 遍历下一个字段
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
// 如果这是一个请求头参数
|
|||
|
if (field.isAnnotationPresent(HttpHeader.class)) {
|
|||
|
if (value instanceof Map) {
|
|||
|
Map<?, ?> map = ((Map<?, ?>) value);
|
|||
|
for (Object o : map.keySet()) {
|
|||
|
if (o != null && map.get(o) != null) {
|
|||
|
headers.put(String.valueOf(o), String.valueOf(map.get(o)));
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
headers.put(key, String.valueOf(value));
|
|||
|
}
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
// 否则这就是一个普通的参数
|
|||
|
switch (type) {
|
|||
|
case FORM:
|
|||
|
if (value instanceof Map) {
|
|||
|
Map<?, ?> map = ((Map<?, ?>) value);
|
|||
|
for (Object o : map.keySet()) {
|
|||
|
if (o != null && map.get(o) != null) {
|
|||
|
params.put(String.valueOf(o), map.get(o));
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
params.put(key, value);
|
|||
|
}
|
|||
|
break;
|
|||
|
case JSON:
|
|||
|
if (value instanceof List) {
|
|||
|
// 如果这是一个 List 参数
|
|||
|
params.put(key, EasyUtils.listToJsonArray(((List<?>) value)));
|
|||
|
} else if (value instanceof Map) {
|
|||
|
// 如果这是一个 Map 参数
|
|||
|
params.put(key, EasyUtils.mapToJsonObject(((Map<?, ?>) value)));
|
|||
|
} else if (EasyUtils.isBeanType(value)) {
|
|||
|
// 如果这是一个 Bean 参数
|
|||
|
params.put(key, EasyUtils.mapToJsonObject(EasyUtils.beanToHashMap(value)));
|
|||
|
} else {
|
|||
|
// 如果这是一个普通的参数
|
|||
|
params.put(key, value);
|
|||
|
}
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
} catch (IllegalAccessException e) {
|
|||
|
HttpLog.print(e);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 创建 okhttp 请求
|
|||
|
*
|
|||
|
* @param url 请求地址
|
|||
|
* @param tag 标记
|
|||
|
* @param params 参数
|
|||
|
* @param headers 请求头
|
|||
|
* @param type 参数提交方式
|
|||
|
* @return okhttp 请求对象
|
|||
|
*/
|
|||
|
private Request createRequest(String url, String tag, HttpParams params, HttpHeaders headers, BodyType type) {
|
|||
|
Request.Builder request = new Request.Builder();
|
|||
|
|
|||
|
if (tag != null) {
|
|||
|
request.tag(tag);
|
|||
|
}
|
|||
|
|
|||
|
// 添加请求头
|
|||
|
if (!headers.isEmpty()) {
|
|||
|
for (String key : headers.getNames()) {
|
|||
|
request.addHeader(key, headers.get(key));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
RequestBody body = null;
|
|||
|
String requestUrl = "";
|
|||
|
String httpMethod = getRequestApi().getMethod();
|
|||
|
|
|||
|
switch (httpMethod){
|
|||
|
case HttpConstant.Method.GET:
|
|||
|
HttpUrl.Builder builder = HttpUrl.get(url).newBuilder();
|
|||
|
// 添加参数
|
|||
|
if (!params.isEmpty()) {
|
|||
|
for (String key : params.getNames()) {
|
|||
|
builder.addEncodedQueryParameter(key, String.valueOf(params.get(key)));
|
|||
|
}
|
|||
|
}
|
|||
|
HttpUrl link = builder.build();
|
|||
|
requestUrl = String.valueOf(link);
|
|||
|
request.url(link);
|
|||
|
break;
|
|||
|
case HttpConstant.Method.POST:
|
|||
|
request.url(url);
|
|||
|
requestUrl = url;
|
|||
|
body = createBody(params, type);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
request.method(httpMethod, body);
|
|||
|
HttpLog.print("RequestUrl", requestUrl);
|
|||
|
HttpLog.print("RequestMethod", httpMethod);
|
|||
|
|
|||
|
// 打印请求头和参数的日志
|
|||
|
if (HttpConfig.getInstance().isLogEnabled()) {
|
|||
|
|
|||
|
if (!headers.isEmpty() || !params.isEmpty()) {
|
|||
|
HttpLog.print();
|
|||
|
}
|
|||
|
|
|||
|
for (String key : headers.getNames()) {
|
|||
|
HttpLog.print(key, headers.get(key));
|
|||
|
}
|
|||
|
|
|||
|
if (!headers.isEmpty() && !params.isEmpty()) {
|
|||
|
HttpLog.print();
|
|||
|
}
|
|||
|
|
|||
|
for (String key : params.getNames()) {
|
|||
|
HttpLog.print(key, String.valueOf(params.get(key)));
|
|||
|
}
|
|||
|
|
|||
|
if (!headers.isEmpty() || !params.isEmpty()) {
|
|||
|
HttpLog.print();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return getRequestHandler().requestStart(getRequestApi(), request.build());
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 组装 RequestBody 对象
|
|||
|
*/
|
|||
|
private RequestBody createBody(HttpParams params, BodyType type) {
|
|||
|
RequestBody body;
|
|||
|
|
|||
|
if (params.isMultipart() && !params.isEmpty()) {
|
|||
|
MultipartBody.Builder builder = new MultipartBody.Builder();
|
|||
|
builder.setType(MultipartBody.FORM);
|
|||
|
for (String key : params.getNames()) {
|
|||
|
Object object = params.get(key);
|
|||
|
|
|||
|
// 如果这是一个 File 对象
|
|||
|
if (object instanceof File) {
|
|||
|
MultipartBody.Part part = EasyUtils.createPart(key, (File) object);
|
|||
|
if (part != null) {
|
|||
|
builder.addPart(part);
|
|||
|
}
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
// 如果这是一个 InputStream 对象
|
|||
|
if (object instanceof InputStream) {
|
|||
|
MultipartBody.Part part = EasyUtils.createPart(key, (InputStream) object);
|
|||
|
if (part != null) {
|
|||
|
builder.addPart(part);
|
|||
|
}
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
// 如果这是一个自定义的 MultipartBody.Part 对象
|
|||
|
if (object instanceof MultipartBody.Part) {
|
|||
|
builder.addPart((MultipartBody.Part) object);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
// 如果这是一个自定义的 RequestBody 对象
|
|||
|
if (object instanceof RequestBody) {
|
|||
|
builder.addFormDataPart(key, null, (RequestBody) object);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
// 如果这是一个普通参数
|
|||
|
builder.addFormDataPart(key, String.valueOf(object));
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
body = builder.build();
|
|||
|
} catch (IllegalStateException ignored) {
|
|||
|
// 如果参数为空则会抛出异常:Multipart body must have at least one part.
|
|||
|
body = new FormBody.Builder().build();
|
|||
|
}
|
|||
|
|
|||
|
} else if (type == BodyType.JSON) {
|
|||
|
body = new JsonBody(params.getParams());
|
|||
|
} else {
|
|||
|
FormBody.Builder builder = new FormBody.Builder();
|
|||
|
if (!params.isEmpty()) {
|
|||
|
for (String key : params.getNames()) {
|
|||
|
builder.add(key, String.valueOf(params.get(key)));
|
|||
|
}
|
|||
|
}
|
|||
|
body = builder.build();
|
|||
|
}
|
|||
|
|
|||
|
return body;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* 根据 TAG 取消请求任务
|
|||
|
*/
|
|||
|
public static void cancel(Object tag) {
|
|||
|
if (tag == null) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
OkHttpClient client = HttpConfig.getInstance().getClient();
|
|||
|
|
|||
|
// 清除排队等候的任务
|
|||
|
for (Call call : client.dispatcher().queuedCalls()) {
|
|||
|
if (tag.equals(call.request().tag())) {
|
|||
|
call.cancel();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 清除正在执行的任务
|
|||
|
for (Call call : client.dispatcher().runningCalls()) {
|
|||
|
if (tag.equals(call.request().tag())) {
|
|||
|
call.cancel();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 清除所有请求任务
|
|||
|
*/
|
|||
|
public static void cancel() {
|
|||
|
OkHttpClient client = HttpConfig.getInstance().getClient();
|
|||
|
|
|||
|
// 清除排队等候的任务
|
|||
|
for (Call call : client.dispatcher().queuedCalls()) {
|
|||
|
call.cancel();
|
|||
|
}
|
|||
|
|
|||
|
// 清除正在执行的任务
|
|||
|
for (Call call : client.dispatcher().runningCalls()) {
|
|||
|
call.cancel();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 无参数请求
|
|||
|
*/
|
|||
|
public static class UrlRequest<Result> extends EasyHttp<Void, Result> {
|
|||
|
|
|||
|
@Override
|
|||
|
public UrlRequest<Result> api(RequestApi<Void, Result> api) {
|
|||
|
super.api(api);
|
|||
|
return this;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 路径参数请求
|
|||
|
*/
|
|||
|
public static class RestfulRequest<Result> extends EasyHttp<Void, Result> {
|
|||
|
|
|||
|
@Override
|
|||
|
public RestfulRequest<Result> api(RequestApi<Void, Result> api) {
|
|||
|
super.api(api);
|
|||
|
return this;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @param params 路径参数
|
|||
|
*/
|
|||
|
public RestfulRequest<Result> pathParam(Object... params) {
|
|||
|
super.path(MessageFormat.format(getPath(), params));
|
|||
|
return this;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
*
|
|||
|
* 带参数请求
|
|||
|
*
|
|||
|
* @param <Param> 参数类型
|
|||
|
* @param <Result> 返回结果类型
|
|||
|
*/
|
|||
|
public static class ParamRequest<Param, Result> extends EasyHttp<Param, Result> {
|
|||
|
|
|||
|
@Override
|
|||
|
public ParamRequest<Param, Result> api(RequestApi<Param, Result> api) {
|
|||
|
super.api(api);
|
|||
|
return this;
|
|||
|
}
|
|||
|
|
|||
|
@Override
|
|||
|
public ParamRequest<Param, Result> param(Param param) {
|
|||
|
super.param(param);
|
|||
|
return this;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|