如何使用NameValuePair在HttpURLConnection中使用POST方法添加参数

277

我正在尝试使用 HttpURLConnection 进行 POST 请求(必须以这种方式使用,无法使用 HttpPost),并且我想要向该连接添加参数,例如:

post.setEntity(new UrlEncodedFormEntity(nvp));

在哪里

nvp = new ArrayList<NameValuePair>();

我有一些数据存储在ArrayList中。我找不到将此ArrayList添加到下面的HttpURLConnection的方法:

HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
https.setHostnameVerifier(DO_NOT_VERIFY);
http = https;
http.setRequestMethod("POST");
http.setDoInput(true);
http.setDoOutput(true);
那种奇怪的https和http组合的原因是需要不验证证书。虽然这不是问题,它能够很好地将数据发送到服务器。但我需要带参数进行数据发送。
有什么想法吗?

重复免责声明:
在2012年时,我不知道如何将参数插入到HTTP POST请求中。我一直使用NameValuePair,因为它在教程中。这个问题可能看起来像是一个重复,但是我的2012年自己读了那个其他问题,它并没有使用NameValuePair。事实上,它没有解决我的问题。

2
如果您在发布参数方面遇到问题,那么下面的链接可能会对您有所帮助。https://dev59.com/vHE85IYBdhLWcg3wZyqt - Hitendra
1
字符串 url = "http://example.com"; 字符串 charset = "UTF-8"; 字符串 param1 = "value1"; 字符串 param2 = "value2"; // ... 字符串 query = String.format("param1=%s&param2=%s", URLEncoder.encode(param1, charset), URLEncoder.encode(param2, charset)); 你可以使用查询字符串来代替使用NameValuePair列表。 - Hitendra
我需要以这种方式使用它,不能使用HttpPost,这就是为什么我建议使用Manikandan发布的其他答案,它可以正常工作。 - Hitendra
3
可能是Java - sending HTTP parameters via POST method easily的重复问题。 - rogerdpack
1
因为这里的“很多答案”与那个问题的答案相同。但是现在我明白这是一个不同的问题,谢谢澄清 :) - rogerdpack
显示剩余3条评论
16个回答

376
您可以获取连接的输出流,并将参数查询字符串写入其中。
URL url = new URL("http://yoururl.com");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setReadTimeout(10000);
conn.setConnectTimeout(15000);
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);

List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("firstParam", paramValue1));
params.add(new BasicNameValuePair("secondParam", paramValue2));
params.add(new BasicNameValuePair("thirdParam", paramValue3));

OutputStream os = conn.getOutputStream();
BufferedWriter writer = new BufferedWriter(
        new OutputStreamWriter(os, "UTF-8"));
writer.write(getQuery(params));
writer.flush();
writer.close();
os.close();

conn.connect();

...

private String getQuery(List<NameValuePair> params) throws UnsupportedEncodingException
{
    StringBuilder result = new StringBuilder();
    boolean first = true;

    for (NameValuePair pair : params)
    {
        if (first)
            first = false;
        else
            result.append("&");

        result.append(URLEncoder.encode(pair.getName(), "UTF-8"));
        result.append("=");
        result.append(URLEncoder.encode(pair.getValue(), "UTF-8"));
    }

    return result.toString();
}

27
NameValuePair可以用AbstractMap的SimpleEntry替代。请参考此页面:https://dev59.com/HXA85IYBdhLWcg3wCe9Z - user1499731
10
为了获得最佳性能,当请求体的长度事先已知时,应调用setFixedLengthStreamingMode(int);当请求体长度未知时,应调用setChunkedStreamingMode(int)。否则,HttpURLConnection将被迫在传输请求体之前在内存中缓冲完整的请求体,浪费(并可能耗尽)堆空间并增加延迟。 - Muhammad Babar
11
NameValuePair 在 Api 22 中已弃用,请查看我的答案 https://dev59.com/0Gkw5IYBdhLWcg3wm70h#29561084。 - Fahim
只有当数据被编码为UTF-8时才需要使用UTF-8编码。是这样的吧..?? - Ramesh-X
1
或许在构建URL对象时可以使用原始模式,类似这样: URL url = new URL("http://yoururl.com?k1=v1&k2=v2&···&kn=vn"); 然后设置conn使用POST方法时就不需要再写它们了。 - alexscmar
显示剩余12条评论

190

由于NameValuePair已经被弃用,想要分享我的代码

public String  performPostCall(String requestURL,
            HashMap<String, String> postDataParams) {

        URL url;
        String response = "";
        try {
            url = new URL(requestURL);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(15000);
            conn.setConnectTimeout(15000);
            conn.setRequestMethod("POST");
            conn.setDoInput(true);
            conn.setDoOutput(true);


            OutputStream os = conn.getOutputStream();
            BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(os, "UTF-8"));
            writer.write(getPostDataString(postDataParams));

            writer.flush();
            writer.close();
            os.close();
            int responseCode=conn.getResponseCode();

            if (responseCode == HttpsURLConnection.HTTP_OK) {
                String line;
                BufferedReader br=new BufferedReader(new InputStreamReader(conn.getInputStream()));
                while ((line=br.readLine()) != null) {
                    response+=line;
                }
            }
            else {
                response="";

            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return response;
    }

....

  private String getPostDataString(HashMap<String, String> params) throws UnsupportedEncodingException{
        StringBuilder result = new StringBuilder();
        boolean first = true;
        for(Map.Entry<String, String> entry : params.entrySet()){
            if (first)
                first = false;
            else
                result.append("&");

            result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
            result.append("=");
            result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
        }

        return result.toString();
    }

10
谢谢你保持更新,Fahim :-) - Michal
3
如果您的compileSdkVersion是23(Marshmallow),则无法再使用NameValuePair,因为它们已经删除了该库。我曾担心迁移会很麻烦,但是您提供的解决方案让我省了很多时间。谢谢。 - ChallengeAccepted
@Apostrofix 这只是将变量初始化为空值。你也可以将其设置为 null。 - Fahim
1
你们中有人在Jelly Bean上使用OutputStream os = conn.getOutputStream();这行代码时遇到了“没有与主机名关联的地址”的问题吗? - Ricardo
1
感谢您分享您的代码。即使是Android开发者网站也没有提供解决方案。 - Ahsan
显示剩余7条评论

162

如果您不需要使用 ArrayList<NameValuePair> 作为参数,那么可以使用 Uri.Builder 类构建查询字符串,这是较短的解决方案:

URL url = new URL("http://yoururl.com");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setReadTimeout(10000);
conn.setConnectTimeout(15000);
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);

Uri.Builder builder = new Uri.Builder()
        .appendQueryParameter("firstParam", paramValue1)
        .appendQueryParameter("secondParam", paramValue2)
        .appendQueryParameter("thirdParam", paramValue3);
String query = builder.build().getEncodedQuery();

OutputStream os = conn.getOutputStream();
BufferedWriter writer = new BufferedWriter(
            new OutputStreamWriter(os, "UTF-8"));
writer.write(query);
writer.flush();
writer.close();
os.close();

conn.connect();

10
"这应该是一个答案,因为不需要重新发明轮子!" - injecteer
1
如何在appendQueryParameter中上传文件主体以用于图像和其他内容。 - Harsha
2
更加令人满意的解决方案 - PYPL
1
@Krups 我认为你的问题不同,尝试查找使用POST发送JSON对象的方法。 - mpolci
3
Uri.Builder来自哪里? - tschumann
显示剩余3条评论

25

一种解决方案是自己创建参数字符串。

这是我最近项目中使用的实际方法。您需要将 args 从 hashtable 更改为 namevaluepair。

private static String getPostParamString(Hashtable<String, String> params) {
    if(params.size() == 0)
        return "";

    StringBuffer buf = new StringBuffer();
    Enumeration<String> keys = params.keys();
    while(keys.hasMoreElements()) {
        buf.append(buf.length() == 0 ? "" : "&");
        String key = keys.nextElement();
        buf.append(key).append("=").append(params.get(key));
    }
    return buf.toString();
}

提交参数:

OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
writer.write(getPostParamString(req.getPostParams()));

4
当然,您应该对键值对进行编码。 - Max Nanasy

14

被接受的答案在以下代码处抛出 ProtocolException:

OutputStream os = conn.getOutputStream();

因为它没有启用 URLConnection 对象的输出。解决方案应该包括以下内容:

conn.setDoOutput(true);

以使其正常工作。


14

我认为我找到了你需要的东西,它可能对其他人也有帮助。

您可以使用方法UrlEncodedFormEntity.writeTo(OutputStream)

UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nvp); 
http.connect();

OutputStream output = null;
try {
  output = http.getOutputStream();    
  formEntity.writeTo(output);
} finally {
 if (output != null) try { output.close(); } catch (IOException ioe) {}
}

13

如果还不晚的话,我想分享我的代码。

Utils.java:

public static String buildPostParameters(Object content) {
        String output = null;
        if ((content instanceof String) ||
                (content instanceof JSONObject) ||
                (content instanceof JSONArray)) {
            output = content.toString();
        } else if (content instanceof Map) {
            Uri.Builder builder = new Uri.Builder();
            HashMap hashMap = (HashMap) content;
            if (hashMap != null) {
                Iterator entries = hashMap.entrySet().iterator();
                while (entries.hasNext()) {
                    Map.Entry entry = (Map.Entry) entries.next();
                    builder.appendQueryParameter(entry.getKey().toString(), entry.getValue().toString());
                    entries.remove(); // avoids a ConcurrentModificationException
                }
                output = builder.build().getEncodedQuery();
            }
        }

        return output;
    }

public static URLConnection makeRequest(String method, String apiAddress, String accessToken, String mimeType, String requestBody) throws IOException {
        URL url = new URL(apiAddress);
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

        urlConnection.setDoInput(true);
        urlConnection.setDoOutput(!method.equals("GET"));
        urlConnection.setRequestMethod(method);

        urlConnection.setRequestProperty("Authorization", "Bearer " + accessToken);        

        urlConnection.setRequestProperty("Content-Type", mimeType);
        OutputStream outputStream = new BufferedOutputStream(urlConnection.getOutputStream());
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, "utf-8"));
        writer.write(requestBody);
        writer.flush();
        writer.close();
        outputStream.close();            

        urlConnection.connect();

        return urlConnection;
    }

MainActivity.java:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    new APIRequest().execute();
}

private class APIRequest extends AsyncTask<Void, Void, String> {

        @Override
        protected Object doInBackground(Void... params) {

            // Of course, you should comment the other CASES when testing one CASE

            // CASE 1: For FromBody parameter
            String url = "http://10.0.2.2/api/frombody";
            String requestBody = Utils.buildPostParameters("'FromBody Value'"); // must have '' for FromBody parameter
            HttpURLConnection urlConnection = null;
            try {
                urlConnection = (HttpURLConnection) Utils.makeRequest("POST", url, null, "application/json", requestBody);                    
                InputStream inputStream;
                // get stream
                if (urlConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) {
                    inputStream = urlConnection.getInputStream();
                } else {
                    inputStream = urlConnection.getErrorStream();
                }
                // parse stream
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                String temp, response = "";
                while ((temp = bufferedReader.readLine()) != null) {
                    response += temp;
                }
                return response;
            } catch (IOException e) {
                e.printStackTrace();
                return e.toString();
            } finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
            }

            // CASE 2: For JSONObject parameter
            String url = "http://10.0.2.2/api/testjsonobject";
            JSONObject jsonBody;
            String requestBody;
            HttpURLConnection urlConnection;
            try {
                jsonBody = new JSONObject();
                jsonBody.put("Title", "BNK Title");
                jsonBody.put("Author", "BNK");
                jsonBody.put("Date", "2015/08/08");
                requestBody = Utils.buildPostParameters(jsonBody);
                urlConnection = (HttpURLConnection) Utils.makeRequest("POST", url, null, "application/json", requestBody);                    
                ...
                // the same logic to case #1
                ...
                return response;
            } catch (JSONException | IOException e) {
                e.printStackTrace();
                return e.toString();
            } finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
            }           

            // CASE 3: For form-urlencoded parameter
            String url = "http://10.0.2.2/api/token";
            HttpURLConnection urlConnection;
            Map<String, String> stringMap = new HashMap<>();
            stringMap.put("grant_type", "password");
            stringMap.put("username", "username");
            stringMap.put("password", "password");
            String requestBody = Utils.buildPostParameters(stringMap);
            try {
                urlConnection = (HttpURLConnection) Utils.makeRequest("POST", url, null, "application/x-www-form-urlencoded", requestBody);
                ...
                // the same logic to case #1
                ...
                return response;
            } catch (Exception e) {
                e.printStackTrace();
                return e.toString();
            } finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
            }                  
        }

        @Override
        protected void onPostExecute(String response) {
            super.onPostExecute(response);
            // do something...
        }
    }

@Srinivasan,正如您在我的代码中所看到的:“if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { ... } else { ... } ” - BNK
是的,我已经知道了,但我的问题是哪个变量将拥有来自给定URL的整个响应。 - iSrinivasan27
1
@Srinivasan 更多细节,您可以尝试使用InputStream inputStream = null; 如果(urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { inputStream = urlConnection.getInputStream(); } else { inputStream = urlConnection.getErrorStream(); } - BNK
@Srinivasan:我刚刚更新了我的答案,希望更清晰明了 :) - BNK
1
超级棒的东西兄弟,好的例子。 - Whats Going On
显示剩余3条评论

12

使用PrintWriter有一个更简单的方法(请参见这里)。

基本上你所需要做的就是:

// set up URL connection
URL urlToRequest = new URL(urlStr);
HttpURLConnection urlConnection = (HttpURLConnection)urlToRequest.openConnection();
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

// write out form parameters
String postParamaters = "param1=value1&param2=value2"
urlConnection.setFixedLengthStreamingMode(postParameters.getBytes().length);
PrintWriter out = new PrintWriter(urlConnection.getOutputStream());
out.print(postParameters);
out.close();

// connect
urlConnection.connect();

这种方法安全吗? - Khalid Lakhani

4

AsyncTask用于以POST方法发送数据,数据格式为JSONObect

public class PostMethodDemo extends AsyncTask<String , Void ,String> {
        String server_response;

        @Override
        protected String doInBackground(String... strings) {
            URL url;
            HttpURLConnection urlConnection = null;

            try {
                url = new URL(strings[0]);
                urlConnection = (HttpURLConnection) url.openConnection();
                urlConnection.setDoOutput(true);
                urlConnection.setDoInput(true);
                urlConnection.setRequestMethod("POST");

                DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream ());

                try {
                    JSONObject obj = new JSONObject();
                    obj.put("key1" , "value1");
                    obj.put("key2" , "value2");

                    wr.writeBytes(obj.toString());
                    Log.e("JSON Input", obj.toString());
                    wr.flush();
                    wr.close();
                } catch (JSONException ex) {
                    ex.printStackTrace();
                }
                urlConnection.connect();

                int responseCode = urlConnection.getResponseCode();

                if(responseCode == HttpURLConnection.HTTP_OK){
                    server_response = readStream(urlConnection.getInputStream());
                }

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            Log.e("Response", "" + server_response);
        }
    }

    public static String readStream(InputStream in) {
        BufferedReader reader = null;
        StringBuffer response = new StringBuffer();
        try {
            reader = new BufferedReader(new InputStreamReader(in));
            String line = "";
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return response.toString();
    }

3
尝试以下操作:
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("your url");
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(3);
nameValuePairs.add(new BasicNameValuePair("user_name", "Name"));
nameValuePairs.add(new BasicNameValuePair("pass","Password" ));
nameValuePairs.add(new BasicNameValuePair("user_email","email" ));
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

// Execute HTTP Post Request
HttpResponse response = httpclient.execute(httppost);

String ret = EntityUtils.toString(response.getEntity());
Log.v("Util response", ret);

您可以添加尽可能多的nameValuePairs。别忘了在列表中提及数量。


请参考此链接:http://www.xyzws.com/Javafaq/how-to-use-httpurlconnection-post-data-to-web-server/139 - Manikandan
1
这并没有回答题目为“如何使用POST向HttpURLConnection添加参数”的问题,它会引导人们产生误解。 - User3
2
这不是针对此问题的正确答案。 - Skynet
1
NameValuePair在Api 22中已被弃用,请查看我的答案https://dev59.com/0Gkw5IYBdhLWcg3wm70h#29561084。 - Fahim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接