使用URLConnection处理http请求

一、准备

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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()方法将参数转化为URL编码

String.format()方法会只是为了方便,如果需要多个 + 来操作字符的时候我就喜欢用这个方法。

二、发送一个带参数的get请求

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

1
2
3
4
5
6
7
8
URLConnection connection = new URL(url + "?" + query).
                            openConnection();
                                                                                 
connection.setRequestProperty( "Accept-Charset" , charset);
                                                                                 
InputStream response = connection.getInputStream();
                                                                                 
// ...

 

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

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

 

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

三、发送一个带参数的post请求

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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)方法。

1
2
3
4
5
6
7
HttpURLConnection httpConnection = (HttpURLConnection)
                                    new URL(url)
                                    .openConnection();
                                                                             
httpConnection.setRequestMethod( "POST" );
                                                                             
// ...

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

三、手动发起HTTP请求

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

四、获取HTTP响应信息

1)HTTP响应状态

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

1
int status = httpConnection.getResponseCode();

 

2)HTTP响应头部

1
2
3
4
5
for (Entry> header : connection.getHeaderFields().entrySet()) {
                                                                            
     System.out.println(header.getKey() + "=" + header.getValue());
                                                                            
}

 

3)HTTP响应的编码方式

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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字段,在以后的请求中再发送给服务器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
URLConnection connection = new URL(url).openConnection();
                                                                          
List 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。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 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()方法

1
httpConnection.setFixedLengthStreamingMode(contentLength);

 

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

1
httpConnection.setChunkedStreamingMode( 1024 );

 

七、User-Agent

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

1
2
3
// 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()来查看服务器发送过来的信息。

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

 

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

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

 

九、上传文件

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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

 

你可能感兴趣的:(java)