This commit is contained in:
2025-11-28 16:23:32 +08:00
commit a9e0e16c29
826 changed files with 89805 additions and 0 deletions

View 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>

View File

@@ -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);
}
}

View File

@@ -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()));
}
}
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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<>();
}
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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";
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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() {
// 设定全局日志级别 NONEBASICHEADERSBODY 默认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);
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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());
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}