fix bugs
This commit is contained in:
41
blade-starter-http/pom.xml
Normal file
41
blade-starter-http/pom.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>BladeX-Tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>blade-starter-http</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<version>${project.parent.version}</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-core-tool</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>logging-interceptor</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Request;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 异步执行器
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class AsyncCall {
|
||||
private static final Consumer<ResponseSpec> DEFAULT_CONSUMER = (r) -> {};
|
||||
private static final BiConsumer<Request, IOException> DEFAULT_FAIL_CONSUMER = (r, e) -> {};
|
||||
private final Call call;
|
||||
private Consumer<ResponseSpec> successConsumer;
|
||||
private Consumer<ResponseSpec> responseConsumer;
|
||||
private BiConsumer<Request, IOException> failedBiConsumer;
|
||||
|
||||
AsyncCall(Call call) {
|
||||
this.call = call;
|
||||
this.successConsumer = DEFAULT_CONSUMER;
|
||||
this.responseConsumer = DEFAULT_CONSUMER;
|
||||
this.failedBiConsumer = DEFAULT_FAIL_CONSUMER;
|
||||
}
|
||||
|
||||
public void onSuccessful(Consumer<ResponseSpec> consumer) {
|
||||
this.successConsumer = consumer;
|
||||
this.execute();
|
||||
}
|
||||
|
||||
public void onResponse(Consumer<ResponseSpec> consumer) {
|
||||
this.responseConsumer = consumer;
|
||||
this.execute();
|
||||
}
|
||||
|
||||
public AsyncCall onFailed(BiConsumer<Request, IOException> biConsumer) {
|
||||
this.failedBiConsumer = biConsumer;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void execute() {
|
||||
call.enqueue(new AsyncCallback(this));
|
||||
}
|
||||
|
||||
void onResponse(HttpResponse httpResponse) {
|
||||
responseConsumer.accept(httpResponse);
|
||||
}
|
||||
|
||||
void onSuccessful(HttpResponse httpResponse) {
|
||||
successConsumer.accept(httpResponse);
|
||||
}
|
||||
|
||||
void onFailure(Request request, IOException e) {
|
||||
failedBiConsumer.accept(request, e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.Response;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 异步处理
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public class AsyncCallback implements Callback {
|
||||
private final AsyncCall asyncCall;
|
||||
|
||||
AsyncCallback(AsyncCall asyncCall) {
|
||||
this.asyncCall = asyncCall;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
asyncCall.onFailure(call.request(), e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) throws IOException {
|
||||
try (HttpResponse httpResponse = new HttpResponse(response)) {
|
||||
asyncCall.onResponse(httpResponse);
|
||||
if (response.isSuccessful()) {
|
||||
asyncCall.onSuccessful(httpResponse);
|
||||
} else {
|
||||
asyncCall.onFailure(call.request(), new IOException(httpResponse.message()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import okhttp3.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* BaseAuth
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class BaseAuthenticator implements Authenticator {
|
||||
private final String userName;
|
||||
private final String password;
|
||||
|
||||
@Override
|
||||
public Request authenticate(Route route, Response response) throws IOException {
|
||||
String credential = Credentials.basic(userName, password, StandardCharsets.UTF_8);
|
||||
return response.request().newBuilder()
|
||||
.header("Authorization", credential)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* xml CssQuery
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Target({ElementType.FIELD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface CssQuery {
|
||||
|
||||
/**
|
||||
* CssQuery
|
||||
*
|
||||
* @return CssQuery
|
||||
*/
|
||||
String value();
|
||||
|
||||
/**
|
||||
* 读取的 dom attr
|
||||
*
|
||||
* <p>
|
||||
* attr:元素对于的 attr 的值
|
||||
* html:整个元素的html
|
||||
* text:元素内文本
|
||||
* allText:多个元素的文本值
|
||||
* </p>
|
||||
*
|
||||
* @return attr
|
||||
*/
|
||||
String attr() default "";
|
||||
|
||||
/**
|
||||
* 正则,用于对 attr value 处理
|
||||
*
|
||||
* @return regex
|
||||
*/
|
||||
String regex() default "";
|
||||
|
||||
/**
|
||||
* 默认的正则 group
|
||||
*/
|
||||
int DEFAULT_REGEX_GROUP = 0;
|
||||
|
||||
/**
|
||||
* 正则 group,默认为 0
|
||||
*
|
||||
* @return regexGroup
|
||||
*/
|
||||
int regexGroup() default DEFAULT_REGEX_GROUP;
|
||||
|
||||
/**
|
||||
* 嵌套的内部模型:默认 false
|
||||
*
|
||||
* @return 是否为内部模型
|
||||
*/
|
||||
boolean inner() default false;
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.nodes.TextNode;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.jsoup.select.Selector;
|
||||
import org.springblade.core.tool.utils.ConvertUtil;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springframework.cglib.proxy.MethodInterceptor;
|
||||
import org.springframework.cglib.proxy.MethodProxy;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 代理模型
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class CssQueryMethodInterceptor implements MethodInterceptor {
|
||||
private final Class<?> clazz;
|
||||
private final Element element;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
|
||||
// 只处理 get 方法 is
|
||||
String name = method.getName();
|
||||
if (!StringUtil.startsWithIgnoreCase(name, StringPool.GET)) {
|
||||
return methodProxy.invokeSuper(object, args);
|
||||
}
|
||||
Field field = clazz.getDeclaredField(StringUtil.firstCharToLower(name.substring(3)));
|
||||
CssQuery cssQuery = field.getAnnotation(CssQuery.class);
|
||||
// 没有注解,不代理
|
||||
if (cssQuery == null) {
|
||||
return methodProxy.invokeSuper(object, args);
|
||||
}
|
||||
Class<?> returnType = method.getReturnType();
|
||||
boolean isColl = Collection.class.isAssignableFrom(returnType);
|
||||
String cssQueryValue = cssQuery.value();
|
||||
// 是否为 bean 中 bean
|
||||
boolean isInner = cssQuery.inner();
|
||||
if (isInner) {
|
||||
return proxyInner(cssQueryValue, method, returnType, isColl);
|
||||
}
|
||||
Object proxyValue = proxyValue(cssQueryValue, cssQuery, returnType, isColl);
|
||||
if (String.class.isAssignableFrom(returnType)) {
|
||||
return proxyValue;
|
||||
}
|
||||
// 用于读取 field 上的注解
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(field);
|
||||
return ConvertUtil.convert(proxyValue, typeDescriptor);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object proxyValue(String cssQueryValue, CssQuery cssQuery, Class<?> returnType, boolean isColl) {
|
||||
if (isColl) {
|
||||
Elements elements = Selector.select(cssQueryValue, element);
|
||||
Collection<Object> valueList = newColl(returnType);
|
||||
if (elements.isEmpty()) {
|
||||
return valueList;
|
||||
}
|
||||
for (Element select : elements) {
|
||||
String value = getValue(select, cssQuery);
|
||||
if (value != null) {
|
||||
valueList.add(value);
|
||||
}
|
||||
}
|
||||
return valueList;
|
||||
}
|
||||
Element select = Selector.selectFirst(cssQueryValue, element);
|
||||
return getValue(select, cssQuery);
|
||||
}
|
||||
|
||||
private Object proxyInner(String cssQueryValue, Method method, Class<?> returnType, boolean isColl) {
|
||||
if (isColl) {
|
||||
Elements elements = Selector.select(cssQueryValue, element);
|
||||
Collection<Object> valueList = newColl(returnType);
|
||||
ResolvableType resolvableType = ResolvableType.forMethodReturnType(method);
|
||||
Class<?> innerType = resolvableType.getGeneric(0).resolve();
|
||||
if (innerType == null) {
|
||||
throw new IllegalArgumentException("Class " + returnType + " 读取泛型失败。");
|
||||
}
|
||||
for (Element select : elements) {
|
||||
valueList.add(DomMapper.readValue(select, innerType));
|
||||
}
|
||||
return valueList;
|
||||
}
|
||||
Element select = Selector.selectFirst(cssQueryValue, element);
|
||||
return DomMapper.readValue(select, returnType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getValue(@Nullable Element element, CssQuery cssQuery) {
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
// 读取的属性名
|
||||
String attrName = cssQuery.attr();
|
||||
// 读取的值
|
||||
String attrValue;
|
||||
if (StringUtil.isBlank(attrName)) {
|
||||
attrValue = element.outerHtml();
|
||||
} else if ("html".equalsIgnoreCase(attrName)) {
|
||||
attrValue = element.html();
|
||||
} else if ("text".equalsIgnoreCase(attrName)) {
|
||||
attrValue = getText(element);
|
||||
} else if ("allText".equalsIgnoreCase(attrName)) {
|
||||
attrValue = element.text();
|
||||
} else {
|
||||
attrValue = element.attr(attrName);
|
||||
}
|
||||
// 判断是否需要正则处理
|
||||
String regex = cssQuery.regex();
|
||||
if (StringUtil.isBlank(attrValue) || StringUtil.isBlank(regex)) {
|
||||
return attrValue;
|
||||
}
|
||||
// 处理正则表达式
|
||||
return getRegexValue(regex, cssQuery.regexGroup(), attrValue);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getRegexValue(String regex, int regexGroup, String value) {
|
||||
// 处理正则表达式
|
||||
Matcher matcher = Pattern.compile(regex, Pattern.DOTALL | Pattern.CASE_INSENSITIVE).matcher(value);
|
||||
if (!matcher.find()) {
|
||||
return null;
|
||||
}
|
||||
// 正则 group
|
||||
if (regexGroup > CssQuery.DEFAULT_REGEX_GROUP) {
|
||||
return matcher.group(regexGroup);
|
||||
}
|
||||
return matcher.group();
|
||||
}
|
||||
|
||||
private String getText(Element element) {
|
||||
return element.childNodes().stream()
|
||||
.filter(node -> node instanceof TextNode)
|
||||
.map(node -> (TextNode) node)
|
||||
.map(TextNode::text)
|
||||
.collect(Collectors.joining());
|
||||
}
|
||||
|
||||
private Collection<Object> newColl(Class<?> returnType) {
|
||||
return Set.class.isAssignableFrom(returnType) ? new HashSet<>() : new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import org.jsoup.helper.DataUtil;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.parser.Parser;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.springblade.core.tool.utils.Exceptions;
|
||||
import org.springframework.cglib.proxy.Enhancer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 爬虫 xml 转 bean 基于 jsoup
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class DomMapper {
|
||||
|
||||
/**
|
||||
* Returns body to jsoup Document.
|
||||
*
|
||||
* @return Document
|
||||
*/
|
||||
public static Document asDocument(ResponseSpec response) {
|
||||
return readDocument(response.asString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将流读取为 jsoup Document
|
||||
*
|
||||
* @param inputStream InputStream
|
||||
* @return Document
|
||||
*/
|
||||
public static Document readDocument(InputStream inputStream) {
|
||||
try {
|
||||
return DataUtil.load(inputStream, StandardCharsets.UTF_8.name(), "");
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 html 字符串读取为 jsoup Document
|
||||
*
|
||||
* @param html String
|
||||
* @return Document
|
||||
*/
|
||||
public static Document readDocument(String html) {
|
||||
return Parser.parse(html, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 xml 信息为 java Bean
|
||||
*
|
||||
* @param inputStream InputStream
|
||||
* @param clazz bean Class
|
||||
* @param <T> 泛型
|
||||
* @return 对象
|
||||
*/
|
||||
public static <T> T readValue(InputStream inputStream, final Class<T> clazz) {
|
||||
return readValue(readDocument(inputStream), clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 xml 信息为 java Bean
|
||||
*
|
||||
* @param html html String
|
||||
* @param clazz bean Class
|
||||
* @param <T> 泛型
|
||||
* @return 对象
|
||||
*/
|
||||
public static <T> T readValue(String html, final Class<T> clazz) {
|
||||
return readValue(readDocument(html), clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 xml 信息为 java Bean
|
||||
*
|
||||
* @param doc xml element
|
||||
* @param clazz bean Class
|
||||
* @param <T> 泛型
|
||||
* @return 对象
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T readValue(final Element doc, final Class<T> clazz) {
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(clazz);
|
||||
enhancer.setUseCache(true);
|
||||
enhancer.setCallback(new CssQueryMethodInterceptor(clazz, doc));
|
||||
return (T) enhancer.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 xml 信息为 java Bean
|
||||
*
|
||||
* @param <T> 泛型
|
||||
* @param inputStream InputStream
|
||||
* @param clazz bean Class
|
||||
* @return 对象
|
||||
*/
|
||||
public static <T> List<T> readList(InputStream inputStream, final Class<T> clazz) {
|
||||
return readList(readDocument(inputStream), clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 xml 信息为 java Bean
|
||||
*
|
||||
* @param <T> 泛型
|
||||
* @param html html String
|
||||
* @param clazz bean Class
|
||||
* @return 对象
|
||||
*/
|
||||
public static <T> List<T> readList(String html, final Class<T> clazz) {
|
||||
return readList(readDocument(html), clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 xml 信息为 java Bean
|
||||
*
|
||||
* @param doc xml element
|
||||
* @param clazz bean Class
|
||||
* @param <T> 泛型
|
||||
* @return 对象列表
|
||||
*/
|
||||
public static <T> List<T> readList(Element doc, Class<T> clazz) {
|
||||
CssQuery annotation = clazz.getAnnotation(CssQuery.class);
|
||||
if (annotation == null) {
|
||||
throw new IllegalArgumentException("DomMapper readList " + clazz + " mast has annotation @CssQuery.");
|
||||
}
|
||||
String cssQueryValue = annotation.value();
|
||||
Elements elements = doc.select(cssQueryValue);
|
||||
List<T> valueList = new ArrayList<>();
|
||||
for (Element element : elements) {
|
||||
valueList.add(readValue(element, clazz));
|
||||
}
|
||||
return valueList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Request;
|
||||
import org.springblade.core.tool.utils.Exceptions;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Exchange
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class Exchange {
|
||||
private BiConsumer<Request, IOException> failedBiConsumer = (r, e) -> {};
|
||||
private final Call call;
|
||||
|
||||
public Exchange onFailed(BiConsumer<Request, IOException> failConsumer) {
|
||||
this.failedBiConsumer = failConsumer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public <R> R onResponse(Function<ResponseSpec, R> func) {
|
||||
try (HttpResponse response = new HttpResponse(call.execute())) {
|
||||
return func.apply(response);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <R> R onSuccess(Function<ResponseSpec, R> func) {
|
||||
try (HttpResponse response = new HttpResponse(call.execute())) {
|
||||
return func.apply(response);
|
||||
} catch (IOException e) {
|
||||
failedBiConsumer.accept(call.request(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <R> R onSuccessful(Function<ResponseSpec, R> func) {
|
||||
try (HttpResponse response = new HttpResponse(call.execute())) {
|
||||
if (response.isOk()) {
|
||||
return func.apply(response);
|
||||
} else {
|
||||
failedBiConsumer.accept(call.request(), new IOException(response.toString()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
failedBiConsumer.accept(call.request(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public <R> Optional<R> onSuccessOpt(Function<ResponseSpec, R> func) {
|
||||
return Optional.ofNullable(this.onSuccess(func));
|
||||
}
|
||||
|
||||
public <R> Optional<R> onSuccessfulOpt(Function<ResponseSpec, R> func) {
|
||||
return Optional.ofNullable(this.onSuccessful(func));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns body String.
|
||||
*
|
||||
* @return body String
|
||||
*/
|
||||
public String asString() {
|
||||
return onResponse(ResponseSpec::asString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns body to byte arrays.
|
||||
*
|
||||
* @return byte arrays
|
||||
*/
|
||||
public byte[] asBytes() {
|
||||
return onResponse(ResponseSpec::asBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns body to JsonNode.
|
||||
*
|
||||
* @return JsonNode
|
||||
*/
|
||||
public JsonNode asJsonNode() {
|
||||
return onResponse(ResponseSpec::asJsonNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns body to Object.
|
||||
*
|
||||
* @param valueType value value type
|
||||
* @return Object
|
||||
*/
|
||||
public <T> T asValue(Class<T> valueType) {
|
||||
return onResponse(responseSpec -> responseSpec.asValue(valueType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns body to Object.
|
||||
*
|
||||
* @param typeReference value Type Reference
|
||||
* @return Object
|
||||
*/
|
||||
public <T> T asValue(TypeReference<T> typeReference) {
|
||||
return onResponse(responseSpec -> responseSpec.asValue(typeReference));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns body to List.
|
||||
*
|
||||
* @param valueType value type
|
||||
* @return List
|
||||
*/
|
||||
public <T> List<T> asList(Class<T> valueType) {
|
||||
return onResponse(responseSpec -> responseSpec.asList(valueType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns body to Map.
|
||||
*
|
||||
* @param keyClass key type
|
||||
* @param valueType value type
|
||||
* @return Map
|
||||
*/
|
||||
public <K, V> Map<K, V> asMap(Class<?> keyClass, Class<?> valueType) {
|
||||
return onResponse(responseSpec -> responseSpec.asMap(keyClass, valueType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns body to Map.
|
||||
*
|
||||
* @param valueType value 类型
|
||||
* @return Map
|
||||
*/
|
||||
public <V> Map<String, V> asMap(Class<?> valueType) {
|
||||
return onResponse(responseSpec -> responseSpec.asMap(valueType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 xml、heml 转成对象
|
||||
*
|
||||
* @param valueType 对象类
|
||||
* @param <T> 泛型
|
||||
* @return 对象
|
||||
*/
|
||||
public <T> T asDomValue(Class<T> valueType) {
|
||||
return onResponse(responseSpec -> responseSpec.asDomValue(valueType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 xml、heml 转成对象
|
||||
*
|
||||
* @param valueType 对象类
|
||||
* @param <T> 泛型
|
||||
* @return 对象集合
|
||||
*/
|
||||
public <T> List<T> asDomList(Class<T> valueType) {
|
||||
return onResponse(responseSpec -> responseSpec.asDomList(valueType));
|
||||
}
|
||||
|
||||
/**
|
||||
* toFile.
|
||||
*
|
||||
* @param file File
|
||||
*/
|
||||
public File toFile(File file) {
|
||||
return onResponse(responseSpec -> responseSpec.toFile(file));
|
||||
}
|
||||
|
||||
/**
|
||||
* toFile.
|
||||
*
|
||||
* @param path Path
|
||||
*/
|
||||
public Path toFile(Path path) {
|
||||
return onResponse(responseSpec -> responseSpec.toFile(path));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import okhttp3.FormBody;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 表单构造器
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class FormBuilder {
|
||||
private final HttpRequest request;
|
||||
private final FormBody.Builder formBuilder;
|
||||
|
||||
FormBuilder(HttpRequest request) {
|
||||
this.request = request;
|
||||
this.formBuilder = new FormBody.Builder();
|
||||
}
|
||||
|
||||
public FormBuilder add(String name, @Nullable Object value) {
|
||||
this.formBuilder.add(name, HttpRequest.handleValue(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public FormBuilder addMap(@Nullable Map<String, Object> formMap) {
|
||||
if (formMap != null && !formMap.isEmpty()) {
|
||||
formMap.forEach(this::add);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public FormBuilder addEncoded(String name, @Nullable Object encodedValue) {
|
||||
this.formBuilder.addEncoded(name, HttpRequest.handleValue(encodedValue));
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest build() {
|
||||
FormBody formBody = formBuilder.build();
|
||||
this.request.form(formBody);
|
||||
return this.request;
|
||||
}
|
||||
|
||||
public Exchange execute() {
|
||||
return this.build().execute();
|
||||
}
|
||||
|
||||
public AsyncCall async() {
|
||||
return this.build().async();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,501 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import okhttp3.*;
|
||||
import okhttp3.internal.Util;
|
||||
import okhttp3.internal.http.HttpMethod;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springblade.core.tool.ssl.DisableValidationTrustManager;
|
||||
import org.springblade.core.tool.ssl.TrustAllHostNames;
|
||||
import org.springblade.core.tool.utils.Exceptions;
|
||||
import org.springblade.core.tool.utils.Holder;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.net.ssl.*;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.ProxySelector;
|
||||
import java.net.URI;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* ok http 封装,请求结构体
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class HttpRequest {
|
||||
private static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36";
|
||||
private static final MediaType APPLICATION_JSON = MediaType.parse("application/json;charset=UTF-8");
|
||||
private static volatile OkHttpClient httpClient = new OkHttpClient();
|
||||
@Nullable
|
||||
private static HttpLoggingInterceptor globalLoggingInterceptor = null;
|
||||
private final Request.Builder requestBuilder;
|
||||
private final HttpUrl.Builder uriBuilder;
|
||||
private final String httpMethod;
|
||||
private String userAgent;
|
||||
@Nullable
|
||||
private RequestBody requestBody;
|
||||
@Nullable
|
||||
private Boolean followRedirects;
|
||||
@Nullable
|
||||
private Boolean followSslRedirects;
|
||||
@Nullable
|
||||
private HttpLoggingInterceptor.Level level;
|
||||
@Nullable
|
||||
private CookieJar cookieJar;
|
||||
@Nullable
|
||||
private EventListener eventListener;
|
||||
private final List<Interceptor> interceptors = new ArrayList<>();
|
||||
@Nullable
|
||||
private Authenticator authenticator;
|
||||
@Nullable
|
||||
private Duration connectTimeout;
|
||||
@Nullable
|
||||
private Duration readTimeout;
|
||||
@Nullable
|
||||
private Duration writeTimeout;
|
||||
@Nullable
|
||||
private Proxy proxy;
|
||||
@Nullable
|
||||
private ProxySelector proxySelector;
|
||||
@Nullable
|
||||
private Authenticator proxyAuthenticator;
|
||||
@Nullable
|
||||
private RetryPolicy retryPolicy;
|
||||
@Nullable
|
||||
private Boolean disableSslValidation;
|
||||
@Nullable
|
||||
private HostnameVerifier hostnameVerifier;
|
||||
@Nullable
|
||||
private SSLSocketFactory sslSocketFactory;
|
||||
@Nullable
|
||||
private X509TrustManager trustManager;
|
||||
|
||||
public static HttpRequest get(final String url) {
|
||||
return new HttpRequest(new Request.Builder(), url, Method.GET);
|
||||
}
|
||||
|
||||
public static HttpRequest get(final URI uri) {
|
||||
return get(uri.toString());
|
||||
}
|
||||
|
||||
public static HttpRequest post(final String url) {
|
||||
return new HttpRequest(new Request.Builder(), url, Method.POST);
|
||||
}
|
||||
|
||||
public static HttpRequest post(final URI uri) {
|
||||
return post(uri.toString());
|
||||
}
|
||||
|
||||
public static HttpRequest patch(final String url) {
|
||||
return new HttpRequest(new Request.Builder(), url, Method.PATCH);
|
||||
}
|
||||
|
||||
public static HttpRequest patch(final URI uri) {
|
||||
return patch(uri.toString());
|
||||
}
|
||||
|
||||
public static HttpRequest put(final String url) {
|
||||
return new HttpRequest(new Request.Builder(), url, Method.PUT);
|
||||
}
|
||||
|
||||
public static HttpRequest put(final URI uri) {
|
||||
return put(uri.toString());
|
||||
}
|
||||
|
||||
public static HttpRequest delete(final String url) {
|
||||
return new HttpRequest(new Request.Builder(), url, Method.DELETE);
|
||||
}
|
||||
|
||||
public static HttpRequest delete(final URI uri) {
|
||||
return delete(uri.toString());
|
||||
}
|
||||
|
||||
public HttpRequest query(String query) {
|
||||
this.uriBuilder.query(query);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest queryEncoded(String encodedQuery) {
|
||||
this.uriBuilder.encodedQuery(encodedQuery);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest queryMap(@Nullable Map<String, Object> queryMap) {
|
||||
if (queryMap != null && !queryMap.isEmpty()) {
|
||||
queryMap.forEach(this::query);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest query(String name, @Nullable Object value) {
|
||||
this.uriBuilder.addQueryParameter(name, value == null ? null : String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest queryEncoded(String encodedName, @Nullable Object encodedValue) {
|
||||
this.uriBuilder.addEncodedQueryParameter(encodedName, encodedValue == null ? null : String.valueOf(encodedValue));
|
||||
return this;
|
||||
}
|
||||
|
||||
HttpRequest form(FormBody formBody) {
|
||||
this.requestBody = formBody;
|
||||
return this;
|
||||
}
|
||||
|
||||
HttpRequest multipartForm(MultipartBody multipartBody) {
|
||||
this.requestBody = multipartBody;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FormBuilder formBuilder() {
|
||||
return new FormBuilder(this);
|
||||
}
|
||||
|
||||
public MultipartFormBuilder multipartFormBuilder() {
|
||||
return new MultipartFormBuilder(this);
|
||||
}
|
||||
|
||||
public HttpRequest body(RequestBody requestBody) {
|
||||
this.requestBody = requestBody;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest bodyString(String body) {
|
||||
this.requestBody = RequestBody.create(APPLICATION_JSON, body);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest bodyString(MediaType contentType, String body) {
|
||||
this.requestBody = RequestBody.create(contentType, body);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest bodyJson(@Nonnull Object body) {
|
||||
return bodyString(JsonUtil.toJson(body));
|
||||
}
|
||||
|
||||
private HttpRequest(final Request.Builder requestBuilder, String url, String httpMethod) {
|
||||
HttpUrl httpUrl = HttpUrl.parse(url);
|
||||
if (httpUrl == null) {
|
||||
throw new IllegalArgumentException(String.format("Url 不能解析: %s: [%s]。", httpMethod.toLowerCase(), url));
|
||||
}
|
||||
this.requestBuilder = requestBuilder;
|
||||
this.uriBuilder = httpUrl.newBuilder();
|
||||
this.httpMethod = httpMethod;
|
||||
this.userAgent = DEFAULT_USER_AGENT;
|
||||
}
|
||||
|
||||
private Call internalCall(final OkHttpClient client) {
|
||||
OkHttpClient.Builder builder = client.newBuilder();
|
||||
if (connectTimeout != null) {
|
||||
builder.connectTimeout(connectTimeout.toMillis(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (readTimeout != null) {
|
||||
builder.readTimeout(readTimeout.toMillis(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (writeTimeout != null) {
|
||||
builder.writeTimeout(writeTimeout.toMillis(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
if (proxy != null) {
|
||||
builder.proxy(proxy);
|
||||
}
|
||||
if (proxySelector != null) {
|
||||
builder.proxySelector(proxySelector);
|
||||
}
|
||||
if (proxyAuthenticator != null) {
|
||||
builder.proxyAuthenticator(proxyAuthenticator);
|
||||
}
|
||||
if (hostnameVerifier != null) {
|
||||
builder.hostnameVerifier(hostnameVerifier);
|
||||
}
|
||||
if (sslSocketFactory != null && trustManager != null) {
|
||||
builder.sslSocketFactory(sslSocketFactory, trustManager);
|
||||
}
|
||||
if (Boolean.TRUE.equals(disableSslValidation)) {
|
||||
disableSslValidation(builder);
|
||||
}
|
||||
if (authenticator != null) {
|
||||
builder.authenticator(authenticator);
|
||||
}
|
||||
if (!interceptors.isEmpty()) {
|
||||
builder.interceptors().addAll(interceptors);
|
||||
}
|
||||
if (cookieJar != null) {
|
||||
builder.cookieJar(cookieJar);
|
||||
}
|
||||
if (eventListener != null) {
|
||||
builder.eventListener(eventListener);
|
||||
}
|
||||
if (followRedirects != null) {
|
||||
builder.followRedirects(followRedirects);
|
||||
}
|
||||
if (followSslRedirects != null) {
|
||||
builder.followSslRedirects(followSslRedirects);
|
||||
}
|
||||
if (retryPolicy != null) {
|
||||
builder.addInterceptor(new RetryInterceptor(retryPolicy));
|
||||
}
|
||||
if (level != null && HttpLoggingInterceptor.Level.NONE != level) {
|
||||
builder.addInterceptor(getLoggingInterceptor(level));
|
||||
} else if (globalLoggingInterceptor != null) {
|
||||
builder.addInterceptor(globalLoggingInterceptor);
|
||||
}
|
||||
// 设置 User-Agent
|
||||
requestBuilder.header("User-Agent", userAgent);
|
||||
// url
|
||||
requestBuilder.url(uriBuilder.build());
|
||||
String method = httpMethod;
|
||||
Request request;
|
||||
if (HttpMethod.requiresRequestBody(method) && requestBody == null) {
|
||||
request = requestBuilder.method(method, Util.EMPTY_REQUEST).build();
|
||||
} else {
|
||||
request = requestBuilder.method(method, requestBody).build();
|
||||
}
|
||||
return builder.build().newCall(request);
|
||||
}
|
||||
|
||||
public Exchange execute() {
|
||||
return new Exchange(internalCall(httpClient));
|
||||
}
|
||||
|
||||
public AsyncCall async() {
|
||||
return new AsyncCall(internalCall(httpClient));
|
||||
}
|
||||
|
||||
public HttpRequest baseAuth(String userName, String password) {
|
||||
this.authenticator = new BaseAuthenticator(userName, password);
|
||||
return this;
|
||||
}
|
||||
|
||||
//// HTTP header operations
|
||||
public HttpRequest addHeader(final Map<String, String> headers) {
|
||||
this.requestBuilder.headers(Headers.of(headers));
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest addHeader(final String... namesAndValues) {
|
||||
Headers headers = Headers.of(namesAndValues);
|
||||
this.requestBuilder.headers(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest addHeader(final String name, final String value) {
|
||||
this.requestBuilder.addHeader(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest setHeader(final String name, final String value) {
|
||||
this.requestBuilder.header(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest removeHeader(final String name) {
|
||||
this.requestBuilder.removeHeader(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest addCookie(final Cookie cookie) {
|
||||
this.addHeader("Cookie", cookie.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest cacheControl(final CacheControl cacheControl) {
|
||||
this.requestBuilder.cacheControl(cacheControl);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest userAgent(final String userAgent) {
|
||||
this.userAgent = userAgent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest followRedirects(boolean followRedirects) {
|
||||
this.followRedirects = followRedirects;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest followSslRedirects(boolean followSslRedirects) {
|
||||
this.followSslRedirects = followSslRedirects;
|
||||
return this;
|
||||
}
|
||||
|
||||
private static HttpLoggingInterceptor getLoggingInterceptor(HttpLoggingInterceptor.Level level) {
|
||||
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(Slf4jLogger.INSTANCE);
|
||||
loggingInterceptor.setLevel(level);
|
||||
return loggingInterceptor;
|
||||
}
|
||||
|
||||
public HttpRequest log() {
|
||||
this.level = HttpLoggingInterceptor.Level.BODY;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest log(LogLevel logLevel) {
|
||||
this.level = logLevel.getLevel();
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest authenticator(Authenticator authenticator) {
|
||||
this.authenticator = authenticator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest interceptor(Interceptor interceptor) {
|
||||
this.interceptors.add(interceptor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest cookieManager(CookieJar cookieJar) {
|
||||
this.cookieJar = cookieJar;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest eventListener(EventListener eventListener) {
|
||||
this.eventListener = eventListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
//// HTTP connection parameter operations
|
||||
public HttpRequest connectTimeout(final Duration timeout) {
|
||||
this.connectTimeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest readTimeout(Duration readTimeout) {
|
||||
this.readTimeout = readTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest writeTimeout(Duration writeTimeout) {
|
||||
this.writeTimeout = writeTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest proxy(final InetSocketAddress address) {
|
||||
this.proxy = new Proxy(Proxy.Type.HTTP, address);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest proxySelector(final ProxySelector proxySelector) {
|
||||
this.proxySelector = proxySelector;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest proxyAuthenticator(final Authenticator proxyAuthenticator) {
|
||||
this.proxyAuthenticator = proxyAuthenticator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest retry() {
|
||||
this.retryPolicy = RetryPolicy.INSTANCE;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest retryOn(Predicate<ResponseSpec> respPredicate) {
|
||||
this.retryPolicy = new RetryPolicy(respPredicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest retry(int maxAttempts, long sleepMillis) {
|
||||
this.retryPolicy = new RetryPolicy(maxAttempts, sleepMillis);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest retry(int maxAttempts, long sleepMillis, Predicate<ResponseSpec> respPredicate) {
|
||||
this.retryPolicy = new RetryPolicy(maxAttempts, sleepMillis);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭 ssl 校验
|
||||
*
|
||||
* @return HttpRequest
|
||||
*/
|
||||
public HttpRequest disableSslValidation() {
|
||||
this.disableSslValidation = Boolean.TRUE;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest hostnameVerifier(HostnameVerifier hostnameVerifier) {
|
||||
this.hostnameVerifier = hostnameVerifier;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) {
|
||||
this.sslSocketFactory = sslSocketFactory;
|
||||
this.trustManager = trustManager;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return requestBuilder.toString();
|
||||
}
|
||||
|
||||
public static void setHttpClient(OkHttpClient httpClient) {
|
||||
HttpRequest.httpClient = httpClient;
|
||||
}
|
||||
|
||||
public static void setGlobalLog(LogLevel logLevel) {
|
||||
HttpRequest.globalLoggingInterceptor = getLoggingInterceptor(logLevel.getLevel());
|
||||
}
|
||||
|
||||
static String handleValue(@Nullable Object value) {
|
||||
if (value == null) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
if (value instanceof String) {
|
||||
return (String) value;
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
private static void disableSslValidation(OkHttpClient.Builder builder) {
|
||||
try {
|
||||
X509TrustManager disabledTrustManager = DisableValidationTrustManager.INSTANCE;
|
||||
TrustManager[] trustManagers = new TrustManager[]{disabledTrustManager};
|
||||
SSLContext sslContext = SSLContext.getInstance("SSL");
|
||||
sslContext.init(null, trustManagers, Holder.SECURE_RANDOM);
|
||||
SSLSocketFactory disabledSslSocketFactory = sslContext.getSocketFactory();
|
||||
builder.sslSocketFactory(disabledSslSocketFactory, disabledTrustManager);
|
||||
builder.hostnameVerifier(TrustAllHostNames.INSTANCE);
|
||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import okhttp3.*;
|
||||
import okhttp3.internal.Util;
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springblade.core.tool.utils.Exceptions;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* ok http 封装,相应结构体
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class HttpResponse implements ResponseSpec, Closeable {
|
||||
private final Request request;
|
||||
private final Response response;
|
||||
private final ResponseBody body;
|
||||
|
||||
HttpResponse(final Response response) {
|
||||
this.request = response.request();
|
||||
this.response = response;
|
||||
this.body = ifNullBodyToEmpty(response.body());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int code() {
|
||||
return response.code();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String message() {
|
||||
return response.message();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOk() {
|
||||
return response.isSuccessful();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRedirect() {
|
||||
return response.isRedirect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers headers() {
|
||||
return response.headers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Cookie> cookies() {
|
||||
return Cookie.parseAll(request.url(), this.headers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request rawRequest() {
|
||||
return this.request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response rawResponse() {
|
||||
return this.response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseBody rawBody() {
|
||||
return this.body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asString() {
|
||||
try {
|
||||
return body.string();
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] asBytes() {
|
||||
try {
|
||||
return body.bytes();
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream asStream() {
|
||||
return body.byteStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode asJsonNode() {
|
||||
return JsonUtil.readTree(asBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T asValue(Class<T> valueType) {
|
||||
return JsonUtil.readValue(asBytes(), valueType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T asValue(TypeReference<T> typeReference) {
|
||||
return JsonUtil.readValue(asBytes(), typeReference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> asList(Class<T> valueType) {
|
||||
return JsonUtil.readList(asBytes(), valueType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> Map<K, V> asMap(Class<?> keyClass, Class<?> valueType) {
|
||||
return JsonUtil.readMap(asBytes(), keyClass, valueType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Map<String, V> asMap(Class<?> valueType) {
|
||||
return this.asMap(String.class, valueType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T asDomValue(Class<T> valueType) {
|
||||
return DomMapper.readValue(this.asStream(), valueType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> asDomList(Class<T> valueType) {
|
||||
return DomMapper.readList(this.asStream(), valueType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File toFile(File file) {
|
||||
toFile(file.toPath());
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path toFile(Path path) {
|
||||
try {
|
||||
Files.copy(this.asStream(), path);
|
||||
return path;
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return body.contentType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long contentLength() {
|
||||
return body.contentLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return response.toString();
|
||||
}
|
||||
|
||||
private static ResponseBody ifNullBodyToEmpty(@Nullable ResponseBody body) {
|
||||
return body == null ? Util.EMPTY_RESPONSE : body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
Util.closeQuietly(this.body);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
|
||||
/**
|
||||
* 日志级别
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum LogLevel {
|
||||
/**
|
||||
* No logs.
|
||||
*/
|
||||
NONE(HttpLoggingInterceptor.Level.NONE),
|
||||
/**
|
||||
* Logs request and response lines.
|
||||
*
|
||||
* <p>Example:
|
||||
* <pre>{@code
|
||||
* --> POST /greeting http/1.1 (3-byte body)
|
||||
*
|
||||
* <-- 200 OK (22ms, 6-byte body)
|
||||
* }</pre>
|
||||
*/
|
||||
BASIC(HttpLoggingInterceptor.Level.BASIC),
|
||||
/**
|
||||
* Logs request and response lines and their respective headers.
|
||||
*
|
||||
* <p>Example:
|
||||
* <pre>{@code
|
||||
* --> POST /greeting http/1.1
|
||||
* Host: example.com
|
||||
* Content-Type: plain/text
|
||||
* Content-Length: 3
|
||||
* --> END POST
|
||||
*
|
||||
* <-- 200 OK (22ms)
|
||||
* Content-Type: plain/text
|
||||
* Content-Length: 6
|
||||
* <-- END HTTP
|
||||
* }</pre>
|
||||
*/
|
||||
HEADERS(HttpLoggingInterceptor.Level.HEADERS),
|
||||
/**
|
||||
* Logs request and response lines and their respective headers and bodies (if present).
|
||||
*
|
||||
* <p>Example:
|
||||
* <pre>{@code
|
||||
* --> POST /greeting http/1.1
|
||||
* Host: example.com
|
||||
* Content-Type: plain/text
|
||||
* Content-Length: 3
|
||||
*
|
||||
* Hi?
|
||||
* --> END POST
|
||||
*
|
||||
* <-- 200 OK (22ms)
|
||||
* Content-Type: plain/text
|
||||
* Content-Length: 6
|
||||
*
|
||||
* Hello!
|
||||
* <-- END HTTP
|
||||
* }</pre>
|
||||
*/
|
||||
BODY(HttpLoggingInterceptor.Level.BODY);
|
||||
|
||||
private final HttpLoggingInterceptor.Level level;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.http;
|
||||
|
||||
/**
|
||||
* http method
|
||||
*
|
||||
* @author dream.lu
|
||||
*/
|
||||
public interface Method {
|
||||
String GET = "GET";
|
||||
String POST = "POST";
|
||||
String PATCH = "PATCH";
|
||||
String PUT = "PUT";
|
||||
String DELETE = "DELETE";
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 表单构造器
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MultipartFormBuilder {
|
||||
private final HttpRequest request;
|
||||
private final MultipartBody.Builder formBuilder;
|
||||
|
||||
MultipartFormBuilder(HttpRequest request) {
|
||||
this.request = request;
|
||||
this.formBuilder = new MultipartBody.Builder();
|
||||
}
|
||||
|
||||
public MultipartFormBuilder add(String name, @Nullable Object value) {
|
||||
this.formBuilder.addFormDataPart(name, HttpRequest.handleValue(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public MultipartFormBuilder addMap(@Nullable Map<String, Object> formMap) {
|
||||
if (formMap != null && !formMap.isEmpty()) {
|
||||
formMap.forEach(this::add);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public MultipartFormBuilder add(String name, File file) {
|
||||
String fileName = file.getName();
|
||||
return add(name, fileName, file);
|
||||
}
|
||||
|
||||
public MultipartFormBuilder add(String name, @Nullable String filename, File file) {
|
||||
RequestBody fileBody = RequestBody.create(null, file);
|
||||
return add(name, filename, fileBody);
|
||||
}
|
||||
|
||||
public MultipartFormBuilder add(String name, @Nullable String filename, RequestBody fileBody) {
|
||||
this.formBuilder.addFormDataPart(name, filename, fileBody);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MultipartFormBuilder add(RequestBody body) {
|
||||
this.formBuilder.addPart(body);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MultipartFormBuilder add(@Nullable Headers headers, RequestBody body) {
|
||||
this.formBuilder.addPart(headers, body);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MultipartFormBuilder add(MultipartBody.Part part) {
|
||||
this.formBuilder.addPart(part);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest build() {
|
||||
formBuilder.setType(MultipartBody.FORM);
|
||||
MultipartBody formBody = formBuilder.build();
|
||||
this.request.multipartForm(formBody);
|
||||
return this.request;
|
||||
}
|
||||
|
||||
public Exchange execute() {
|
||||
return this.build().execute();
|
||||
}
|
||||
|
||||
public AsyncCall async() {
|
||||
return this.build().async();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import okhttp3.*;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 相应接口
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public interface ResponseSpec {
|
||||
|
||||
/**
|
||||
* Returns the HTTP code.
|
||||
*
|
||||
* @return code
|
||||
*/
|
||||
int code();
|
||||
|
||||
/**
|
||||
* Returns the HTTP status message.
|
||||
*
|
||||
* @return message
|
||||
*/
|
||||
String message();
|
||||
|
||||
/**
|
||||
* Returns the HTTP isSuccessful.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
default boolean isOk() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the is Redirect.
|
||||
*
|
||||
* @return is Redirect
|
||||
*/
|
||||
boolean isRedirect();
|
||||
|
||||
/**
|
||||
* Returns the Headers.
|
||||
*
|
||||
* @return Headers
|
||||
*/
|
||||
Headers headers();
|
||||
|
||||
/**
|
||||
* Headers Consumer.
|
||||
*
|
||||
* @param consumer Consumer
|
||||
* @return Headers
|
||||
*/
|
||||
default ResponseSpec headers(Consumer<Headers> consumer) {
|
||||
consumer.accept(this.headers());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Cookies.
|
||||
*
|
||||
* @return Cookie List
|
||||
*/
|
||||
List<Cookie> cookies();
|
||||
|
||||
/**
|
||||
* 读取消费 cookie
|
||||
*
|
||||
* @param consumer Consumer
|
||||
* @return ResponseSpec
|
||||
*/
|
||||
default ResponseSpec cookies(Consumer<List<Cookie>> consumer) {
|
||||
consumer.accept(this.cookies());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns body String.
|
||||
*
|
||||
* @return body String
|
||||
*/
|
||||
String asString();
|
||||
|
||||
/**
|
||||
* Returns body to byte arrays.
|
||||
*
|
||||
* @return byte arrays
|
||||
*/
|
||||
byte[] asBytes();
|
||||
|
||||
/**
|
||||
* Returns body to InputStream.
|
||||
*
|
||||
* @return InputStream
|
||||
*/
|
||||
InputStream asStream();
|
||||
|
||||
/**
|
||||
* Returns body to JsonNode.
|
||||
*
|
||||
* @return JsonNode
|
||||
*/
|
||||
JsonNode asJsonNode();
|
||||
|
||||
/**
|
||||
* Returns body to Object.
|
||||
*
|
||||
* @param valueType value value type
|
||||
* @return Object
|
||||
*/
|
||||
@Nullable
|
||||
<T> T asValue(Class<T> valueType);
|
||||
|
||||
/**
|
||||
* Returns body to Object.
|
||||
*
|
||||
* @param typeReference value Type Reference
|
||||
* @return Object
|
||||
*/
|
||||
@Nullable
|
||||
<T> T asValue(TypeReference<T> typeReference);
|
||||
|
||||
/**
|
||||
* Returns body to List.
|
||||
*
|
||||
* @param valueType value type
|
||||
* @return List
|
||||
*/
|
||||
<T> List<T> asList(Class<T> valueType);
|
||||
|
||||
/**
|
||||
* Returns body to Map.
|
||||
*
|
||||
* @param keyClass key type
|
||||
* @param valueType value type
|
||||
* @return Map
|
||||
*/
|
||||
<K, V> Map<K, V> asMap(Class<?> keyClass, Class<?> valueType);
|
||||
|
||||
/**
|
||||
* Returns body to Map.
|
||||
*
|
||||
* @param valueType value 类型
|
||||
* @return Map
|
||||
*/
|
||||
<V> Map<String, V> asMap(Class<?> valueType);
|
||||
|
||||
/**
|
||||
* 将 xml、heml 转成对象
|
||||
*
|
||||
* @param valueType 对象类
|
||||
* @param <T> 泛型
|
||||
* @return 对象
|
||||
*/
|
||||
<T> T asDomValue(Class<T> valueType);
|
||||
|
||||
/**
|
||||
* 将 xml、heml 转成对象
|
||||
*
|
||||
* @param valueType 对象类
|
||||
* @param <T> 泛型
|
||||
* @return 对象集合
|
||||
*/
|
||||
<T> List<T> asDomList(Class<T> valueType);
|
||||
|
||||
/**
|
||||
* toFile.
|
||||
*
|
||||
* @param file File
|
||||
*/
|
||||
File toFile(File file);
|
||||
|
||||
/**
|
||||
* toFile.
|
||||
*
|
||||
* @param path Path
|
||||
*/
|
||||
Path toFile(Path path);
|
||||
|
||||
/**
|
||||
* Returns contentType.
|
||||
*
|
||||
* @return contentType
|
||||
*/
|
||||
@Nullable
|
||||
MediaType contentType();
|
||||
|
||||
/**
|
||||
* Returns contentLength.
|
||||
*
|
||||
* @return contentLength
|
||||
*/
|
||||
long contentLength();
|
||||
|
||||
/**
|
||||
* Returns rawRequest.
|
||||
*
|
||||
* @return Request
|
||||
*/
|
||||
Request rawRequest();
|
||||
|
||||
/**
|
||||
* rawRequest Consumer.
|
||||
*
|
||||
* @param consumer Consumer
|
||||
* @return ResponseSpec
|
||||
*/
|
||||
@Nullable
|
||||
default ResponseSpec rawRequest(Consumer<Request> consumer) {
|
||||
consumer.accept(this.rawRequest());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns rawResponse.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
Response rawResponse();
|
||||
|
||||
/**
|
||||
* rawResponse Consumer.
|
||||
*
|
||||
* @param consumer Consumer
|
||||
* @return Response
|
||||
*/
|
||||
default ResponseSpec rawResponse(Consumer<Response> consumer) {
|
||||
consumer.accept(this.rawResponse());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns rawBody.
|
||||
*
|
||||
* @return ResponseBody
|
||||
*/
|
||||
@Nullable
|
||||
ResponseBody rawBody();
|
||||
|
||||
/**
|
||||
* rawBody Consumer.
|
||||
*
|
||||
* @param consumer Consumer
|
||||
* @return ResponseBody
|
||||
*/
|
||||
@Nullable
|
||||
default ResponseSpec rawBody(Consumer<ResponseBody> consumer) {
|
||||
consumer.accept(this.rawBody());
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
import org.springframework.retry.backoff.FixedBackOffPolicy;
|
||||
import org.springframework.retry.policy.SimpleRetryPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* 重试拦截器,应对代理问题
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class RetryInterceptor implements Interceptor {
|
||||
private final RetryPolicy retryPolicy;
|
||||
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request request = chain.request();
|
||||
RetryTemplate template = createRetryTemplate(retryPolicy);
|
||||
return template.execute(context -> {
|
||||
Response response = chain.proceed(request);
|
||||
// 结果集校验
|
||||
Predicate<ResponseSpec> respPredicate = retryPolicy.getRespPredicate();
|
||||
if (respPredicate == null) {
|
||||
return response;
|
||||
}
|
||||
// copy 一份 body
|
||||
ResponseBody body = response.peekBody(Long.MAX_VALUE);
|
||||
try (HttpResponse httpResponse = new HttpResponse(response)) {
|
||||
if (respPredicate.test(httpResponse)) {
|
||||
throw new IOException("Http Retry ResponsePredicate test Failure.");
|
||||
}
|
||||
}
|
||||
return response.newBuilder().body(body).build();
|
||||
});
|
||||
}
|
||||
|
||||
private static RetryTemplate createRetryTemplate(RetryPolicy policy) {
|
||||
RetryTemplate template = new RetryTemplate();
|
||||
// 重试策略
|
||||
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
|
||||
retryPolicy.setMaxAttempts(policy.getMaxAttempts());
|
||||
// 设置间隔策略
|
||||
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
|
||||
backOffPolicy.setBackOffPeriod(policy.getSleepMillis());
|
||||
template.setRetryPolicy(retryPolicy);
|
||||
template.setBackOffPolicy(backOffPolicy);
|
||||
return template;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import org.springframework.retry.policy.SimpleRetryPolicy;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* 重试策略
|
||||
*
|
||||
* @author dream.lu
|
||||
*/
|
||||
@Getter
|
||||
@ToString
|
||||
public class RetryPolicy {
|
||||
public static final RetryPolicy INSTANCE = new RetryPolicy();
|
||||
|
||||
private final int maxAttempts;
|
||||
private final long sleepMillis;
|
||||
@Nullable
|
||||
private final Predicate<ResponseSpec> respPredicate;
|
||||
|
||||
public RetryPolicy() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public RetryPolicy(int maxAttempts, long sleepMillis) {
|
||||
this(maxAttempts, sleepMillis, null);
|
||||
}
|
||||
|
||||
public RetryPolicy(@Nullable Predicate<ResponseSpec> respPredicate) {
|
||||
this(SimpleRetryPolicy.DEFAULT_MAX_ATTEMPTS, 0L, respPredicate);
|
||||
}
|
||||
|
||||
public RetryPolicy(int maxAttempts, long sleepMillis, @Nullable Predicate<ResponseSpec> respPredicate) {
|
||||
this.maxAttempts = maxAttempts;
|
||||
this.sleepMillis = sleepMillis;
|
||||
this.respPredicate = respPredicate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
|
||||
/**
|
||||
* OkHttp Slf4j logger
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
public class Slf4jLogger implements HttpLoggingInterceptor.Logger {
|
||||
|
||||
public static final HttpLoggingInterceptor.Logger INSTANCE = new Slf4jLogger();
|
||||
|
||||
@Override
|
||||
public void log(String message) {
|
||||
log.info(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.http.Exchange;
|
||||
import org.springblade.core.http.FormBuilder;
|
||||
import org.springblade.core.http.HttpRequest;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Http请求工具类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
public class HttpUtil {
|
||||
|
||||
/**
|
||||
* GET
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param queries 请求的参数,在浏览器?后面的数据,没有可以传null
|
||||
* @return String
|
||||
*/
|
||||
public static String get(String url, Map<String, Object> queries) {
|
||||
return get(url, null, queries);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param header 请求头
|
||||
* @param queries 请求的参数,在浏览器?后面的数据,没有可以传null
|
||||
* @return String
|
||||
*/
|
||||
public static String get(String url, Map<String, String> header, Map<String, Object> queries) {
|
||||
// 添加请求头
|
||||
HttpRequest httpRequest = HttpRequest.get(url);
|
||||
if (header != null && !header.keySet().isEmpty()) {
|
||||
header.forEach(httpRequest::addHeader);
|
||||
}
|
||||
// 添加参数
|
||||
httpRequest.queryMap(queries);
|
||||
return httpRequest.execute().asString();
|
||||
}
|
||||
|
||||
/**
|
||||
* POST
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param params post form 提交的参数
|
||||
* @return String
|
||||
*/
|
||||
public static String post(String url, Map<String, Object> params) {
|
||||
return exchange(url, null, params).asString();
|
||||
}
|
||||
|
||||
/**
|
||||
* POST
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param header 请求头
|
||||
* @param params post form 提交的参数
|
||||
* @return String
|
||||
*/
|
||||
public static String post(String url, Map<String, String> header, Map<String, Object> params) {
|
||||
return exchange(url, header, params).asString();
|
||||
}
|
||||
|
||||
/**
|
||||
* POST请求发送JSON数据
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param json 请求的json串
|
||||
* @return String
|
||||
*/
|
||||
public static String postJson(String url, String json) {
|
||||
return exchange(url, null, json).asString();
|
||||
}
|
||||
|
||||
/**
|
||||
* POST请求发送JSON数据
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param header 请求头
|
||||
* @param json 请求的json串
|
||||
* @return String
|
||||
*/
|
||||
public static String postJson(String url, Map<String, String> header, String json) {
|
||||
return exchange(url, header, json).asString();
|
||||
}
|
||||
|
||||
public static Exchange exchange(String url, Map<String, String> header, Map<String, Object> params) {
|
||||
HttpRequest httpRequest = HttpRequest.post(ensureHttpUrl(url));
|
||||
//添加请求头
|
||||
if (header != null && !header.keySet().isEmpty()) {
|
||||
header.forEach(httpRequest::addHeader);
|
||||
}
|
||||
FormBuilder formBuilder = httpRequest.formBuilder();
|
||||
//添加参数
|
||||
if (params != null && !params.keySet().isEmpty()) {
|
||||
params.forEach(formBuilder::add);
|
||||
}
|
||||
return formBuilder.execute();
|
||||
}
|
||||
|
||||
public static Exchange exchange(String url, Map<String, String> header, String content) {
|
||||
HttpRequest httpRequest = HttpRequest.post(ensureHttpUrl(url));
|
||||
//添加请求头
|
||||
if (header != null && !header.keySet().isEmpty()) {
|
||||
header.forEach(httpRequest::addHeader);
|
||||
}
|
||||
return httpRequest.bodyString(content).execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保URL具有http或https协议头
|
||||
*
|
||||
* @param url 要处理的URL字符串
|
||||
* @return 处理后的URL字符串
|
||||
*/
|
||||
public static String ensureHttpUrl(String url) {
|
||||
if (url == null || url.isEmpty()) {
|
||||
return url;
|
||||
}
|
||||
if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://")) {
|
||||
return "http://" + url;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http.test;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 代理设置
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
public class BladeProxySelector extends ProxySelector {
|
||||
|
||||
@Override
|
||||
public List<Proxy> select(URI uri) {
|
||||
// 注意代理都不可用
|
||||
List<Proxy> proxyList = new ArrayList<>();
|
||||
proxyList.add(getProxy("127.0.0.1", 8080));
|
||||
proxyList.add(getProxy("127.0.0.1", 8081));
|
||||
proxyList.add(getProxy("127.0.0.1", 8082));
|
||||
proxyList.add(getProxy("127.0.0.1", 3128));
|
||||
return proxyList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectFailed(URI uri, SocketAddress address, IOException ioe) {
|
||||
// 注意:经过测试,此处不会触发
|
||||
log.error("ConnectFailed uri:{}, address:{}, ioe:{}", uri, address, ioe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造 Proxy
|
||||
*
|
||||
* @param host host
|
||||
* @param port 端口
|
||||
* @return Proxy 对象
|
||||
*/
|
||||
public static Proxy getProxy(String host, int port) {
|
||||
return new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(host, port));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http.test;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.springblade.core.http.HttpRequest;
|
||||
import org.springblade.core.http.LogLevel;
|
||||
import org.springblade.core.http.ResponseSpec;
|
||||
import okhttp3.Cookie;
|
||||
import org.springblade.core.tool.utils.Base64Util;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* This example of blade http
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class HttpRequestDemo {
|
||||
|
||||
public void doc() {
|
||||
// 设定全局日志级别 NONE,BASIC,HEADERS,BODY, 默认:NONE
|
||||
HttpRequest.setGlobalLog(LogLevel.BODY);
|
||||
|
||||
// 同步请求 url,方法支持 get、post、patch、put、delete
|
||||
HttpRequest.get("https://www.baidu.com")
|
||||
.log(LogLevel.BASIC) //设定本次的日志级别,优先于全局
|
||||
.addHeader("x-account-id", "blade001") // 添加 header
|
||||
.addCookie(new Cookie.Builder() // 添加 cookie
|
||||
.name("sid")
|
||||
.value("blade_user_001")
|
||||
.build()
|
||||
)
|
||||
.query("q", "blade") //设置 url 参数,默认进行 url encode
|
||||
.queryEncoded("name", "encodedValue")
|
||||
.formBuilder() // 表单构造器,同类 multipartFormBuilder 文件上传表单
|
||||
.add("id", 123123) // 表单参数
|
||||
.execute()// 发起请求
|
||||
.asJsonNode();
|
||||
// 结果集转换,注:如果网络异常等会直接抛出异常。
|
||||
// 同类的方法有 asString、asBytes
|
||||
// json 类响应:asJsonNode、asObject、asList、asMap,采用 jackson 处理
|
||||
// xml、html响应:asDocument,采用的 jsoup 处理
|
||||
// file 文件:toFile
|
||||
|
||||
// 同步
|
||||
String html = HttpRequest.post("https://www.baidu.com")
|
||||
.execute()
|
||||
.onSuccess(ResponseSpec::asString);// 处理响应,有网络异常等直接返回 null
|
||||
|
||||
// 同步
|
||||
String text = HttpRequest.patch("https://www.baidu.com")
|
||||
.execute()
|
||||
.onSuccess(ResponseSpec::asString);
|
||||
// onSuccess http code in [200..300) 处理响应,有网络异常等直接返回 null
|
||||
|
||||
// 发送异步请求
|
||||
HttpRequest.delete("https://www.baidu.com")
|
||||
.async() // 开启异步
|
||||
.onFailed((request, e) -> { // 异常时的处理
|
||||
e.printStackTrace();
|
||||
})
|
||||
.onSuccessful(responseSpec -> { // 消费响应成功 http code in [200..300)
|
||||
// 注意:响应结果流只能读一次
|
||||
JsonNode jsonNode = responseSpec.asJsonNode();
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 设定全局日志级别
|
||||
HttpRequest.setGlobalLog(LogLevel.BODY);
|
||||
|
||||
// 同步,异常时 返回 null
|
||||
String html = HttpRequest.get("https://www.baidu.com")
|
||||
.connectTimeout(Duration.ofSeconds(1000))
|
||||
.query("test", "a")
|
||||
.query("name", "張三")
|
||||
.query("x", 1)
|
||||
.query("abd", Base64Util.encode("123&$#%"))
|
||||
.queryEncoded("abc", Base64Util.encode("123&$#%"))
|
||||
.execute()
|
||||
.onFailed(((request, e) -> {
|
||||
e.printStackTrace();
|
||||
}))
|
||||
.onSuccess(ResponseSpec::asString);
|
||||
System.out.println(html);
|
||||
|
||||
// 同步调用,返回 Optional,异常时返回 Optional.empty()
|
||||
Optional<String> opt = HttpRequest.post(URI.create("https://www.baidu.com"))
|
||||
.bodyString("Important stuff")
|
||||
.formBuilder()
|
||||
.add("a", "b")
|
||||
.execute()
|
||||
.onSuccessOpt(ResponseSpec::asString);
|
||||
|
||||
// 同步,成功时消费(处理) response
|
||||
HttpRequest.post("https://www.baidu.com/some-form")
|
||||
.addHeader("X-Custom-header", "stuff")
|
||||
.execute()
|
||||
.onFailed((request, e) -> {
|
||||
e.printStackTrace();
|
||||
})
|
||||
.onSuccessful(ResponseSpec::asString);
|
||||
|
||||
// async,异步执行结果,失败时打印堆栈
|
||||
HttpRequest.get("https://www.baidu.com/some-form")
|
||||
.async()
|
||||
.onFailed((request, e) -> {
|
||||
e.printStackTrace();
|
||||
})
|
||||
.onSuccessful(System.out::println);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.http.test;
|
||||
|
||||
import org.springblade.core.http.HttpRequest;
|
||||
|
||||
public class HttpRequestProxyTest {
|
||||
|
||||
//@Test(expected = IOException.class)
|
||||
public void proxy() {
|
||||
// 代理都不可用
|
||||
HttpRequest.get("https://www.baidu.com")
|
||||
.log()
|
||||
.retry()
|
||||
.proxySelector(new BladeProxySelector())
|
||||
.execute()
|
||||
.asString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.springblade.core.http.test;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springblade.core.http.CssQuery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class OsChina {
|
||||
|
||||
@CssQuery(value = "head > title", attr = "text")
|
||||
private String title;
|
||||
|
||||
@CssQuery(value = "#v_news .page .news", inner = true)
|
||||
private List<VNews> vNews;
|
||||
|
||||
@CssQuery(value = ".blog-container .blog-list div", inner = true)
|
||||
private List<VBlog> vBlogList;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.springblade.core.http.test;
|
||||
|
||||
import org.springblade.core.http.HttpRequest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class OsChinaTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 同步,异常返回 null
|
||||
OsChina oschina = HttpRequest.get("https://www.oschina.net")
|
||||
.execute()
|
||||
.onSuccess(responseSpec -> responseSpec.asDomValue(OsChina.class));
|
||||
if (oschina == null) {
|
||||
return;
|
||||
}
|
||||
System.out.println(oschina.getTitle());
|
||||
|
||||
System.out.println("热门新闻");
|
||||
|
||||
List<VNews> vNews = oschina.getVNews();
|
||||
for (VNews vNew : vNews) {
|
||||
System.out.println("title:\t" + vNew.getTitle());
|
||||
System.out.println("href:\t" + vNew.getHref());
|
||||
System.out.println("时间:\t" + vNew.getDate());
|
||||
}
|
||||
|
||||
System.out.println("热门博客");
|
||||
List<VBlog> vBlogList = oschina.getVBlogList();
|
||||
for (VBlog vBlog : vBlogList) {
|
||||
System.out.println("title:\t" + vBlog.getTitle());
|
||||
System.out.println("href:\t" + vBlog.getHref());
|
||||
System.out.println("阅读数:\t" + vBlog.getRead());
|
||||
System.out.println("评价数:\t" + vBlog.getPing());
|
||||
System.out.println("点赞数:\t" + vBlog.getZhan());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.springblade.core.http.test;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springblade.core.http.CssQuery;
|
||||
|
||||
/**
|
||||
* 热门博客
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class VBlog {
|
||||
|
||||
@CssQuery(value = "a", attr = "title")
|
||||
private String title;
|
||||
|
||||
@CssQuery(value = "a", attr = "href")
|
||||
private String href;
|
||||
|
||||
//1341阅/9评/4赞
|
||||
@CssQuery(value = "span", attr = "text", regex = "^\\d+")
|
||||
private Integer read;
|
||||
|
||||
@CssQuery(value = "span", attr = "text", regex = "(\\d*).*/(\\d*).*/(\\d*).*", regexGroup = 2)
|
||||
private Integer ping;
|
||||
|
||||
@CssQuery(value = "span", attr = "text", regex = "(\\d*).*/(\\d*).*/(\\d*).*", regexGroup = 3)
|
||||
private Integer zhan;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.springblade.core.http.test;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springblade.core.http.CssQuery;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class VNews {
|
||||
|
||||
@CssQuery(value = "a", attr = "title")
|
||||
private String title;
|
||||
|
||||
@CssQuery(value = "a", attr = "href")
|
||||
private String href;
|
||||
|
||||
@CssQuery(value = ".news-date", attr = "text")
|
||||
@DateTimeFormat(pattern = "MM/dd")
|
||||
private Date date;
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user