【JavaWeb】HttpClient
阿新 • • 發佈:2021-10-05
需要的依賴:
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version></dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
參考視訊:
https://www.bilibili.com/video/BV1W54y1s7BZ
1、原生JDK實現的網路請求
這個在狂神的爬蟲上面有看到過原生的方式
當時還不明白這個技術其實就是後臺的Ajax
@Test public void quickStart() throws IOException { InputStream inputStream = null; InputStreamReader inputStreamReader = null; BufferedReader bufferedReader = null; try { // 原生JDK API 傳送請求String urlString = "https://www.baidu.com/"; URL url = new URL(urlString); URLConnection urlConnection = url.openConnection(); HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection; httpURLConnection.setRequestMethod("GET"); httpURLConnection.setRequestProperty("aaa", "123"); inputStream = httpURLConnection.getInputStream(); inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); bufferedReader = new BufferedReader(inputStreamReader); String line = null; while (null != ( line = bufferedReader.readLine())) { System.out.println(line); } } catch (Exception e) { e.printStackTrace(); } finally { bufferedReader.close(); inputStreamReader.close(); inputStream.close(); } }
2、使用ApacheHttpClient傳送請求:
@Test public void useHttpClient() throws IOException { // 使用 HttpClient Get 無參請求實現 CloseableHttpClient closeableHttpClient = null; CloseableHttpResponse closeableHttpResponse = null; HttpEntity httpEntity = null; try { // 建立一個可關閉的Http客戶端物件 closeableHttpClient = HttpClients.createDefault(); // 要請求的地址 String urlString = "https://www.baidu.com/"; // GET引數需要進行URL編碼處理 String param1 = "orderId=21343000123324"; String param2 = "orderRemark=大三大四1王企鵝1 哇多久啊是巴西 &%……¥%"; param1 = URLEncoder.encode(param1, StandardCharsets.UTF_8.name()); param2 = URLEncoder.encode(param2, StandardCharsets.UTF_8.name()); // 根據地址建立一個Http請求物件 這裡使用的是GET請求物件 // HttpGet httpGet = new HttpGet(urlString); HttpGet httpGet = new HttpGet(urlString + "?" + param1 + "&" + param2); // 瀏覽器偽裝資訊 httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38"); // 防盜鏈設定 https://www.jianshu.com/p/0a1338db6cab httpGet.addHeader("Referer", "https://ntp.msn.cn/"); // 客戶端物件帶著請求物件 執行請求傳送, 返回響應物件 closeableHttpResponse = closeableHttpClient.execute(httpGet); // 可以從響應物件獲取對應的響應資訊 StatusLine statusLine = closeableHttpResponse.getStatusLine(); System.out.println(statusLine); // HTTP/1.1 200 OK // 響應不成功狀態直接結束後續邏輯 if (HttpStatus.SC_OK != statusLine.getStatusCode()) return; ProtocolVersion protocolVersion = statusLine.getProtocolVersion(); // HTTP/1.1 int major = protocolVersion.getMajor(); // 1 主版本協議號 int minor = protocolVersion.getMinor(); // 1 附屬小版本協議號 String protocol = protocolVersion.getProtocol(); // HTTP int statusCode = statusLine.getStatusCode(); // 200 String reasonPhrase = statusLine.getReasonPhrase(); // OK Header[] allHeaders = closeableHttpResponse.getAllHeaders(); for (Header header : allHeaders) { System.out.println("Response Header -> " + header.getName() + " : " + header.getValue()); } // 從響應物件中獲取響應實體物件 httpEntity = closeableHttpResponse.getEntity(); Header contentType = httpEntity.getContentType(); String contentTypeName = contentType.getName(); // Content-Type String contentTypeValue = contentType.getValue(); // Content-Type: text/html;charset=utf-8 // 這個響應頭不常見 // Header contentEncoding = httpEntity.getContentEncoding(); // null // String contentEncodingName = contentEncoding.getName(); // String contentEncodingValue = contentEncoding.getValue(); // 使用實體工具類轉換成字元結果 String httpEntityResult = EntityUtils.toString(httpEntity, StandardCharsets.UTF_8); // System.out.println(httpEntityResult); } catch (Exception exception) { exception.printStackTrace(); } finally { // 最後呼叫此方法確保資源釋放 EntityUtils.consume(httpEntity); closeableHttpResponse.close(); closeableHttpClient.close(); } }
3、下載圖片資源:
@Test public void downloadWebPicture() throws Exception { // 請求傳送 CloseableHttpClient closeableHttpClient = HttpClients.createDefault(); String resource = "https://img.zcool.cn/community/01088b5a052431a801204a0e253198.jpg@1280w_1l_2o_100sh.jpg"; HttpGet httpGet = new HttpGet(resource); CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(httpGet); HttpEntity httpEntity = closeableHttpResponse.getEntity(); // 型別解析 Header contentType = httpEntity.getContentType(); String contentTypeValue = contentType.getValue(); // image/jpeg String fileTypeSuffix = contentTypeValue.split("/")[1]; // jpeg // 檔案下載 byte[] bytes = EntityUtils.toByteArray(httpEntity); String localPath = "C:\\Users\\Administrator\\Desktop\\test." + fileTypeSuffix; OutputStream outputStream = new FileOutputStream(localPath); outputStream.write(bytes); // 資源釋放 outputStream.close(); EntityUtils.consume(httpEntity); closeableHttpResponse.close(); closeableHttpClient.close(); }
4、配置代理主機:
@Test public void proxySettings() throws Exception { CloseableHttpClient closeableHttpClient = HttpClients.createDefault(); String target = "https://www.baidu.com/"; HttpGet httpGet = new HttpGet(target); // 代理主機的資訊 http://www.66ip.cn/ String ip = "176.121.1.81"; int port = 8181; HttpHost httpHost = new HttpHost(ip, port); // 建立請求配置物件 RequestConfig requestConfig = RequestConfig .custom() .setProxy(httpHost) // 設定代理主機的資訊 .build(); // 設定請求配置 httpGet.setConfig(requestConfig); CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(httpGet); HttpEntity httpEntity = closeableHttpResponse.getEntity(); Header[] allHeaders = closeableHttpResponse.getAllHeaders(); for (Header header : allHeaders) { System.out.println("Response Header -> " + header.getName() + " : " + header.getValue()); } String httpEntityResult = EntityUtils.toString(httpEntity, StandardCharsets.UTF_8); System.out.println(httpEntityResult); // 資源釋放 EntityUtils.consume(httpEntity); closeableHttpResponse.close(); closeableHttpClient.close(); }
5、設定超時相關的配置:
@Test public void proxySettings() throws Exception { // 請求傳送 CloseableHttpClient closeableHttpClient = HttpClients.createDefault(); String target = "https://www.baidu.com/"; HttpGet httpGet = new HttpGet(target); // 代理主機的資訊 http://www.66ip.cn/ String ip = "176.121.1.81"; int port = 8181; HttpHost httpHost = new HttpHost(ip, port); // 建立請求配置物件 RequestConfig requestConfig = RequestConfig .custom() .setProxy(httpHost) .setConnectTimeout(5000) // 設定連線超時的上限 TCP3次握手的時限 .setSocketTimeout(3000) // 設定讀取超時上限 從請求的網址中獲取響應資料的間隔時限(因為並不是一次請求就完成了載入) .setConnectionRequestTimeout(3000) // 從HttpClient連線池中獲取connection物件的時限 .build(); // 設定請求配置 httpGet.setConfig(requestConfig); CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(httpGet); HttpEntity httpEntity = closeableHttpResponse.getEntity(); Header[] allHeaders = closeableHttpResponse.getAllHeaders(); for (Header header : allHeaders) { System.out.println("Response Header -> " + header.getName() + " : " + header.getValue()); } String httpEntityResult = EntityUtils.toString(httpEntity, StandardCharsets.UTF_8); System.out.println(httpEntityResult); // 資源釋放 EntityUtils.consume(httpEntity); closeableHttpResponse.close(); closeableHttpClient.close(); }
6、MIME-TYPE 郵件擴充套件型別 與POST請求
mime-type 就是具體檔案型別的前面的所屬規範型別
在Tomcat裡面配置的web.xml資訊就可以看到所有的規範型別了
E:\apache-tomcat-8.5.70\conf\web.xml
片段:
<mime-mapping> <extension>zirz</extension> <mime-type>application/vnd.zul</mime-type> </mime-mapping> <mime-mapping> <extension>zmm</extension> <mime-type>application/vnd.handheld-entertainment+xml</mime-type> </mime-mapping>
常見Content-type:
# 一般html表單提交 傳送的型別 application/x-www-form-urlencoded # html上傳檔案規範的型別 multipart/form-data # 目前主流規範的型別 application/json
POST表單型別提交案例:
1、客戶端請求程式碼
@Test public void postWithFormType() throws Exception { CloseableHttpClient closeableHttpClient = HttpClients.createDefault(); String target = "http://localhost:8080/mvc-framework/test/testMethod2"; // 首先建立POST請求物件 HttpPost httpPost = new HttpPost(target); // 設定表單需要提交的引數 List<NameValuePair> nvpList = new ArrayList<>(); nvpList.add(new BasicNameValuePair("username", "張三")); nvpList.add(new BasicNameValuePair("password", "w123e21")); // 表單型別實體物件 裝填引數資訊 application/x-www-form-urlencoded UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(nvpList, Consts.UTF_8); // 設定表單型別實體物件 httpPost.setEntity(urlEncodedFormEntity); // 客戶端執行請求傳送 CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(httpPost); HttpEntity httpEntity = closeableHttpResponse.getEntity(); System.out.println(EntityUtils.toString(httpEntity, StandardCharsets.UTF_8)); EntityUtils.consume(httpEntity); closeableHttpResponse.close(); closeableHttpClient.close(); }
2、伺服器處理程式碼:
/** * * POST請求測試 * http://localhost:8080/mvc-framework/test/testMethod2 * @param request * @param response */ // @RequestMethod(MethodType.POST) @RequestMapping(value = "/testMethod2", methodType = MethodType.POST) public void testMethod2(HttpServletRequest request, HttpServletResponse response) { System.out.println("testMethod2"); ServletUtil.printAllParamByRequestMap(request); Map<String, Object> requestAttribute = (Map<String, Object>) request.getAttribute(ServletConstants.POST_PARAM_KEY); for (String s : requestAttribute.keySet()) { System.out.println("postParam " + s + ": " + requestAttribute.get(s)); } }
服務這裡沒有對請求做出響應,就是列印看看沒有收到引數資訊:
doPost detected
doGet detected
testMethod2
username: [張三]
password: [w123e21]
postParam password: w123e21
postParam username: 張三
做了兩次列印的原因是第一個列印是直接呼叫ServletAPI實現:
基本的POST表單請求Servlet有做識別處理
public static void printAllParamByRequestMap(HttpServletRequest request) { Map<String, String[]> parameterMap = request.getParameterMap(); for (String s : parameterMap.keySet()) { System.out.println(s + ": " + Arrays.toString(parameterMap.get(s))); } }
第二次列印是從輸入流中獲取識別的
/** * 從POST請求中獲取引數 * @param request * @return * @throws Exception */ public static Map<String, Object> getPostParam(HttpServletRequest request) throws Exception { // 返回引數 Map<String, Object> params = new HashMap<>(); // 獲取內容格式 String contentType = request.getContentType(); if (null == contentType || "".equals(contentType)) throw new ServletException("沒有設定請求頭項Content-Type!!!"); else contentType = contentType.split(";")[0]; // form表單格式 表單形式可以從 ParameterMap中獲取 if (ServletConstants.CONTENT_TYPE_VALUE_URL_ENCODED2.equalsIgnoreCase(contentType)) { // 獲取引數 Map<String, String[]> parameterMap = request.getParameterMap(); if (parameterMap != null) { for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) { params.put(entry.getKey(), entry.getValue()[0]); } } } // json格式 json格式需要從request的輸入流中解析獲取 if (ServletConstants.CONTENT_TYPE_VALUE_JSON2.equalsIgnoreCase(contentType)) { // 使用 commons-io中 IOUtils 類快速獲取輸入流內容 String paramJson = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8); Map parseObject = JSON.parseObject(paramJson, Map.class); params.putAll(parseObject); } return params ; }
也可以不使用UrlEncodedFormEntity ,使用請求頭設定即可
這個意思在視訊裡說錯了,應該是這個Entity已經在Header這麼處理了
// 配置Http請求頭 httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
因為表單的引數還是需要放到Entity裡面傳送過去的
這裡特意看了下Entity的實現結構:
搜尋Entity相關的時候發現這個部落格寫的也很好,涉及到Cookie相關的操作
https://www.cnblogs.com/licl11092/p/9075677.html
POST + JSON型別案例:
SpringMVC接受Post JSON資料時要帶上 @RequestBody給方法
普通Get引數則是@RequestParam
類註解為@RestController就可以不註解@RequestBody方法
在這裡我使用的是封裝的一套MVC,Servlet對JSON引數是不支援的,只能從輸入流自行獲取
這裡JSON引數採用的是StringEntity實現儲存,看了原始碼發現預設是text/plain型別,也允許構造器自行設定型別
@Test public void postWithFormJson() throws Exception { CloseableHttpClient closeableHttpClient = HttpClients.createDefault(); java.lang.String target = "http://localhost:8080/mvc-framework/test/testMethod2"; // 首先建立POST請求物件 HttpPost httpPost = new HttpPost(target); // 設定需要提交的JSON引數 這裡做簡單案例,就不去下載Fastjson來轉換了,自己手寫一個 String jsonParam = "{ \"username\": \"張三\", \"password\": \"w123e21\" }"; // 表單型別實體物件 裝填引數資訊 application/x-www-form-urlencoded StringEntity jsonEntity = new StringEntity(jsonParam , Consts.UTF_8); // 配置Http請求頭 // httpPost.addHeader("Content-Type", "application/json; charset=UTF-8"); jsonEntity.setContentType(new BasicHeader("Content-Type", "application/json; charset=UTF-8")); // StringEntity 設定編碼 jsonEntity.setContentEncoding(Consts.UTF_8.name()); // 設定JSON實體物件 httpPost.setEntity(jsonEntity); // 客戶端執行請求傳送 CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(httpPost); HttpEntity httpEntity = closeableHttpResponse.getEntity(); System.out.println(EntityUtils.toString(httpEntity, StandardCharsets.UTF_8)); EntityUtils.consume(httpEntity); closeableHttpResponse.close(); closeableHttpClient.close(); }