Java程序如何发送携带Body参数的GET请求?

该文章已生成可运行项目,

问题描述

最近遇到一个接口文档,要求GET请求的参数放在Body里面。虽然很奇怪,但是确实可以返回数据,以下是postman跑出来的结果。

 首先要声明一点,GET请求携带Body参数是不遵守规范的。因此这接口文档是十分不专业的

但是没办法,博主需要去对接这份接口文档,获取相应的数据。于是在Java程序中,使用hutool的客户端进行测试,奇怪的事情发生了!

这是我的测试代码,版本是jdk11,hutool是5.8.1。

 大概逻辑就是创建一个请求方式为GET的HttpRequest,然后让这个http请求带上body参数,最后执行一下。

但是跑出来的结果让我感到很奇怪(结果如下图)。明明是相同的url和请求方式,携带的参数也是一样的。但是返回的结果却不一样!

(postman返回的结果是{"result": true,"progress": 100,"states": 3})

 这就非常奇怪了,然后我就去问我的mentor,过了一会他告诉我,用hutool的http客户端发送带Body参数的GET请求,会被强行改成POST请求。于是我又去postman,把请求方法改成POST,重新测试了一下。发现返回的结果居然和hutool的http客户端执行得到的结果一模一样!

 所以最终得出的结论就是:一个GET请求因为携带了Body参数,被强行修改为POST请求了!

到底是谁修改的呢?这个我还没得出确切的结论,可能是hutool的HttpClient在发送请求时修改了,也有可能是jdk修改的。

解决方法

那怎么才能在Java程序中发送一个带Body参数的GET请求呢?

这里提供两种方法。

方法一

既然在Java程序中用Http的客户端发送带Body参数的GET请求会被强行修改,那我就不用Java程序的Http客户端呗。我可以在Java程序中执行一个脚本,这个脚本的内容就是原生的curl命令,不就行了吗!

代码如下: 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;


public class HttpClient {
    public String executeCurlRequest() throws IOException, InterruptedException {
        // 构造 curl 命令
        List<String> command = new ArrayList<>();
        command.add("cmd.exe");
        command.add("/c");
        command.add("curl");
        command.add("-k");
        command.add("-X");
        command.add("GET");
        command.add("https://61.130.44.164/ipc/restful/mission");
        command.add("-s");
        command.add("-H");
        command.add("Content-Type: application/json");
        command.add("-d");
        command.add("{\\\"opcode\\\": 1,\\\"username\\\": \\\"admin\\\",\\\"password\\\": \\\"633193332f4133c5398af514af18ed8d\\\",\\\"seqUuid\\\": \\\"89d97ad8-4f93-47c6-b24b-d33a39f1638e\\\"}");

        // 使用 ProcessBuilder 执行命令
        ProcessBuilder processBuilder = new ProcessBuilder(command);
        processBuilder.redirectErrorStream(true); // 合并标准输出和错误输出
        // 启动进程
        Process process = processBuilder.start();
        // 读取命令输出
        StringBuilder output = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line).append("\n");
            }
        }
        // 等待进程结束并检查退出码
        int exitCode = process.waitFor();
        if (exitCode != 0) {
            throw new IOException("Curl command failed with exit code: " + exitCode + ", output: " + output);
        }
        return output.toString();
    }

    public static void main(String[] args) {
        try {
            String s = new HttpClient().executeCurlRequest();
            System.out.println(s);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

代码跑出来的结果如下:

 终于得到了正确的结果!但是这个方法太不优雅了,一想到要写原生的curl命令,就头疼。有没有更优雅的方法呢?当然有!但是这个方法不是我原创的哈,是我的mentor想出来的。请看方法二。

方法二

我刚开始尝试了很多成熟的Http客户端,包括hutool HttpClient、Spring RestTemplate、OkHttp以及java.net包提供的HttpClient。但是都没用,要么会把GET请求强制转成POST请求,要么是没有提供GET请求添加Body参数的方法。

然而,还是我的经验不够丰富。我的mentor在Apache HttpClient基础上改造了一下,给出了如下的HttpClientUtil。没错,这个工具类可以在Java程序中优雅地发送带Body的GET请求。

import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import java.net.URI;


@Slf4j
public class HttpClientUtil {
    private static SSLConnectionSocketFactory sslsf = null;

    private HttpClientUtil() {
    }

    static {
        try {
            sslsf = new SSLConnectionSocketFactory(
                    SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build(),
                    NoopHostnameVerifier.INSTANCE);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    public static CloseableHttpClient getHttpClient() {
        RequestConfig requestConfig = RequestConfig.custom()
                //设置等待数据超时时间
                .setSocketTimeout(50000)
                //设置连接超时时间
                .setConnectTimeout(5000)
                .setConnectionRequestTimeout(5000)
                .build();
        return HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .setConnectionManagerShared(true).setDefaultRequestConfig(requestConfig)
                .build();
    }





    public static String doGetJson(String url, String param) {
        // 构建支持跳过 SSL 验证的 HttpClient 实例
        CloseableHttpClient client = HttpClientUtil.getHttpClient();
        String body = "";
        HttpGetWithEntity httpGetWithEntity = new HttpGetWithEntity(url);
        org.apache.http.HttpEntity httpEntity = new StringEntity(param, ContentType.APPLICATION_JSON);
        httpGetWithEntity.setEntity(httpEntity);
        //执行请求操作,并拿到结果(同步阻塞)
        try (CloseableHttpResponse response = client.execute(httpGetWithEntity)) {
            //获取结果实体
            org.apache.http.HttpEntity entity = response.getEntity();
            if (entity != null) {
                //按指定编码转换结果实体为String类型
                body = EntityUtils.toString(entity, "UTF-8");
            }
        } catch (Exception e) {
            log.error("doGetJson() error.", e);
        }
        return body;
    }

    static class HttpGetWithEntity extends HttpEntityEnclosingRequestBase {
        private final static String METHOD_NAME = "GET";

        @Override
        public String getMethod() {
            return METHOD_NAME;
        }
        public HttpGetWithEntity() {
            super();
        }
        public HttpGetWithEntity(final URI uri) {
            super();
            setURI(uri);
        }
        HttpGetWithEntity(final String uri) {
            super();
            setURI(URI.create(uri));
        }
    }


}

虽然写不出这么好的工具类,但是可以看懂。

那我来总结一下这个工具类的实现思路:

自己写一个类去继承HttpEntityEnclosingRequestBase,而HttpEntityEnclosingRequestBase又继承了一个抽象类HttpRequestBase,于是我们就可以重写HttpRequestBase的一个抽象方法getMethod。每次getMethod都直接返回GET。

为什么要去继承HttpEntityEnclosingRequestBase呢?因为只有继承了这个类,才能给Http请求加Body参数(在Apache HttpClient里面叫Entity)。

Apache原先继承HttpEntityEnclosingRequestBase的都是一些HttpPost,HttpPut,HttpDelete

也就是只允许POST、PUT、DELETE请求携带Body参数。那我们自己写一个类叫做HttpGetWithEntity,那么这个HttpGet不就也可以设置Body参数了吗!大功告成!

最后来看看测试结果:

 

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值