MeteorCat / Java转发请求库

Created Wed, 13 Dec 2023 12:48:54 +0800 Modified Wed, 29 Oct 2025 23:25:00 +0800
2552 Words

Java转发请求库

日常接入第三方需要转发 HTTP 请求, 这种在 SpringBoot 当中有个 RestTemplate 请求工具, 但是日常使用过程当中这个原始库编写十分麻烦, 所以不推荐直接集成编写.

这里推荐用 SpringBoot 内部另外的类库: webflux, 这里先引入下依赖:


<dependencies>
    <!-- HTTP转发请求 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

    <!-- Netty工具,依赖异步转发等工具 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-reactor-netty</artifactId>
    </dependency>
</dependencies>

之后就是编写具体请求库功能:

package com.app.fox.utils;

import com.fasterxml.jackson.databind.JsonNode;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import javax.net.ssl.SSLException;

import reactor.netty.http.client.HttpClient;

import java.time.Duration;
import java.util.Map;
import java.util.function.Consumer;

/**
 * 构建转发请求工具
 */
public class RequestUtils {

    /**
     * 将URL和参数构建 example.com?xxx=yyy 方式
     *
     * @param path   请求地址
     * @param params 请求参数
     * @return String
     */
    public static String getRequestComposePath(String path, MultiValueMap<String, String> params) {
        return params == null ? path : UriComponentsBuilder.fromHttpUrl(path).queryParams(params).toUriString();
    }


    /**
     * 构建忽略SSL证书请求
     *
     * @return WebClient
     */
    public static WebClient createIgnoreSslWebClient() {
        try {
            SslContext ssl = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
            HttpClient client = HttpClient.create().secure(t -> t.sslContext(ssl));
            return WebClient.builder().clientConnector(new ReactorClientHttpConnector(client)).build();
        } catch (SSLException exception) {
            throw new RuntimeException(exception);
        }
    }


    /**
     * 转发推送Get请求数据
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param clazz   响应类型数据
     * @param <T>     Mono 特化类型
     * @return T
     */
    public static <T> Mono<T> getRequestSend(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz) {
        String uri = getRequestComposePath(path, params);
        WebClient.RequestHeadersSpec<?> requestHeadersSpec = createIgnoreSslWebClient().get().uri(uri);
        if (headers != null) {
            headers.forEach(requestHeadersSpec::header);
        }
        return requestHeadersSpec.retrieve().bodyToMono(clazz);
    }

    /**
     * 同步推送Get请求
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param clazz   响应类型数据
     * @param <T>     Mono 特化类型
     * @return T
     */
    public static <T> T getRequestSync(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz) {
        Mono<T> response = getRequestSend(path, params, headers, clazz);
        return response.block();
    }

    /**
     * 同步推送Get请求 - 含超时: 秒
     *
     * @param path       请求路径
     * @param params     请求参数
     * @param headers    请求头
     * @param clazz      响应类型数据
     * @param timeoutSec 超时
     * @param <T>        Mono 特化类型
     * @return T
     */
    public static <T> T getRequestSync(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz, long timeoutSec) {
        Mono<T> response = getRequestSend(path, params, headers, clazz);
        return response.block(Duration.ofSeconds(timeoutSec));
    }


    /**
     * 异步推送Get请求
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param clazz   回调类型
     * @param cb      异步回调处理
     * @param <T>     回调类型
     */
    public static <T> void getRequest(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz, Consumer<T> cb) {
        Mono<T> response = getRequestSend(path, params, headers, clazz);
        response.subscribe(cb);
    }

    /**
     * 异步推送Get请求
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param cb      异步回调处理
     */
    public static void getRequestJson(String path, MultiValueMap<String, String> params, Map<String, String> headers, Consumer<JsonNode> cb) {
        getRequest(path, params, headers, JsonNode.class, cb);
    }


    // -------------------------------------------------------------------------------

    /**
     * 转发推送Post请求数据
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param clazz   响应类型数据
     * @param <T>     Mono 特化类型
     * @return T
     */
    public static <T> Mono<T> postRequestSend(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz) {
        String uri = getRequestComposePath(path, params);
        WebClient.RequestBodySpec requestBodySpec = createIgnoreSslWebClient().post().uri(uri);
        if (headers != null) {
            headers.forEach(requestBodySpec::header);
        }
        return requestBodySpec.body(BodyInserters.fromMultipartData(params)).retrieve().bodyToMono(clazz);
    }


    /**
     * 同步推送Post请求
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param clazz   响应类型数据
     * @param <T>     Mono 特化类型
     * @return T
     */
    public static <T> T postRequestSync(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz) {
        Mono<T> response = postRequestSend(path, params, headers, clazz);
        return response.block();
    }


    /**
     * 同步推送Post请求 - 含超时: 秒
     *
     * @param path       请求路径
     * @param params     请求参数
     * @param headers    请求头
     * @param clazz      响应类型数据
     * @param timeoutSec 超时
     * @param <T>        Mono 特化类型
     * @return T
     */
    public static <T> T postRequestSync(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz, long timeoutSec) {
        Mono<T> response = postRequestSend(path, params, headers, clazz);
        return response.block(Duration.ofSeconds(timeoutSec));
    }


    /**
     * 异步推送Post请求
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param clazz   回调类型
     * @param cb      异步回调处理
     * @param <T>     回调类型
     */
    public static <T> void postRequest(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz, Consumer<T> cb) {
        Mono<T> response = postRequestSend(path, params, headers, clazz);
        response.subscribe(cb);
    }

    /**
     * 异步推送Post请求
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param cb      异步回调处理
     */
    public static void postRequestJson(String path, MultiValueMap<String, String> params, Map<String, String> headers, Consumer<JsonNode> cb) {
        postRequest(path, params, headers, JsonNode.class, cb);
    }


    // -------------------------------------------------------------------------------


    /**
     * 转发推送Put请求数据
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param clazz   响应类型数据
     * @param <T>     Mono 特化类型
     * @return T
     */
    public static <T> Mono<T> putRequestSend(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz) {
        String uri = getRequestComposePath(path, params);
        WebClient.RequestBodySpec requestBodySpec = createIgnoreSslWebClient().put().uri(uri);
        if (headers != null) {
            headers.forEach(requestBodySpec::header);
        }
        return requestBodySpec.body(BodyInserters.fromMultipartData(params)).retrieve().bodyToMono(clazz);
    }


    /**
     * 同步推送Put请求
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param clazz   响应类型数据
     * @param <T>     Mono 特化类型
     * @return T
     */
    public static <T> T putRequestSync(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz) {
        Mono<T> response = putRequestSend(path, params, headers, clazz);
        return response.block();
    }


    /**
     * 同步推送Put请求 - 含超时: 秒
     *
     * @param path       请求路径
     * @param params     请求参数
     * @param headers    请求头
     * @param clazz      响应类型数据
     * @param timeoutSec 超时
     * @param <T>        Mono 特化类型
     * @return T
     */
    public static <T> T putRequestSync(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz, long timeoutSec) {
        Mono<T> response = putRequestSend(path, params, headers, clazz);
        return response.block(Duration.ofSeconds(timeoutSec));
    }


    /**
     * 异步推送Put请求
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param clazz   回调类型
     * @param cb      异步回调处理
     * @param <T>     回调类型
     */
    public static <T> void putRequest(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz, Consumer<T> cb) {
        Mono<T> response = putRequestSend(path, params, headers, clazz);
        response.subscribe(cb);
    }

    /**
     * 异步推送Put请求
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param cb      异步回调处理
     */
    public static void putRequestJson(String path, MultiValueMap<String, String> params, Map<String, String> headers, Consumer<JsonNode> cb) {
        putRequest(path, params, headers, JsonNode.class, cb);
    }


    // -------------------------------------------------------------------------------


    /**
     * 转发推送Patch请求数据
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param clazz   响应类型数据
     * @param <T>     Mono 特化类型
     * @return T
     */
    public static <T> Mono<T> patchRequestSend(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz) {
        String uri = getRequestComposePath(path, params);
        WebClient.RequestBodySpec requestBodySpec = createIgnoreSslWebClient().patch().uri(uri);
        if (headers != null) {
            headers.forEach(requestBodySpec::header);
        }
        return requestBodySpec.body(BodyInserters.fromMultipartData(params)).retrieve().bodyToMono(clazz);
    }


    /**
     * 同步推送Patch请求
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param clazz   响应类型数据
     * @param <T>     Mono 特化类型
     * @return T
     */
    public static <T> T patchRequestSync(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz) {
        Mono<T> response = patchRequestSend(path, params, headers, clazz);
        return response.block();
    }


    /**
     * 同步推送Patch请求 - 含超时: 秒
     *
     * @param path       请求路径
     * @param params     请求参数
     * @param headers    请求头
     * @param clazz      响应类型数据
     * @param timeoutSec 超时
     * @param <T>        Mono 特化类型
     * @return T
     */
    public static <T> T patchRequestSync(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz, long timeoutSec) {
        Mono<T> response = patchRequestSend(path, params, headers, clazz);
        return response.block(Duration.ofSeconds(timeoutSec));
    }


    /**
     * 异步推送Patch请求
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param clazz   回调类型
     * @param cb      异步回调处理
     * @param <T>     回调类型
     */
    public static <T> void patchRequest(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz, Consumer<T> cb) {
        Mono<T> response = patchRequestSend(path, params, headers, clazz);
        response.subscribe(cb);
    }

    /**
     * 异步推送Patch请求
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param cb      异步回调处理
     */
    public static void patchRequestJson(String path, MultiValueMap<String, String> params, Map<String, String> headers, Consumer<JsonNode> cb) {
        patchRequest(path, params, headers, JsonNode.class, cb);
    }


    // -------------------------------------------------------------------------------


    /**
     * 转发推送Delete请求数据
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param clazz   响应类型数据
     * @param <T>     Mono 特化类型
     * @return T
     */
    public static <T> Mono<T> deleteRequestSend(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz) {
        String uri = getRequestComposePath(path, params);
        WebClient.RequestHeadersSpec<?> requestHeadersSpec = createIgnoreSslWebClient().delete().uri(uri);
        if (headers != null) {
            headers.forEach(requestHeadersSpec::header);
        }
        return requestHeadersSpec.retrieve().bodyToMono(clazz);
    }

    /**
     * 同步推送Delete请求
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param clazz   响应类型数据
     * @param <T>     Mono 特化类型
     * @return T
     */
    public static <T> T deleteRequestSync(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz) {
        Mono<T> response = deleteRequestSend(path, params, headers, clazz);
        return response.block();
    }

    /**
     * 同步推送Delete请求 - 含超时: 秒
     *
     * @param path       请求路径
     * @param params     请求参数
     * @param headers    请求头
     * @param clazz      响应类型数据
     * @param timeoutSec 超时
     * @param <T>        Mono 特化类型
     * @return T
     */
    public static <T> T deleteRequestSync(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz, long timeoutSec) {
        Mono<T> response = deleteRequestSend(path, params, headers, clazz);
        return response.block(Duration.ofSeconds(timeoutSec));
    }


    /**
     * 异步推送Delete请求
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param clazz   回调类型
     * @param cb      异步回调处理
     * @param <T>     回调类型
     */
    public static <T> void deleteRequest(String path, MultiValueMap<String, String> params, Map<String, String> headers, Class<T> clazz, Consumer<T> cb) {
        Mono<T> response = deleteRequestSend(path, params, headers, clazz);
        response.subscribe(cb);
    }

    /**
     * 异步推送Delete请求
     *
     * @param path    请求路径
     * @param params  请求参数
     * @param headers 请求头
     * @param cb      异步回调处理
     */
    public static void deleteRequestJson(String path, MultiValueMap<String, String> params, Map<String, String> headers, Consumer<JsonNode> cb) {
        deleteRequest(path, params, headers, JsonNode.class, cb);
    }

}

这个请求组件支持 GET/POST/PUT/PATCH/DELETE 几个标准功能请求, 涵盖了日常会用到推送转发机制, 这里测试个样例:

package com.app.fox;

import com.app.fox.utils.JwtUtils;
import com.app.fox.utils.RequestUtils;
import com.fasterxml.jackson.databind.JsonNode;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;

@SpringBootTest
class FoxApplicationTests {


    @Test
    void request() {
        // 同步推送请求
        String url = "https://www.bing.com";
        String html = RequestUtils.getRequestSync(
                url,
                new LinkedMultiValueMap<>(0),
                null,
                String.class
        );
        System.out.println(html);

        // 异步推送请求
        RequestUtils.getRequest(
                url,
                new LinkedMultiValueMap<>(0),
                null,
                String.class,
                System.out::println
        );


        // 推送登录
        String bili = "https://passport.bilibili.com/x/passport-login/web/login";
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>() {{
            put("source", new LinkedList<>() {{
                add("main-fe-header");
            }});
            put("username", new LinkedList<>() {{
                add("meteorcat");
            }});
            put("password", new LinkedList<>() {{
                add("meteorcat");
            }});
        }};
        JsonNode node = RequestUtils.postRequestSync(
                bili,
                params,
                null,
                JsonNode.class
        );
        System.out.println(node);

        // 异步推送
        RequestUtils.postRequest(
                bili,
                params,
                null,
                JsonNode.class,
                System.out::println
        );

    }
}

这里就是完全支持同步和异步请求推送转发的HTTP库, 可以直接方便调用.