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 {
/** 请求处理策略 */
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 mRequestApi;
/** 请求参数 */
private Param param;
public static class Builder {
private String method;
private String apiPath;
private BodyType bodyType;
public ParamRequest api(ParamApi api) {
return new ParamRequest().api(api);
}
public UrlRequest api(NotParamApi api) {
return new UrlRequest().api(api);
}
public RestfulRequest api(PathParamApi api) {
return new RestfulRequest().api(api);
}
public EasyHttp api(RequestApi api) {
return new EasyHttp().api(api);
}
/**
* 无参数请求
*/
public UrlRequest build(ResponseClass result) {
if(apiPath == null) throw new IllegalArgumentException("请求接口地址为空!");
NotParamApi requestApi = new NotParamApi<>(apiPath, method, bodyType);
return new UrlRequest().api(requestApi);
}
/**
* 带参请求
* @param param 请求参数
*/
public ParamRequest build(Param param, ResponseClass result) {
ParamApi requestApi = new ParamApi<>(apiPath, method, bodyType);
return new ParamRequest().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 api(RequestApi api) {
this.mRequestApi = api;
return this;
}
protected EasyHttp param(Param param) {
this.param = param;
return this;
}
private RequestApi 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 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 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 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 extends EasyHttp {
@Override
public UrlRequest api(RequestApi api) {
super.api(api);
return this;
}
}
/**
* 路径参数请求
*/
public static class RestfulRequest extends EasyHttp {
@Override
public RestfulRequest api(RequestApi api) {
super.api(api);
return this;
}
/**
* @param params 路径参数
*/
public RestfulRequest pathParam(Object... params) {
super.path(MessageFormat.format(getPath(), params));
return this;
}
}
/**
*
* 带参数请求
*
* @param 参数类型
* @param 返回结果类型
*/
public static class ParamRequest extends EasyHttp {
@Override
public ParamRequest api(RequestApi api) {
super.api(api);
return this;
}
@Override
public ParamRequest param(Param param) {
super.param(param);
return this;
}
}
}