1. 概述
在这篇快速教程中,我们将使用Java内置类HttpUrlConnection来实现一个Http请求。
2. HttpUrlConnection
HttpUrlConnection类允许我们不用添加其他任何类库就能实现基本的Http请求。所有需要的类都包含在 java.net包内。缺点是,相比于其他http类库,该方法有点笨重,而且也没有提供一些高级特性的API,比如添加请求头,添加认证等。不过这些都不要紧。你完全可以将这个实现封装一下,添加一些高级特性也不是很复杂。
如果你只是想快速地进行些Http请求而不想添加一些类库的话,本文的这些代码就足够了。
另外,如果你对java的http请求基本实现不很了解,本文给出的代码也会有些帮助。
3. 创建请求
HttpUrlConnection类的创建是通过URL 类的openConnection()方法。这个方法只是创建一个连接对象,并不建立连接。
通过设置requestMethod属性,HttpUrlConnection类可以创建各种请求类型——包括GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE。
比如创建一个GET请求:
URL url = new URL("www.baidu.com");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
4. 添加请求参数
如果我们想要添加请求参数,我们需要设置doOutput 为true,然后将请求参数拼接成字符串,格式param1=value¶m2=value,以流的形式写入到HttpUrlConnection 实例的OutputStream中。示例代码如下:
1 Map<String, String> parameters = new HashMap<>(); 2 parameters.put("param1", "val"); 3 4 con.setDoOutput(true); 5 DataOutputStream out = new DataOutputStream(con.getOutputStream()); 6 out.writeBytes(ParameterStringBuilder.getParamsString(parameters)); 7 out.flush(); 8 out.close();
为方便转换字符串参数,我写了个工具类ParameterStringBuilder。类中包含一个静态方法getParamsString()将Map转换成对应格式的字符串:
1 public class ParameterStringBuilder { 2 public static String getParamsString(Map<String, String> params) 3 throws UnsupportedEncodingException{ 4 StringBuilder result = new StringBuilder(); 5 6 for (Map.Entry<String, String> entry : params.entrySet()) { 7 result.append(URLEncoder.encode(entry.getKey(), "UTF-8")); 8 result.append("="); 9 result.append(URLEncoder.encode(entry.getValue(), "UTF-8")); 10 result.append("&"); 11 } 12 13 String resultString = result.toString(); 14 return resultString.length() > 0 15 ? resultString.substring(0, resultString.length() - 1) 16 : resultString; 17 } 18 }
5. 添加请求头
通过setRequestProperty() 方法可以添加请求头:
1 con.setRequestProperty("Content-Type", "application/json");
通过getHeaderField()方法可以读取响应头:
1 String contentType = con.getHeaderField("Content-Type");
6. 配置超时时间
类允许我们设置连接超时时间和读取超时时间。这些值决定了连接建立的最大等待时间间隔或读取到达数据的最大等待时间间隔。
设置超时时间,我们可以调用方法setConnectTimeout() 和方法setReadTimeout():
1 con.setConnectTimeout(5000); 2 con.setReadTimeout(5000);
这个例子中我们将超时时间设为5秒。
7. 处理Cookies
java.net 包包含的类CookieManager,HttpCookie等能很便捷地处理Cookies.
首先,从响应中读取cookies,我们先获取相应头里的Set-Cookie值,然后解析成HttpCookie对象的List.
1 String cookiesHeader = con.getHeaderField("Set-Cookie"); 2 List<HttpCookie> cookies = HttpCookie.parse(cookiesHeader);
接下来,我们将cookies存储起来:
1 cookies.forEach(cookie -> cookieManager.getCookieStore().add(null, cookie));
我们检查cookies中是否包含一个username属性,如果不包含,我们把一个叫zhangsan的username添加进去:
1 Optional<HttpCookie> usernameCookie = cookies.stream() 2 .findAny().filter(cookie -> cookie.getName().equals("username")); 3 if (usernameCookie == null) { 4 cookieManager.getCookieStore().add(null, new HttpCookie("username", "john")); 5 }
最后,将cookies添加到请求中去,我们需要在关闭连接和重新打开连接后,添加Cookie请求头 :
1 con.disconnect(); 2 con = (HttpURLConnection) url.openConnection(); 3 4 con.setRequestProperty("Cookie", 5 StringUtils.join(cookieManager.getCookieStore().getCookies(), ";"));
8. 处理重定向
我们可以通过调用方法setInstanceFollowRedirects(),设置为true或者false,来控制是否允许一个特定连接自动跟随重定向:
1 con.setInstanceFollowRedirects(false);
也可以全局设置所有的连接是否允许自动跟随重定向:
1 HttpUrlConnection.setFollowRedirects(false);
默认是允许自动跟随重定向的。
请求返回状态码301,302表示重定向,我们可以获取响应头的Location属性并用新的URL创建一个新的连接。
1 if (status == HttpURLConnection.HTTP_MOVED_TEMP 2 || status == HttpURLConnection.HTTP_MOVED_PERM) { 3 String location = con.getHeaderField("Location"); 4 URL newUrl = new URL(location); 5 con = (HttpURLConnection) newUrl.openConnection(); 6 }
9. 读取响应
通过读取HttpUrlConnection实例的InputStream流来读取响应。
读取响应常用方法有getResponseCode(), connect(), getInputStream() or getOutputStream() 。
比如,读取响应状态码:
1 int status = con.getResponseCode();
比如,读取响应头:
1 String contentType = con.getHeaderField("Content-Type");
比如,读取响应文本:
1 BufferedReader in = new BufferedReader( 2 new InputStreamReader(con.getInputStream())); 3 String inputLine; 4 StringBuffer content = new StringBuffer(); 5 while ((inputLine = in.readLine()) != null) { 6 content.append(inputLine); 7 } 8 in.close();
关闭连接:
1 con.disconnect();
结论
在这篇文章中,我们展示了如何通过HttpUrlConnection类来时间Http请求。以下代码可以直接拷贝使用。由于太简单,就不传github了。
1 package com.shlugood.utils; 2 3 import java.io.*; 4 import java.net.HttpURLConnection; 5 import java.net.URL; 6 import java.net.URLEncoder; 7 import java.util.HashMap; 8 import java.util.Map; 9 10 public class HttpUtil { 11 12 private static String POST = "POST"; 13 private static String GET = "GET"; 14 private static String CONTENT_TYPE_URLENCODED = "application/x-www-form-urlencoded"; 15 private static String CONTENT_TYPE_JSON = "application/json"; 16 17 private static String httpRequest(String method, String contentType, String urlStr, HashMap<String,String> paras) 18 throws IOException { 19 URL url = new URL(urlStr); 20 HttpURLConnection con = (HttpURLConnection) url.openConnection(); 21 con.setConnectTimeout(5000); 22 con.setReadTimeout(5000); 23 con.setRequestMethod("POST"); 24 con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 25 26 if(paras != null && !paras.isEmpty()){ 27 con.setDoOutput(true); 28 DataOutputStream out = new DataOutputStream(con.getOutputStream()); 29 out.writeBytes(ParameterStringBuilder.getParamsString(paras)); 30 out.flush(); 31 out.close(); 32 } 33 34 35 BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); 36 String inputLine; 37 StringBuffer content = new StringBuffer(); 38 while ((inputLine = in.readLine()) != null) { 39 content.append(inputLine); 40 } 41 in.close(); 42 con.disconnect(); 43 return content.toString(); 44 } 45 46 private static class ParameterStringBuilder { 47 public static String getParamsString(Map<String, String> params) 48 throws UnsupportedEncodingException { 49 StringBuilder result = new StringBuilder(); 50 51 for (Map.Entry<String, String> entry : params.entrySet()) { 52 result.append(URLEncoder.encode(entry.getKey(), "UTF-8")); 53 result.append("="); 54 result.append(URLEncoder.encode(entry.getValue(), "UTF-8")); 55 result.append("&"); 56 } 57 58 String resultString = result.toString(); 59 return resultString.length() > 0 60 ? resultString.substring(0, resultString.length() - 1) 61 : resultString; 62 } 63 } 64 65 66 public static String httpGetRequest(String url){ 67 try { 68 return httpRequest(GET, CONTENT_TYPE_URLENCODED, url, null); 69 } catch (IOException e) { 70 e.printStackTrace(); 71 } 72 return ""; 73 } 74 75 public static String httpPostRequest(String url, HashMap<String,String> paras){ 76 try { 77 return httpRequest(POST, CONTENT_TYPE_URLENCODED, url, paras); 78 } catch (IOException e) { 79 e.printStackTrace(); 80 } 81 return ""; 82 } 83 }