【Stackoverflow问题精选】如何使用java.net.URLConnection收发HTTP请求

问题

如何使用java.net.URLConnection收发HTTP请求呢?处理Http请求,有哪些最佳实践?

讨论:


精华回答

首先声明,下面的代码,都是基本的例子。更严谨的话,还应加入处理各种异常的代码(如IOExceptions、NullPointerException、ArrayIndexOutOfBoundsException)


准备

首先,需要设置请求的URL以及charset(编码);另外还需要哪些参数,则取决于各自url的要求。

String url = "http://example.com";
String charset = "UTF-8";
String param1 = "value1";
String param2 = "value2";
// ...
String query = String.format("param1=%s¶m2=%s",
URLEncoder.encode(param1, charset),
URLEncoder.encode(param2, charset));

请求参数必须是name=value这样的格式,每个参数间用&连接。一般来说,你还得用 URLEncoder#encode()对参数做编码

上面例子还用到了String#format(),这只是为了方便,我更喜欢用这个方式来完成string的拼接。


发送一个HTTP GET请求(可选:带上参数)

这依然是个繁琐的事情。默认的方式如下:

URLConnection connection = new URL(url + "?" + query).openConnection();
connection.setRequestProperty("Accept-Charset", charset);
InputStream response = connection.getInputStream();
// ...

url和参数之间,要用?号连接。请求头(header)中的Accept-Charset,用于告诉服务器,你所发送参数的编码。如果你不发送任何参数,也可以不管Accept-Charset。如果你无需设置任何header,也可以用URL#openStream()而非openConnection。

不管那种方式,假设服务器端是HttpServlet,那么你的get请求将会触发它的doGet()方法,它能通过HttpServletRequest#getParameter()获取你传递的参数。


发送一个HTTP POST请求,并带上参数

设置URLConnection#setDoOutput(),等于隐式地将请求方法设为POST。标准的HTTP
POST 表单,其Content-Tyep为application/x-www-form-urlencoded,请求的内容放到到body中。也就是如下代码:

URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true); // Triggers POST.
connection.setRequestProperty("Accept-Charset", charset);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + charset);

try (OutputStream output = connection.getOutputStream()) {
    output.write(query.getBytes(charset));
}

InputStream response = connection.getInputStream();
// ...

提醒:

当你要提交一个HTML表单时,务必要把<input type="hidden"这类元素的值,以name=value的形式也一并提交。另外,还有<input type="submit">这类元素,也是如此。因为,通常服务端也需要这个信息,来确认哪一个按钮触发了这个提交动作。

也可以使用HttpURLConnection来代替URLConnection,然后调用HttpURLConnection#setRequestMethod()来将请求设为POST类型。

HttpURLConnection httpConnection = (HttpURLConnection) new URL(url).openConnection();
httpConnection.setRequestMethod("POST");
// ...

同样的,如果服务端是HttpServlet,将会触发它的doPost()方法,可以通过HttpServletRequest#getParameter()获取post参数


真正触发HTTP请求的发送

你可以显式地通过URLConnection#connect()来发送请求,但是,当你调用获取响应信息的方法时,一样将自动发送请求。例如当你使用URLConnection#getInputStream()时,就会自动触发请求,因此,不用多次一举地调用connect()方法。上面我的例子,也都是直接调用getInputStream()方法。


获取HTTP响应信息

1)HTTP响应码:

首先默认你使用了 HttpURLConnection

int status = httpConnection.getResponseCode();

2)HTTP 响应头(headers)

</pre><pre name="code" class="java">for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
    System.out.println(header.getKey() + "=" + header.getValue());
}

3)HTTP响应编码:

当Content-Type中包含charset参数时,说明响应内容是基于charset参数指定的编码。因此,解码响应信息时,也要按照这个编码格式来。

String contentType = connection.getHeaderField("Content-Type");
String charset = null;

for (String param : contentType.replace(" ", "").split(";")) {
    if (param.startsWith("charset=")) {
        charset = param.split("=", 2)[1];
        break;
    }
}

if (charset != null) {
    try (BufferedReader reader = new BufferedReader(new InputStreamReader(response, charset))) {
        for (String line; (line = reader.readLine()) != null;) {
            // ... System.out.println(line) ?
        }
    }
}
else {
    // It's likely binary content, use InputStream/OutputStream.
}

session的维护

服务端session,通常是基于cookie实现的。你可以通过CookieHandlerAPI来管理cookie。在发送HTTP请求前,初始化一个CookieManager
然后设置参数为CookiePolicy.ACCEPT_ALL

// First set the default cookie manager.
CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
// All the following subsequent URLConnections will use the same cookie manager.
URLConnection connection = new URL(url).openConnection();
// ...
connection = new URL(url).openConnection();
// ...
connection = new URL(url).openConnection();
// ...

请注意,这个方式并非适用于所有场景。如果使用这个方式失败了,你可以尝试自己设置cookie:你需要从响应头中拿到Set-Cookie参数,然后再把cookie设置到接下来的其他请求中。

// Gather all cookies on the first request.
URLConnection connection = new URL(url).openConnection();
List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
// ...

// Then use the same cookies on all subsequent requests.
connection = new URL(url).openConnection();
for (String cookie : cookies) {
    connection.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
}
// ...

上面的split(";", 2)[0],作用是去掉一些跟服务端无关的cookie信息(例如expores,path等)。也可用cookie.substring(0, cookie.indexOf(‘;‘))实现同样的目的


流的处理

不管你是否通过connection.setRequestProperty("Content-Length", contentLength)为content设置了定长,  HttpURLConnection在发送请求前,默认都会缓存整个请求的body。如果发送一个比较大的post请求(例如上传文件),有可能会导致OutOfMemoryException。为了避免这个问题,可以设置HttpURLConnection#setFixedLengthStreamingMode()

httpConnection.setFixedLengthStreamingMode(contentLength);

但如果content长度是未知的,则可以用HttpURLConnection#setChunkedStreamingMode()。这样,header中Transfer-Encoding会变成chunked,你的请求将会分块发送,例如下面的例子,请求的body,将会按1KB一块,分块发送

httpConnection.setChunkedStreamingMode(1024);

User-Agent

有时候,你发送的请求,可能只有在浏览器下才能正常返回,而其他方式却不行。这可能跟请求头中的User-Agent有关。通过URLConnection发送的请求,默认会带上的User-Agent信息是Java/1.6.0_19,也就是java+jre的版本。你可以重写这个信息:

connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.3) Gecko/20100401"); // Do as if you're using Firefox 3.6.3.

这里有一份更全的浏览器User-Agent清单


错误处理

如果HTTP的响应码是4xx(客户端异常)或者5xx(服务端异常),你可以通过HttpURLConnection#getErrorStream()获取信息,服务端可能会将一些有用的错误信息放到这里面。

InputStream error = ((HttpURLConnection) connection).getErrorStream();

(译注:没看明白这段话想表达的意思)

If the HTTP response code is -1, then something went wrong with connection and response handling. The HttpURLConnection implementation is somewhat buggy with keeping connections alive. You may
want to turn it off by setting the http.keepAlive system property to false. You can do this programmatically in the beginning of your application by:

System.setProperty("http.keepAlive", "false");


上传文件

一般来说,你需要将post的内容设为multipart/form-data(相关的RFC文档:RFC2388)

String param = "value";
File textFile = new File("/path/to/file.txt");
File binaryFile = new File("/path/to/file.bin");
String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.
String CRLF = "\r\n"; // Line separator required by multipart/form-data.
URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

try (
    OutputStream output = connection.getOutputStream();
    PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, charset), true);
) {
    // Send normal param.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"param\"").append(CRLF);
    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
    writer.append(CRLF).append(param).append(CRLF).flush();

    // Send text file.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"textFile\"; filename=\"" + textFile.getName() + "\"").append(CRLF);
    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF); // Text file itself must be saved in this charset!
    writer.append(CRLF).flush();
    Files.copy(textFile.toPath(), output);
    output.flush(); // Important before continuing with writer!
    writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.

    // Send binary file.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"binaryFile\"; filename=\"" + binaryFile.getName() + "\"").append(CRLF);
    writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(binaryFile.getName())).append(CRLF);
    writer.append("Content-Transfer-Encoding: binary").append(CRLF);
    writer.append(CRLF).flush();
    Files.copy(binaryFile.toPath(), output);
    output.flush(); // Important before continuing with writer!
    writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.

    // End of multipart/form-data.
    writer.append("--" + boundary + "--").append(CRLF).flush();
}

假设服务端还是一个HttpServlet,它的doPost()方法将会处理这个请求,服务端通过 HttpServletRequest#getPart()获取你发送的内容(注意了,不是getParameter())。getPart()是个比较新的方法,是在Servlet
3.0后才引入的。如果你是Servlet 3.0之前的版本,则可以选用 Apache Commons FileUpload来解析multipart/form-data的请求。可以参考这里的例子


最后的话

上面啰嗦了很多,Apache提供了工具包,帮助我们更方便地完成这些事情

Apache HttpComponents HttpClient

google也有类似的工具包


解析、提取HTML内容

如果你是想解析提取html的内容,你可以用Jsoup等解析器

stackoverflow原址:

http://stackoverflow.com/questions/2793150/using-java-net-urlconnection-to-fire-and-handle-http-requests

专栏介绍:

非常喜欢stackoverflow,总能在上面找到疑难杂症的解决办法。偶然发现该网站有一个热度榜单。于是精选了热度较高的一些问题,然后按照自己的理解,把大家的讨论梳理出来。因此,这些文章不是真正的翻译,而是按照自己的理解做了一些增删、润色,希望能把上面的讨论,更精简有效地分享给大家。

如需转载,请注明原文地址

http://blog.csdn.net/lizeyang

时间: 2024-12-17 19:08:20

【Stackoverflow问题精选】如何使用java.net.URLConnection收发HTTP请求的相关文章

通过java.net.URLConnection发送HTTP请求的方法

1.GET与POST请求的区别 a) get请求可以获取静态页面,也可以把参数放在URL字串后面,传递给servlet, b) post与get的不同之处在于post的参数不是放在URL字串里面,而是放在http请求的正文内. 2.URLConnection的对象 a) 获取URLConnection实例 URL url = new URL(urlString); // 根据url生成urlConnection对象 urlConnection = (HttpURLConnection) url.

Java -- 通过 URLConnection 进行http请求中文乱码

对writer和reader指定字符集 out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(), "utf-8")); in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8")); 具体代码: public static String sendPost(String url, String

java中post和get请求

示例代码: package com.shareboxes.util; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.URL; imp

Java发送get及post请求工具方法

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URL; import java.net.URLConnection; import java.util.List; import java.util.Map; public class HttpRequest { /** *

Hbuilder MUI里面使用java.net.URL发送网络请求,操作cookie

1. 引入所需网络请求类: var URL = plus.android.importClass("java.net.URL"); var URLConnection = plus.android.importClass("java.net.URLConnection"); var BufferedReader = plus.android.importClass("java.io.BufferedReader"); var InputStrea

java 实现HttpRequest 发送http请求

package com.test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URL; import java.net.URLConnection; import java.util.List; import java.util.Map; public class h

java发送GET和post请求

1 package com.baqingshe.bjs.util; 2 3 import java.io.BufferedReader; 4 5 import java.io.IOException; 6 7 import java.io.InputStream; 8 9 import java.io.InputStreamReader; 10 11 import java.io.PrintWriter; 12 13 import java.net.URL; 14 15 import java.

Java 发送Get和Post请求

1 package com.htpt.superviseServices.dm.util; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6 import java.io.PrintWriter; 7 import java.net.URL; 8 import java.net.URLConnection; 9 import java.uti

Java发送http get/post请求,调用接口/方法

由于项目中要用,所以找了一些资料,整理下来. GitHub地址: https://github.com/iamyong    转自:http://blog.csdn.net/capmiachael/article/details/51833531 例1:使用 HttpClient (commons-httpclient-3.0.jar 1 import java.io.ByteArrayInputStream; 2 import java.io.ByteArrayOutputStream; 3