Java通過(guò)httpclient比較重定向和請(qǐng)求轉(zhuǎn)發(fā)
前言:
剛開(kāi)始學(xué)習(xí)Java的web編程時(shí),都會(huì)碰到重定向和請(qǐng)求轉(zhuǎn)發(fā)這兩個(gè)概念,而且一般也會(huì)特別強(qiáng)調(diào)二者的區(qū)別。當(dāng)時(shí),因?yàn)槭莿偨佑|,所以也就似懂非懂的接受了,然后記得編程的時(shí)候要注意二者的區(qū)別。學(xué)習(xí)完計(jì)算機(jī)網(wǎng)絡(luò)后,對(duì)這兩個(gè)概念的理解也更加深刻了,主要是具有了網(wǎng)絡(luò)的層次概念,以及應(yīng)用層的基本知識(shí)。最近使用 httpclient,發(fā)生了一個(gè)錯(cuò)誤,我請(qǐng)求一個(gè)網(wǎng)站地址,發(fā)現(xiàn)并沒(méi)有收到正確的響應(yīng),最后發(fā)現(xiàn)是因?yàn)榘l(fā)生了重定向,但是 httpclient 并沒(méi)有幫我自動(dòng)重定向。發(fā)現(xiàn)這個(gè)問(wèn)題還挺有趣的,所以就來(lái)再深入了解一下。
這里介紹一下:HttpClient 4.x 版本,get請(qǐng)求方法會(huì)自動(dòng)進(jìn)行重定向,而post請(qǐng)求方法不會(huì)自動(dòng)進(jìn)行重定向,這是要注意的地方。我上次發(fā)生錯(cuò)誤,就是使用post提交表單登錄,當(dāng)時(shí)沒(méi)有自動(dòng)重定向。
請(qǐng)求轉(zhuǎn)發(fā)和重定向的區(qū)別
1、重定向是兩次請(qǐng)求,轉(zhuǎn)發(fā)是一次請(qǐng)求,因此轉(zhuǎn)發(fā)的速度要快于重定向。
2、重定向之后地址欄上的地址會(huì)發(fā)生變化,變化成第二次請(qǐng)求的地址,轉(zhuǎn)發(fā)之后地址欄上的地址不會(huì)變化,還是第一次請(qǐng)求的地址。
3、轉(zhuǎn)發(fā)是服務(wù)器行為,重定向是客戶端行為。重定向時(shí)瀏覽器上的網(wǎng)址改變 ,轉(zhuǎn)發(fā)是瀏覽器上的網(wǎng)址不變。
4、重定向是兩次request,轉(zhuǎn)發(fā)只有一次請(qǐng)求。
5、重定向時(shí)的網(wǎng)址可以是任何網(wǎng)址,轉(zhuǎn)發(fā)的網(wǎng)址必須是本站點(diǎn)的網(wǎng)址。
這里重點(diǎn)看第三條和第四條。
HTTP報(bào)文包含響應(yīng)碼、響應(yīng)頭和響應(yīng)體。 這里只說(shuō) 200 和 302. 響應(yīng)碼:2xx(一般是200)。表示請(qǐng)求成功,然后就可以接受響應(yīng)的數(shù)據(jù)。 響應(yīng)碼:3xx(一般為302)。表示重定向,服務(wù)器會(huì)要求客戶端重新發(fā)送一個(gè)請(qǐng)求,服務(wù)器會(huì)發(fā)送一個(gè)響應(yīng)頭 Location,它指定了新請(qǐng)求的 URL 地址。(這里很重要!客戶端需要通過(guò) Location 頭,獲取重定向的地址。)
這里并沒(méi)有提及請(qǐng)求轉(zhuǎn)發(fā),因?yàn)檎?qǐng)求轉(zhuǎn)發(fā)是一種服務(wù)器端的操作,它是在服務(wù)器內(nèi)部進(jìn)行操作的,所以就是一次請(qǐng)求(在客戶端看和普通的請(qǐng)求沒(méi)有區(qū)別)。而重定向不同,它是客戶端的操作,因?yàn)榉?wù)器要求客戶端重新發(fā)送一次請(qǐng)求,所以重定向是兩次請(qǐng)求。這也解釋了為什么重定向后請(qǐng)求參數(shù)會(huì)丟失,因?yàn)楦静皇且淮握?qǐng)求。我這里說(shuō)客戶端,并不說(shuō)瀏覽器,因?yàn)槲覀冇袝r(shí)不一定會(huì)使用瀏覽器作為客戶端,比如爬蟲(chóng)也是是一個(gè)客戶端了。
但是,對(duì)于剛開(kāi)始學(xué)習(xí)的時(shí)候,或者對(duì)于普通用戶來(lái)說(shuō),似乎感覺(jué)不出來(lái)二者的區(qū)別,最多是可以發(fā)現(xiàn)瀏覽器地址是否改變。請(qǐng)求轉(zhuǎn)發(fā)瀏覽器地址不變,而重定向?yàn)g覽器地址會(huì)變化為新的地址。(但是在這個(gè)過(guò)程中并未體現(xiàn)重定向的兩次請(qǐng)求,這是因?yàn)闉g覽器自動(dòng)幫我們進(jìn)行了重定向。)
下面以Java編程來(lái)看看二者的區(qū)別:上面的第三條和第四條。
Java web 部分
TestServlet 類
提供一個(gè)簡(jiǎn)單的 Servlet 類,它的功能和簡(jiǎn)單,它會(huì)攜帶一個(gè) key 參數(shù),如果該參數(shù)存在且值為 “1”,那么就進(jìn)行請(qǐng)求轉(zhuǎn)發(fā)到 “/dispatcher.jsp” 頁(yè)面;否則,就重定向到 “redirect.jsp” 頁(yè)面。
package com.study; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/TestServlet") public class TestServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String key = request.getParameter("key"); if((key != null && key.equals("1"))) { System.out.println("請(qǐng)求轉(zhuǎn)發(fā):key " + key); //請(qǐng)求轉(zhuǎn)發(fā) request.getRequestDispatcher("/dispatcher.jsp").forward(request,response); }else { //重定向 response.sendRedirect("redirect.jsp"); System.out.println("重定向:key " + key); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
dispacher.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>請(qǐng)求轉(zhuǎn)發(fā)</title> </head> <body> <h1>請(qǐng)求轉(zhuǎn)發(fā)</h1> </body> </html>
redirect.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>重定向</title> </head> <body> <h1>重定向</h1> </body> </html>
啟動(dòng)項(xiàng)目測(cè)試
我這里的目的主要是請(qǐng)求轉(zhuǎn)發(fā)和重定向的訪問(wèn)區(qū)別,并不是比較它們二者的其它區(qū)別,所以示例很簡(jiǎn)單。
測(cè)試請(qǐng)求地址:http://localhost:8080/study/TestServlet?key=1
注意:請(qǐng)求地址無(wú)變化。
測(cè)試請(qǐng)求地址:http://localhost:8080/study/TestServlet?key=2 注意:請(qǐng)求地址發(fā)生了變化。
這樣的話,是看不出來(lái)二者在訪問(wèn)上有什么區(qū)別的,但是看不到,不代表它們沒(méi)有區(qū)別,下面通過(guò)代碼來(lái)查看二者在訪問(wèn)方式上的區(qū)別。
使用 HttpClient
- 301 Moved Permanently(永久移動(dòng))
- 302 Found(發(fā)現(xiàn))
- 303 See Other(查看其他)
- 307 Temporary Redirect(臨時(shí)重定向)
由于 HttpClient 4.x 版本會(huì)自動(dòng)重定向,所以我們必須關(guān)閉自動(dòng)重定向,才能跟蹤重定向的過(guò)程。
在 RequestConfig 里面設(shè)置,并設(shè)置超時(shí)時(shí)間(三個(gè))。
//HttpClient4.3中默認(rèn)允許自動(dòng)重定向,導(dǎo)致程序中不能跟蹤跳轉(zhuǎn)情況。 int timeout = 10*1000; RequestConfig config = RequestConfig.custom() .setSocketTimeout(timeout) .setConnectTimeout(timeout) .setConnectionRequestTimeout(timeout) .setRedirectsEnabled(false) //關(guān)閉自動(dòng)重定向,默認(rèn)值為true。 .build();
然后發(fā)送請(qǐng)求時(shí),設(shè)置并允許自動(dòng)重定向。
//創(chuàng)建get請(qǐng)求對(duì)象 HttpGet getMethod = new HttpGet(url); //設(shè)置請(qǐng)求方法關(guān)閉自動(dòng)重定向 getMethod.setConfig(config); //配置信息
這樣當(dāng)我們?cè)L問(wèn)路徑為:http://localhost:8080/study/TestServlet?key=1
服務(wù)器會(huì)進(jìn)行請(qǐng)求轉(zhuǎn)發(fā),但是這是服務(wù)器行為,與客戶端沒(méi)有關(guān)系,所以我們不用關(guān)心,在請(qǐng)求正確的情況下,響應(yīng)碼為 200。
測(cè)試結(jié)果:
當(dāng)我們?cè)L問(wèn)路徑為:http://localhost:8080/study/TestServlet?key=2,服務(wù)器會(huì)要求客戶端進(jìn)行重定向(即要求客戶端請(qǐng)求另一個(gè)地址),這時(shí)會(huì)先收到狀態(tài)碼 302,當(dāng)再次訪問(wèn)成功時(shí)狀態(tài)碼為 200(當(dāng)然了,也許重定向不止一次,但是瀏覽器會(huì)對(duì)重定向次數(shù)有限制)。
如果發(fā)生了重定向,我們需要獲取響應(yīng)頭中的 Location 字段,這里面是重定向的地址。
//讀取新的 URL 地址 Header header = response.getFirstHeader("location"); String newUrl = header.getValue();
注意:重定向是可以訪問(wèn)服務(wù)器外的地址的,服務(wù)器內(nèi)部的地址一般是相對(duì)地址,需要拼接 URL,服務(wù)器外就是絕對(duì) URL 了。
測(cè)試結(jié)果:
完整測(cè)試代碼
package com.learn; import java.io.IOException; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; import org.apache.http.ParseException; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; public class TestRedirect { /** * 重定向是客戶端操作,而請(qǐng)求轉(zhuǎn)發(fā)是服務(wù)端操作 。 * 但是通常用戶使用瀏覽器,并不注意二者的區(qū)別, * 這是因?yàn)闉g覽器自動(dòng)幫我們重定向了。(當(dāng)然了, * 編程還是需要注意的)。 * @throws IOException * @throws ParseException * */ public static void main(String[] args) throws ParseException, IOException { String root = "http://localhost:8080/study/"; //網(wǎng)站的根路徑,因?yàn)橹囟ㄏ虻玫降氖窍鄬?duì)路徑(服務(wù)器內(nèi)部的路徑) //HttpClient4.3中默認(rèn)允許自動(dòng)重定向,導(dǎo)致程序中不能跟蹤跳轉(zhuǎn)情況。 int timeout = 10*1000; RequestConfig config = RequestConfig.custom() .setSocketTimeout(timeout) .setConnectTimeout(timeout) .setConnectionRequestTimeout(timeout) .setRedirectsEnabled(false) //關(guān)閉自動(dòng)重定向,默認(rèn)值為true。 .build(); String url = "http://localhost:8080/study/TestServlet?key=1"; //請(qǐng)求轉(zhuǎn)發(fā)。 //創(chuàng)建 httpclient 對(duì)象 CloseableHttpClient httpClient = HttpClients.createDefault(); //創(chuàng)建get請(qǐng)求對(duì)象 HttpGet getMethod = new HttpGet(url); getMethod.setConfig(config); //配置信息 //執(zhí)行請(qǐng)求,得到響應(yīng)信息 try (CloseableHttpResponse response = httpClient.execute(getMethod)) { HttpEntity entity = null; int statusCode = response.getStatusLine().getStatusCode(); System.out.println("返回值狀態(tài)碼:" + statusCode); if (statusCode == HttpStatus.SC_OK) { //獲取請(qǐng)求轉(zhuǎn)發(fā)的實(shí)體信息 entity = response.getEntity(); if (entity != null) { System.out.println(EntityUtils.toString(entity, "UTF-8")); } } else if (statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { //讀取新的 URL 地址 Header header = response.getFirstHeader("location"); System.out.println(header); if (header != null) { String newUrl = header.getValue(); if (newUrl != null && !newUrl.equals("")) { //使用get方法轉(zhuǎn)向。 HttpGet redirectGet = new HttpGet(root+newUrl); System.out.println("重定向到新的地址:" + redirectGet.getURI()); redirectGet.setConfig(config); //發(fā)送請(qǐng)求,做進(jìn)一步處理。。。 try (CloseableHttpResponse redirectRes = httpClient.execute(redirectGet)) { statusCode = redirectRes.getStatusLine().getStatusCode(); System.out.println("返回值狀態(tài)碼:" + statusCode); if (statusCode == HttpStatus.SC_OK) { //獲取請(qǐng)求轉(zhuǎn)發(fā)的實(shí)體信息 entity = redirectRes.getEntity(); if (entity != null) { System.out.println(EntityUtils.toString(entity, "UTF-8")); } } } } } } } } }
總結(jié)
剛開(kāi)始學(xué)習(xí)的時(shí)候,對(duì)于知識(shí)總是一知半解,然后就開(kāi)始用起來(lái)了。但是,隨著學(xué)習(xí)的深入,就會(huì)發(fā)現(xiàn)自己對(duì)與知識(shí)的理解加深了,當(dāng)然了,不斷地動(dòng)手嘗試,犯錯(cuò)誤才行。因?yàn)閯?dòng)手嘗試才會(huì)熟練,犯錯(cuò)誤才會(huì)明白為什么?這樣是一個(gè)比較好地學(xué)習(xí)方式。我也是某次使用 httpclient 地時(shí)候,發(fā)現(xiàn)自己沒(méi)有考慮重定向這個(gè)問(wèn)題,而且 httpclient 也沒(méi)有幫我自動(dòng)重定向(POST請(qǐng)求方法不會(huì)自動(dòng)重定向),所以這個(gè)問(wèn)題,就卡住了我很長(zhǎng)時(shí)間,因?yàn)槲也恢绬?wèn)題到底出在哪里?最后也是在網(wǎng)上查資料解決的問(wèn)題,所以就想著試著用 httpclient 加深一下自己對(duì)于重定向和請(qǐng)求轉(zhuǎn)發(fā)地理解。
到此這篇關(guān)于Java通過(guò)httpclient比較重定向和請(qǐng)求轉(zhuǎn)發(fā)的文章就介紹到這了,更多相關(guān)httpclient比較重定向和請(qǐng)求轉(zhuǎn)發(fā)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- java?11新特性HttpClient主要組件及發(fā)送請(qǐng)求示例詳解
- Java HttpClient執(zhí)行請(qǐng)求時(shí)配置cookie流程詳細(xì)講解
- Java HttpClient-Restful工具各種請(qǐng)求高度封裝提煉及總結(jié)
- java中httpclient封裝post請(qǐng)求和get的請(qǐng)求實(shí)例
- java爬蟲(chóng)之使用HttpClient模擬瀏覽器發(fā)送請(qǐng)求方法詳解
- java發(fā)送form-data請(qǐng)求實(shí)現(xiàn)文件上傳的示例代碼
- Java請(qǐng)求調(diào)用參數(shù)格式為form-data類型的接口代碼示例
- Java后臺(tái)接收數(shù)據(jù)的三種方式(url、form-data與application/json)
- Java httpclient請(qǐng)求form-data格式并設(shè)置boundary代碼實(shí)現(xiàn)方法
相關(guān)文章
Jenkins配置前端自動(dòng)打包部署全過(guò)程(若依項(xiàng)目)
Jenkins作為一個(gè)開(kāi)源的自動(dòng)化服務(wù)器,廣泛用于持續(xù)集成、持續(xù)部署(CI/CD)流程中,這篇文章主要給大家介紹了關(guān)于Jenkins配置前端自動(dòng)打包部署(若依項(xiàng)目)的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2024-09-09Java Swing組件下拉菜單控件JComboBox用法示例
這篇文章主要介紹了Java Swing組件下拉菜單控件JComboBox用法,結(jié)合具體實(shí)例形式分析了Swing組件下拉菜單控件JComboBox的具體定義、使用方法及相關(guān)使用注意事項(xiàng),需要的朋友可以參考下2017-11-11Spring實(shí)戰(zhàn)之容器中的工程Bean用法示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之容器中的工程Bean用法,結(jié)合實(shí)例形式分析了Sring框架容器中的工程Bean相關(guān)配置、使用操作技巧,需要的朋友可以參考下2019-11-11springboot與數(shù)據(jù)庫(kù)返回?cái)?shù)據(jù)中文亂碼
大家好,本篇文章主要講的是springboot與數(shù)據(jù)庫(kù)返回?cái)?shù)據(jù)中文亂碼,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2022-01-01深入研究spring boot集成kafka之spring-kafka底層原理
這篇文章主要深入研究了spring boot集成kafka如何實(shí)現(xiàn)spring-kafka的底層原理分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02