解决HttpURLConnection GET封装请求体

广义上get没有请求体,但在实际开发中可能会遇到get请求封装请求体的需求。这里我们要解决的是使用 HttpURLConnection 封装get请求body时,源码中get请求封装请求体会转为post请求。

源码如下:

//HttpURLConnection
 private synchronized OutputStream getOutputStream0() throws IOException {
        try {
            if (!this.doOutput) {
                throw new ProtocolException("cannot write to a URLConnection if doOutput=false - call setDoOutput(true)");
            } else {
                if (this.method.equals("GET")) {
                    this.method = "POST"; //注意这里
                }
 
                if ("TRACE".equals(this.method) && "http".equals(this.url.getProtocol())) {
                    throw new ProtocolException("HTTP method TRACE doesn't support output");
                } else if (this.inputStream != null) {
                    throw new ProtocolException("Cannot write output after reading input.");
                } else {
                    if (!this.checkReuseConnection()) {
                        this.connect();
                    }
 
                    boolean var1 = false;
                    String var8 = this.requests.findValue("Expect");
                    if ("100-Continue".equalsIgnoreCase(var8) && this.streaming()) {
                        this.http.setIgnoreContinue(false);
                        var1 = true;
                    }
 
                    if (this.streaming() && this.strOutputStream == null) {
                        this.writeRequests();
                    }
 
                    if (var1) {
                        this.expect100Continue();
                    }
 
                    this.ps = (PrintStream)this.http.getOutputStream();
                    if (this.streaming()) {
                        if (this.strOutputStream == null) {
                            if (this.chunkLength != -1) {
                                this.strOutputStream = new HttpURLConnection.StreamingOutputStream(new ChunkedOutputStream(this.ps, this.chunkLength), -1L);
                            } else {
                                long var3 = 0L;
                                if (this.fixedContentLengthLong != -1L) {
                                    var3 = this.fixedContentLengthLong;
                                } else if (this.fixedContentLength != -1) {
                                    var3 = (long)this.fixedContentLength;
                                }
 
                                this.strOutputStream = new HttpURLConnection.StreamingOutputStream(this.ps, var3);
                            }
                        }
 
                        return this.strOutputStream;
                    } else {
                        if (this.poster == null) {
                            this.poster = new PosterOutputStream();
                        }
 
                        return this.poster;
                    }
                }
            }
        } catch (RuntimeException var5) {
            this.disconnectInternal();
            throw var5;
        } catch (ProtocolException var6) {
            int var2 = this.responseCode;
            this.disconnectInternal();
            this.responseCode = var2;
            throw var6;
        } catch (IOException var7) {
            this.disconnectInternal();
            throw var7;
        }
    }

解决方法:通过反射将对象的参数 requestMethod 重置为get。

            //将post请求转为get请求(通过反射)
            // 解决在Rest请求中,GET请求附带body导致GET请求被强制转换为POST
            // 在sun.net.www.protocol.http.HttpURLConnection.getOutputStream0方法中,会把GET方法
            // 修改为POST,而且无法调用setRequestMethod方法修改,因此此处使用反射强制修改字段属性值
            // https://stackoverflow.com/questions/978061/http-get-with-request-body/983458
            List<Field> fields = new ArrayList<>();
            Class tempClass = con.getClass();
            while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
                fields.addAll(Arrays.asList(tempClass.getDeclaredFields()));
                tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
            }
            for (Field field : fields) {
                if ("method".equals(field.getName())) {
                    field.setAccessible(true);
                    field.set(con, "GET");
                }
            }
            //写入请求参数
            writer.write(params.toString());
            writer.flush();
            writer.close();

完整请求方法:

  /**
     * 发送请求
     */
    public JSONObject sendRequest(String urlParam, String requestType, String username, String password, JSONObject params) throws IOException {
        BufferedReader buffer = null;
        InputStream inputStream = null;
        try {
            URL url = new URL(urlParam);
            //得到连接对象
            HttpURLConnection con = (HttpURLConnection) url.openConnection();
            //设置请求类型
            con.setRequestMethod(requestType);

            //header内的的参数在这里set
            //设置请求需要返回的数据类型和字符集类型
            con.setRequestProperty("Content-Type", "application/json");

            //设置Basic认证
//            String auth = "pi_csbuyt:*****";
            // 后期使用
            String auth = username + ":" + password;
            byte[] rel = Base64.encodeBase64(auth.getBytes());
            String res = new String(rel);
            con.setRequestProperty("Authorization", "Basic " + res);

            baseBean.writeLog("auth == " + auth);

            //设置客户端与服务连接类型
//            con.addRequestProperty("Connection", "Keep-Alive");

            //允许写出、读入
            con.setDoOutput(true);
            con.setDoInput(true);
            //post请求不使用缓存
//            con.setUseCaches(false);

            //设置body内的参数,put到JSONObject中
//            JSONObject param = new JSONObject();
//            param.put("REQ_DATA", "");

//            baseBean.writeLog(param);

            // 建立实际的连接
            con.connect();

            OutputStreamWriter writer = new OutputStreamWriter(con.getOutputStream(), "UTF-8");


            //将post请求转为get请求(通过反射)
            // 解决在Rest请求中,GET请求附带body导致GET请求被强制转换为POST
            // 在sun.net.www.protocol.http.HttpURLConnection.getOutputStream0方法中,会把GET方法
            // 修改为POST,而且无法调用setRequestMethod方法修改,因此此处使用反射强制修改字段属性值
            // https://stackoverflow.com/questions/978061/http-get-with-request-body/983458
            List<Field> fields = new ArrayList<>();
            Class tempClass = con.getClass();
            while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
                fields.addAll(Arrays.asList(tempClass.getDeclaredFields()));
                tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
            }
            for (Field field : fields) {
                if ("method".equals(field.getName())) {
                    field.setAccessible(true);
                    field.set(con, "GET");
                }
            }
            //写入请求参数
            writer.write(params.toString());
            writer.flush();
            writer.close();

            if (con.getResponseCode() == HttpURLConnection.HTTP_OK) {
                //得到响应流
                inputStream = con.getInputStream();
                //将响应流转换成字符串
                StringBuffer resultBuffer = new StringBuffer();
                String line;

                buffer = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
                while ((line = buffer.readLine()) != null) {
                    resultBuffer.append(line);
                }

                //判断响应参数是否为空
                if (!(resultBuffer.length() > 0)) {
                    return null;
                }

                return JSONObject.parseObject(resultBuffer.toString());
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (buffer != null && inputStream != null) {
                inputStream.close();
                buffer.close();
            }
        }
    }