java.net.URLConnection详解

准备

使用URLConnection操作http request时,我们至少得知道URL地址和字符集,参数是可选的,它基于具体的需求。

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

参数必须以name=value的形式出现,不同的参数使用&连接,通常还需要用URLEncoder.encode()方法将参数转化为URL编码
String.format()方法会只是为了方便,如果需要多个 + 来操作字符的时候我就喜欢用这个方法。

发送一个带参数的get请求

这是一段很简单的代码,使用默认的request方法就行

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

所有的参数都要放在URL后边,并且使用?连接,头部中的Accept-Charset会告诉服务器你发送的参数使用的是什么编码。如果你不需要发送任何的参数,可以不用写Accept-Charset,如果你不想发送任何的头部信息,可以直接使用URL.openStream()方法。

InputStream response = new URL(url).openStream();                                                                           
// ...

如果服务器端是HttpServlet,那么它将会调用doGet()方法来处理这个请求,发送的参数可以通过HttpServletRequest.getParameter() 方法访问。

发送一个带参数的post请求

使用URLConnection.setDoOutput(true),就可以发送post请求。网页表单的post请求是application/x-www-form-urlencoded类型的,post请求会将参数放在请求中发送给服务器

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);                                                                               
OutputStream output = null;                                                                              
try {                                                                               
     output = connection.getOutputStream();                                                                               
     output.write(query.getBytes(charset));                                                                               
} finally {                                                                               
     if (output != null)
     try { output.close(); } 
     catch (IOException logOrIgnore) {}                                                                               
}                                                                              
InputStream response = connection.getInputStream();                                                                                // ...

Note:如果你要使用程序自动发送一个网页的表单,不要忘了元素,你要把所有的hidden元素都使用name=value的方法发送给服务器,另外元素也要发送给服务器,因为服务器端通常使用这个参数来判断提交按钮是否被点击,哪一个被点击。
你也可以把URLConnection强制转化为HttpURLConnection,那样的话就可以使用HttpURLConnection.setRequestMethod(“POST”)方法代替URLConnection.setDoOutput(true)了。但是如果你想要从这个连接里获取输入流,那还得使用URLConnection.setDoOutput(true)方法。

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

如果服务器端是HttpServlet,那么它将调用doPost方法来处理这个请求,发送的参数可以使用HttpServletRequest.getParameter() 方法访问。

手动发起HTTP请求

你可以使用URLConnection.connect()方法手动的发送一个http请求,但是如果要获取http响应的时候,请求就会自动的发起,比如你使用URLConnection.getInputStream()方法的时候。上边的例子就是这样做的,所以完全没有必要调用connect方法。

获取HTTP响应信息

HTTP响应状态

获取HTTP响应状态必须使用HttpURLConnection,所以你的先进行强制转化

int status = httpConnection.getResponseCode();

HTTP响应头部

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

HTTP响应的编码方式

如果Content-Type包含一个charset参数,那么HTTP的响应很可能是文本流,我们就得使用服务器指定的字符集来处理这些文本。

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) {                                                                           
    BufferedReader reader = null;                                                                           
    try {                                                                           
        reader = new BufferedReader(
                   new InputStreamReader(response, charset));                                                                           
        for (String line; (line = reader.readLine()) != null;) {                                                                           
            // ... System.out.println(line) ? 
        }                                                                           
    } finally {                                                                           
        if (reader != null)
        try { reader.close(); }
        catch (IOException logOrIgnore) {}                                                                           
    }                                                                    
} else {                                                                     // It's likely binary content, use InputStream/OutputStream. 
}

维持session状态

服务器端的session通常是使用cookie实现的,网站就是通过追踪session来判断你的登录状态的。当你登录或者第一次访问网站的时候,你需要获取响应头部中的所有Set-Cookie字段,在以后的请求中再发送给服务器。

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.substring(0, cookie.indexOf(‘;’))代替split()。也可以通过内置的CookieHandler完成这个任务,在发送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();                                                                         
// ...

流模式

在请求发送之前,HttpURLConnetion会把所有需要发送的数据放到缓冲区里,不管你是否使用connection.setRequestProperty(“Content-Length”, contentLength);设置了contentLength,当你并行发送大量的POST请求时,这可能会引起OutOfMemoryExceptions 异常,为了避免这个问题,你需要使用HttpURLConnection.setFixedLengthStreamingMode()方法

httpConnection.setFixedLengthStreamingMode(contentLength);

但是如果不能事先知道内容的长度,可以使用HttpURLConnection.setChunkedStreamingMode()方法设置为块状流模式。在块状流模式的情况下,放在块里的内容将会被强行发送出去。下边的例子将会把发送的内容按照每块1KB的大小发送出去。

httpConnection.setChunkedStreamingMode(1024);

User-Agent

有的时候发送一个请求,去返回了不期望的响应,但是这个请求在浏览器上却能正常工作。这很可能是服务器做了一些关于基于User-Agent的判断。URLConnection默认将User-Agent设置为Java/1.6.0_19(后半部分是jre版本),你可以通过下边的方法重新设置:

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

处理错误

如果HTTP响应状态为4xx(客户端错误)或者5xx(服务器错误),你可以通过HttpUrlConnection.getErrorStream()来查看服务器发送过来的信息。

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

如果HTTP响应状态为-1,就是出现了连接错误。HttpURLConnection会保持连接一直可用,如果你想关闭这个特性,需要把http.keepAlive设置为false:

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

上传文件

通常使用multipart/form-data方式对混合了二进制和字符的POST内容进行编码,详细的编码细节可以参考RFC2388

String param = "value";                                                                    
File textFile = new File("/path/to/file.txt");                                                                    
File binaryFile = new File("/path/to/file.bin");                                                                    // Just generate some unique random value.
String boundary = Long.toHexString(System.currentTimeMillis());  // Line separator required by multipart/form-data. 
String CRLF = "\r\n";                                                                     
URLConnection connection = new URL(url).openConnection();                                                                    
connection.setDoOutput(true);                                                                    
connection.setRequestProperty("Content-Type","multipart/form-data; boundary="+boundary);                                                                    
PrintWriter writer = null;                                                                    
try {                                                                    
    OutputStream output = connection.getOutputStream();                                                                    
 // true = autoFlush, important! 
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);                                                                    
    writer.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);                                                                    
    writer.append(CRLF).flush();                                                                    
    BufferedReader reader = null;                                                                    
    try {                                                                    
        reader = new BufferedReader(new InputStreamReader(new FileInputStream(textFile), charset));                                                                    
        for (String line; (line = reader.readLine()) != null;) {                                                                    
            writer.append(line).append(CRLF);                                                                    
        }                                                                    
    } finally {                                                                    
        if (reader != null)
 try { reader.close(); } 
catch (IOException logOrIgnore) {}                                                                    
    }                                                                    
    writer.flush();                                                                    
    // 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();                                                                  
    InputStream input = null;                                                                
    try {                                                              
        input = new FileInputStream(binaryFile);                                                            
        byte[] buffer = new byte[1024];                                                          
        for (int length = 0; (length = input.read(buffer)) > 0;) {                                                      
            output.write(buffer, 0, length);                                                       
        }
        // Important! Output cannot be closed.
       // Close of writer will close output as well. 
        output.flush();                                                             
    } finally {                                                          
        if (input != null)
        try { input.close(); } 
        catch (IOException logOrIgnore) {}                                                          
    }
    // CRLF is important! It indicates end of binary boundary. 
    writer.append(CRLF).flush();                                                                  
    // End of multipart/form-data. 
    writer.append("--" + boundary + "--").append(CRLF);                                                               
} finally {                                                              
    if (writer != null) writer.close();                                                                
}

如果服务器端是HttpServlet,那么它将会调用doPost()方法处理这个请求,使用HttpServletRequest.getPart()方法访问里边的内容(不是getParameter)。getPart方法在Servlet3.0(Glassfish3,Tomcat7)才被引入,在Servlet3.0之前,最好使用Apache Commons FileUpload 来处理multipart/form-data请求。

原文出自stackoverflow

转载于:https://my.oschina.net/tangzhichao/blog/781453

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值