JavaWeb中轉(zhuǎn)發(fā)與重定向的區(qū)別小結(jié)
在一個(gè)web應(yīng)用中通過兩種方式,可以完成資源的跳轉(zhuǎn):
- 第一種方式:轉(zhuǎn)發(fā)
- 第二種方式:重定向
1. Forward 轉(zhuǎn)發(fā)
轉(zhuǎn)發(fā) :指內(nèi)部轉(zhuǎn)發(fā)。當(dāng)一個(gè)Servlet處理請(qǐng)求的時(shí)候,它可以決定自己不繼續(xù)處理,而是轉(zhuǎn)發(fā)給另一個(gè)Servlet處理。
// 獲取請(qǐng)求轉(zhuǎn)發(fā)器對(duì)象 RequestDispatcher dispatcher = request.getRequestDispatcher("/dept/list"); // 調(diào)用請(qǐng)求轉(zhuǎn)發(fā)器對(duì)象的forward方法完成轉(zhuǎn)發(fā) dispatcher.forward(request, response); // 合并一行代碼 request.getRequestDispatcher("/dept/list").forward(request, response); // 轉(zhuǎn)發(fā)的時(shí)候是一次請(qǐng)求,不管你轉(zhuǎn)發(fā)了多少次。都是一次請(qǐng)求。 // AServlet轉(zhuǎn)發(fā)到BServlet,再轉(zhuǎn)發(fā)到CServlet,再轉(zhuǎn)發(fā)到DServlet,不管轉(zhuǎn)發(fā)了多少次,都在同一個(gè)request當(dāng)中。 // 這是因?yàn)檎{(diào)用forward方法的時(shí)候,會(huì)將當(dāng)前的request和response對(duì)象傳遞給下一個(gè)Servlet。
注意: 因?yàn)檗D(zhuǎn)發(fā)是服務(wù)器內(nèi)部的進(jìn)行的,所以 request.getRequestDispatcher(/不要項(xiàng)目名的).forward(request, response);
編寫的轉(zhuǎn)發(fā)路徑是不要加項(xiàng)目名的。
舉例: 瀏覽器向 AServlet 發(fā)送請(qǐng)求,AServlet 將該請(qǐng)求 “轉(zhuǎn)發(fā)
”給了 BServlet。但是前端的瀏覽器并不知道該請(qǐng)求被 BServlet 處理了,瀏覽器的地址欄上顯示的還是發(fā)送給 AServlet 請(qǐng)求的路徑信息。
package com.RainbowSea.servlet; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; public class AServlet extends HttpServlet { // 地址欄上回車操作是 doGet()請(qǐng)求 @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 轉(zhuǎn)發(fā)到 BServlet,轉(zhuǎn)發(fā)的路徑不要加項(xiàng)目名 request.getRequestDispatcher("/b").forward(request, response); } }
package com.RainbowSea.servlet; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class BServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 設(shè)置瀏覽器端顯示響應(yīng)的,格式類型,以及字符集 response.setContentType("text/html;charSet=UTF-8"); PrintWriter writer = response.getWriter(); writer.println("<h2> 這里是BServlet 的響應(yīng)處理 </h2>"); } }
轉(zhuǎn)發(fā)是由WEB服務(wù)器來控制的。A資源跳轉(zhuǎn)到B資源,這個(gè)跳轉(zhuǎn)動(dòng)作是Tomcat服務(wù)器內(nèi)部完成的 ,而我們前端也就是瀏覽器端是不知道我們服務(wù)器端對(duì)這個(gè)請(qǐng)求內(nèi)部轉(zhuǎn)發(fā)處理了多少次。 并且無論我們服務(wù)器內(nèi)部轉(zhuǎn)發(fā)了多少次,前端瀏覽器都僅僅只會(huì)認(rèn)為僅僅只轉(zhuǎn)發(fā)了一次,也就是僅僅發(fā)送了一次請(qǐng)求 。因?yàn)槲覀兎?wù)器端雖然進(jìn)行了轉(zhuǎn)發(fā)但是,瀏覽器的地址欄上的請(qǐng)求路徑的地址是沒有改變的(還是初始的請(qǐng)求路徑)
轉(zhuǎn)發(fā):是可以將 一個(gè)Servlet 類當(dāng)中的信息轉(zhuǎn)發(fā)到另一個(gè) Servlet 當(dāng)中去的,可以實(shí)現(xiàn) Servlet 數(shù)據(jù)的共享,需要用到 請(qǐng)求域
注意: 請(qǐng)求域的作用域(請(qǐng)求域當(dāng)中存儲(chǔ)的信息),只在一次 請(qǐng)求
范圍內(nèi)有效。而轉(zhuǎn)發(fā)機(jī)制的特點(diǎn)決定了可以實(shí)現(xiàn)請(qǐng)求域的共享:因?yàn)闊o論服務(wù)器內(nèi)部轉(zhuǎn)發(fā)了多少次,前端瀏覽器都只視為是一次請(qǐng)求。
轉(zhuǎn)發(fā)機(jī)制,將AServlett 類當(dāng)中的信息轉(zhuǎn)發(fā)到 BServlet 當(dāng)中去
package com.RainbowSea.servlet; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Date; public class AServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 設(shè)置,在瀏覽器上響應(yīng)的格式類型 Date nowTime = new Date(); // 創(chuàng)建當(dāng)前時(shí)間的 Date 對(duì)象 // 將 nowTime 的數(shù)據(jù)存儲(chǔ)(綁定)到請(qǐng)求域當(dāng)中 request.setAttribute("sysTime",nowTime); // 第一步: 獲取到轉(zhuǎn)發(fā)對(duì)象,注意:/ 開始,不家項(xiàng)目名 , / + 對(duì)應(yīng)跳轉(zhuǎn)的 Servlet 當(dāng)中的 web.xml 當(dāng)中的url映射的路徑 RequestDispatcher requestDispatcher = request.getRequestDispatcher("/B"); // 第二步: 調(diào)用轉(zhuǎn)發(fā)器的forward方法完成跳轉(zhuǎn)/轉(zhuǎn)發(fā) requestDispatcher.forward(request,response); } }
package com.RainbowSea.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class BServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 設(shè)置,在瀏覽器上響應(yīng)的格式類型 response.setContentType("text/html;charSet=utf-8"); PrintWriter writer = response.getWriter(); // 取出請(qǐng)求域當(dāng)中的數(shù)據(jù): 這里的name值與上面setAttribute(String name,Object obj) 保持一致。 Object sysTime = request.getAttribute("sysTime"); writer.println("sysTime = " + sysTime); // 顯示到瀏覽器頁面當(dāng)中的數(shù)據(jù) } }
轉(zhuǎn)發(fā)的下一個(gè)資源必須是一個(gè)Servlet嗎 ?
不一定,只要是Tomcat服務(wù)器當(dāng)中的合法資源,都是可以轉(zhuǎn)發(fā)的。例如:html....
舉例:轉(zhuǎn)發(fā)一個(gè)html文件
注意: 如果對(duì)應(yīng)的不是 Servlet ,默認(rèn)是從項(xiàng)目的中的web目錄開始的,如果是轉(zhuǎn)發(fā)web的目錄下的子目錄的話,需要指定對(duì)應(yīng)的子目錄的文件。
package com.RainbowSea.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class TestServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 轉(zhuǎn)發(fā)的下一個(gè)資源不一定是Servlet 資源, // 只要是Tomcat服務(wù)器當(dāng)中合法的資源,都是可以轉(zhuǎn)發(fā)的,例如: html... // 注意:轉(zhuǎn)發(fā)的時(shí)候,路徑的寫法要注意,轉(zhuǎn)發(fā)的路徑以 “/” 開始,不加項(xiàng)目名 // 默認(rèn)是從項(xiàng)目的中的web目錄開始的,如果是轉(zhuǎn)發(fā)web的目錄下的子目錄的話,需要指定對(duì)應(yīng)的子目錄 // 如下是含有子目錄的 / 表示 web目錄 request.getRequestDispatcher("/test/test.html").forward(request,response); } }
2. Redirect重定向
重定向: 是指當(dāng)瀏覽器請(qǐng)求一個(gè) URL
時(shí),服務(wù)器返回一個(gè)重定向指令,告訴瀏覽器地址已經(jīng)變了,麻煩使用新的URL
再重新發(fā)送新請(qǐng)求。
重定向有兩種: 一種是302響應(yīng),稱為臨時(shí)重定向,一種是301響應(yīng),稱為永久重定向。兩者的區(qū)別是,如果服務(wù)器發(fā)送301永久重定向響應(yīng),瀏覽器會(huì)緩存/hi
到/hello
這個(gè)重定向的關(guān)聯(lián),下次請(qǐng)求/hi
的時(shí)候,瀏覽器就直接發(fā)送/hello
請(qǐng)求了。
說明: 所謂的重定向是將新的路徑交給瀏覽器的地址欄上,然后自動(dòng)執(zhí)行的,而前端的信息獲取是需要指明項(xiàng)目名的,所以:注意:重定向 response.sendRedirect("/項(xiàng)目名/xxx/xx");
的跳轉(zhuǎn)路徑是需要寫明項(xiàng)目名的 。
// 下面這種是 302 響應(yīng),臨時(shí)重定向 // 注意:路徑上要加一個(gè)項(xiàng)目名。為什么? // 瀏覽器發(fā)送請(qǐng)求,請(qǐng)求路徑上是需要添加項(xiàng)目名的。 // 以下這一行代碼會(huì)將請(qǐng)求路徑“/oa/dept/list”發(fā)送給瀏覽器 // 瀏覽器會(huì)自發(fā)的向服務(wù)器發(fā)送一次全新的請(qǐng)求:/oa/dept/list response.sendRedirect("/oa/dept/list");
HttpServletResponse
提供了快捷的redirect()
方法實(shí)現(xiàn)302重定向。如果要實(shí)現(xiàn)301永久重定向,可以這么寫:
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); // 301 response.setHeader("Location", "/hello");
舉例: 瀏覽器向 AServlet 發(fā)送請(qǐng)求,AServlet 將該請(qǐng)求 “重定向
”給了 BServlet。重定向一個(gè)新的URL地址,告訴瀏覽器發(fā)送的處理請(qǐng)求的URL地址改變了,將該請(qǐng)求發(fā)送到該重定向的URL中去,也就是這里的BServlet。
package com.RainbowSea.servlet; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; public class AServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 重定向的路徑 需要加項(xiàng)目名 response.sendRedirect("/servlet12/b"); // 可以使用 request.getContextPath() 獲取到項(xiàng)目名(也就是項(xiàng)目名的根路徑),注意該返回的路徑是帶 “/”的 /項(xiàng)目名 // 所以不要多寫了 “/” // response.sendRedirect(request.getContextPath()+"/b"); } }
package com.RainbowSea.servlet; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class BServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 設(shè)置瀏覽器端顯示響應(yīng)的,格式類型,以及字符集 response.setContentType("text/html;charSet=UTF-8"); PrintWriter writer = response.getWriter(); writer.println("<h2> 這里是BServlet 的響應(yīng)處理 </h2>"); } }
// 重定向: (重新定方向) //response.sendRedirect("/項(xiàng)目名/"); // 重定向時(shí)的路徑當(dāng)中需要以項(xiàng)目名開始,或者說需要添加項(xiàng)目名的 // 因?yàn)樗^的重定向是將新的路徑交給瀏覽器的地址欄上,然后自動(dòng)執(zhí)行的,而前端的信息獲取是需要指明項(xiàng)目名的 // response對(duì)將這個(gè)路徑: /servlet10/B 響應(yīng)給了瀏覽器了。 // 瀏覽器又自發(fā)的向服務(wù)器發(fā)送了一個(gè)全新的請(qǐng)求: http://127.0.0.1:8080/servlet12/a // 所以瀏覽器一共發(fā)送了 "兩次"請(qǐng)求: // 第一次請(qǐng)求: http://127.0.0.1:8080/servlet12/a // 第二次請(qǐng)求: http://127.0.0.1:8080/servlet12/b // 最終瀏覽器地址欄上顯示的地址信息當(dāng)然就是最后那一次請(qǐng)求的地址,所以重定向會(huì)導(dǎo)致瀏覽器 // 地址欄上的地址發(fā)生改變。 // 但是重定向是一次新的請(qǐng)求,是無法獲取到請(qǐng)求域當(dāng)中(只在一次請(qǐng)求中有效)信息的 // 重定向操作是由:跳轉(zhuǎn)到哪個(gè)資源,是由瀏覽器的地址欄說的算的。 // 注意: request.getContextPath()返回的項(xiàng)目名的根路徑是帶有了 "/"了的 response.sendRedirect(request.getContextPath()+"/B");
重定向: 瀏覽器是知道,實(shí)際轉(zhuǎn)發(fā)了多少次請(qǐng)求的。
注意:重定向是“重定向幾次,就會(huì)發(fā)送幾次請(qǐng)求,導(dǎo)致的結(jié)果就是,重定向無法使用 請(qǐng)求域
,因?yàn)檎?qǐng)求域的作用范圍是再一次請(qǐng)求當(dāng)中的,重定向無法實(shí)現(xiàn) Servlet 之間的數(shù)據(jù)共享。
舉例如下:定義了一個(gè) 名為 User 類的JavaBean,在 AServlet 當(dāng)中 new 一個(gè) User 對(duì)象,并存儲(chǔ)到AServlet請(qǐng)求域當(dāng)中,存儲(chǔ)好以后,重定向
給 BServlet ,在BServlet 當(dāng)中想將存儲(chǔ)到 AServlet 請(qǐng)求域當(dāng)中的數(shù)據(jù)取出來,這是不行的,因?yàn)檎?qǐng)求域只在一次請(qǐng)求中有效,而這里重定向了一次,就會(huì)多一次請(qǐng)求也就是兩次請(qǐng)求。無法獲取到AServlet 請(qǐng)求域當(dāng)?shù)拇鎯?chǔ)的數(shù)據(jù)。
package com.RainbowSea.servlet; import java.io.Serializable; import java.util.Objects; /** * 1. 一個(gè)普通的Javabean * 2. 什么是javabean * Java是咖啡,bean 是豆子 * javabean 咖啡豆 * 咖啡是由咖啡豆研磨而成的,寓意:是Java程序是由一個(gè)一個(gè)的javabean組成的 * 3. 一個(gè)javaBean一般是有規(guī)范的 * 有無參數(shù)的構(gòu)造方法 * 屬性私有化 * 對(duì)外提供 get/set()方法 * 重寫toString() * 重寫hashCode() + equals * 實(shí)現(xiàn)java.io.Serializable 接口 可序列化的 */ public class User implements Serializable { private String id; private String name; @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof User)) return false; User user = (User) o; return Objects.equals(getId(), user.getId()) && Objects.equals(getName(), user.getName()); } @Override public int hashCode() { return Objects.hash(getId(), getName()); } public User() { } public User(String id, String name) { this.id = id; this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}'; } }
package com.RainbowSea.servlet; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; public class AServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 創(chuàng)建 User 對(duì)象 User user = new User("001","張三"); // 將 user 對(duì)象存儲(chǔ)到 請(qǐng)求域當(dāng)中 request.setAttribute("userObj",user); // 重定向的路徑 需要加項(xiàng)目名 response.sendRedirect("/servlet12/b"); // 可以使用 request.getContextPath() 獲取到項(xiàng)目名(也就是項(xiàng)目名的根路徑),注意該返回的路徑是帶 “/”的 /項(xiàng)目名 // 所以不要多寫了 “/” // response.sendRedirect(request.getContextPath()+"/b"); } }
package com.RainbowSea.servlet; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class BServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 無法取出 AServlet 請(qǐng)求域當(dāng)中的數(shù)據(jù) Object userObj = request.getAttribute("userObj"); // 設(shè)置瀏覽器端顯示響應(yīng)的,格式類型,以及字符集 response.setContentType("text/html;charSet=UTF-8"); PrintWriter writer = response.getWriter(); writer.println("從AServlet 獲取的數(shù)據(jù):" + userObj); } }
重定向:除了重定向 Servlet 資源以外,還可以重定向其他資源,比如 html...等等只要是服務(wù)器當(dāng)中合法的資源都可以。
舉例:AServlet 重定向一個(gè)名為 index.html 的資源
package com.RainbowSea.servlet; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; public class AServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 重定向一個(gè)名為 index.html 的資源 // request.getContextPath() 返回的是該webapp的項(xiàng)目的根路徑:也就是/項(xiàng)目名,注意是帶 “/”的,不要多寫了 response.sendRedirect(request.getContextPath()+"/index.html"); } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index</title> </head> <body> <h1>Hello World</h1> <h1>你好世界</h1> </body> </html>
3. 轉(zhuǎn)發(fā)使用不當(dāng)?shù)奈:Γ恨D(zhuǎn)發(fā)刷新問題
說明: 由于轉(zhuǎn)發(fā)的機(jī)制的特點(diǎn):無論轉(zhuǎn)發(fā)了服務(wù)器內(nèi)部 轉(zhuǎn)發(fā)
多少次,瀏覽器都視為是請(qǐng)求了一次,而且還是原來(最初的那一次請(qǐng)求,不是轉(zhuǎn)發(fā)之后到的頁面的請(qǐng)求)。這樣的特征,如果使用不當(dāng)?shù)脑?,就?huì)存在 一個(gè)刷新問題:就是你服務(wù)器內(nèi)部雖然發(fā)送了轉(zhuǎn)發(fā)性質(zhì)的跳轉(zhuǎn)到了一個(gè)新的頁面,服務(wù)器內(nèi)部轉(zhuǎn)發(fā)到一個(gè)新的頁面成功后,你在瀏覽器端重新刷新
的話,還是對(duì)最初的一個(gè)URL請(qǐng)求刷新的(因?yàn)檗D(zhuǎn)發(fā)是不會(huì)改變?yōu)g覽器地址欄上的 URL 地址的)。
舉例: 我們?cè)?StudentServlet 中執(zhí)行向數(shù)據(jù)庫的一張名為 studnet
表插入一條記錄的操作。插入成功跳轉(zhuǎn)到一個(gè)名為 succeed.html
的頁面,插入失敗跳轉(zhuǎn)到一個(gè)名為 error.html
失敗的頁面。然后我們對(duì)跳轉(zhuǎn)到 succeed.html* 頁面后,在瀏覽器 執(zhí)行刷新 操作看看會(huì)導(dǎo)致一個(gè)什么樣的結(jié)果 ?
準(zhǔn)備工作: 創(chuàng)建一個(gè)數(shù)據(jù)表,并插入一些數(shù)據(jù)
CREATE TABLE studnet ( `no` VARCHAR(255), `name` VARCHAR(255) ); INSERT INTO studnet (`no`,`name`) VALUES('1','張三'),('2','李四');
package com.RainbowSea.servlet; import com.RainbowSea.DBUtil.DBUtil; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class StudentServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* 思路: 獲取到前端的提交的數(shù)據(jù) 連接數(shù)據(jù)庫,操作數(shù)據(jù)庫 */ // 獲取到前端提交的數(shù)據(jù) request.setCharacterEncoding("UTF-8"); String no = request.getParameter("no"); String name = request.getParameter("name"); Connection connection = null; PreparedStatement preparedStatement = null; // 表示影響數(shù)據(jù)庫的行數(shù) int count = 0; try { // 1. 連接數(shù)據(jù)庫,這里的DBUtil.getConnection(); 是我寫的一個(gè)連接數(shù)據(jù)庫的工具類,大家不用太在意 connection = DBUtil.getConnection(); // 2. 獲取操作數(shù)據(jù)庫對(duì)象,預(yù)編譯sql語句,注意測(cè)試sql是否存在錯(cuò)誤 // 注意: ? 不要加 '', ""單雙引號(hào),不然無法識(shí)別到該占位符的 String sql = "INSERT INTO studnet (`no`,`name`) VALUES(?,?)"; preparedStatement = connection.prepareStatement(sql); // 3. 填充占位符,真正執(zhí)行sql語句 preparedStatement.setString(1,no); preparedStatement.setString(2,name); count = preparedStatement.executeUpdate(); } catch (SQLException e) { throw new RuntimeException(e); } finally { // 4. 釋放資源 DBUtil.close(connection,preparedStatement,null); } if(count == 1) { // 添加成功 // 先用 轉(zhuǎn)發(fā)機(jī)制 ,轉(zhuǎn)發(fā)服務(wù)器內(nèi)部,不要加項(xiàng)目名 request.getRequestDispatcher("/succeed.html").forward(request,response); } else { request.getRequestDispatcher("/error.html").forward(request,response); } } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加成功</title> </head> <body> <h1>添加成功</h1> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加失敗</title> </head> <body> <h1>添加失敗</h1> </body> </html>
實(shí)驗(yàn)效果如下:
但是,如果我們這時(shí)候,在瀏覽器端點(diǎn)擊刷新
,這里我們,點(diǎn)擊了 3
次刷新。導(dǎo)致的結(jié)果如下:
優(yōu)化: 這里我們將轉(zhuǎn)發(fā)
修改為 重定向
就沒有這樣的問題了,因?yàn)橹囟ㄏ蚴菚?huì)改變?yōu)g覽器地址欄上的 URL 地址的(為最后我們重定向(跳轉(zhuǎn))的頁面的URL地址)。
package com.RainbowSea.servlet; import com.RainbowSea.DBUtil.DBUtil; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class StudentServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* 思路: 獲取到前端的提交的數(shù)據(jù) 連接數(shù)據(jù)庫,操作數(shù)據(jù)庫 */ // 獲取到前端提交的數(shù)據(jù) request.setCharacterEncoding("UTF-8"); String no = request.getParameter("no"); String name = request.getParameter("name"); Connection connection = null; PreparedStatement preparedStatement = null; // 表示影響數(shù)據(jù)庫的行數(shù) int count = 0; try { // 連接數(shù)據(jù)庫 connection = DBUtil.getConnection(); // 注意: ? 不要加 '', ""單雙引號(hào),不然無法識(shí)別到該占位符的 String sql = "INSERT INTO studnet (`no`,`name`) VALUES(?,?)"; preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1,no); preparedStatement.setString(2,name); count = preparedStatement.executeUpdate(); } catch (SQLException e) { throw new RuntimeException(e); } finally { // 釋放資源 DBUtil.close(connection,preparedStatement,null); } if(count == 1) { // 添加成功 // 先用 轉(zhuǎn)發(fā)機(jī)制 ,轉(zhuǎn)發(fā)服務(wù)器內(nèi)部,不要加項(xiàng)目名 // request.getRequestDispatcher("/succeed.html").forward(request,response); // 優(yōu)化修改為重定向:重定向前端需要指明 項(xiàng)目名(/項(xiàng)目的根路徑) // request.getContextPath() 該方法可以獲取到: "/項(xiàng)目的根路徑",注意是帶有 / 的,所以不要多寫了 / response.sendRedirect(request.getContextPath()+"/succeed.html"); } else { // request.getRequestDispatcher("/error.html").forward(request,response); response.sendRedirect(request.getContextPath()+"/error.html"); } } }
4. 轉(zhuǎn)發(fā) 與 重定向的區(qū)別
代碼上的區(qū)別:
轉(zhuǎn)發(fā) :轉(zhuǎn)發(fā)的路徑 不要 寫項(xiàng)目名(項(xiàng)目的根路徑)
// 獲取請(qǐng)求轉(zhuǎn)發(fā)器對(duì)象 RequestDispatcher dispatcher = request.getRequestDispatcher("/dept/list"); // 調(diào)用請(qǐng)求轉(zhuǎn)發(fā)器對(duì)象的forward方法完成轉(zhuǎn)發(fā) dispatcher.forward(request, response); // 合并一行代碼 request.getRequestDispatcher("/dept/list").forward(request, response); // 轉(zhuǎn)發(fā)的時(shí)候是一次請(qǐng)求,不管你轉(zhuǎn)發(fā)了多少次。都是一次請(qǐng)求。 // AServlet轉(zhuǎn)發(fā)到BServlet,再轉(zhuǎn)發(fā)到CServlet,再轉(zhuǎn)發(fā)到DServlet,不管轉(zhuǎn)發(fā)了多少次,都在同一個(gè)request當(dāng)中。 // 這是因?yàn)檎{(diào)用forward方法的時(shí)候,會(huì)將當(dāng)前的request和response對(duì)象傳遞給下一個(gè)Servlet。
重定向 :重定向的路徑,需要 寫項(xiàng)目名(項(xiàng)目的根路徑)
response.sendRedirect("/servlet12/b"); // 可以使用 request.getContextPath() 獲取到項(xiàng)目名(也就是項(xiàng)目名的根路徑),注意該返回的路徑是帶 “/”的 /項(xiàng)目名,所以不要多寫了 “/” response.sendRedirect(request.getContextPath()+"/index.html");
形式上有什么區(qū)別 ?
轉(zhuǎn)發(fā)(一次請(qǐng)求)
在瀏覽器地址欄上發(fā)送的請(qǐng)求是:http://localhost:8080/servlet10/a ,最終請(qǐng)求結(jié)束之后,瀏覽器地址欄上的地址還是這個(gè)。沒變。
重定向(兩次請(qǐng)求)
在瀏覽器地址欄上發(fā)送的請(qǐng)求是:http://localhost:8080/servlet10/a ,最終在瀏覽器地址欄上顯示的地址是:http://localhost:8080/servlet10/b(也就是最后重定向的地址)
轉(zhuǎn)發(fā)和重定向的本質(zhì)區(qū)別 ?
轉(zhuǎn)發(fā):是由WEB服務(wù)器來控制的。A資源跳轉(zhuǎn)到B資源,這個(gè)跳轉(zhuǎn)動(dòng)作是Tomcat服務(wù)器內(nèi)部完成的。所以無論 服務(wù)器轉(zhuǎn)發(fā)
了多少次,而我們前端也就是瀏覽器端是不知道我們服務(wù)器端對(duì)這個(gè)請(qǐng)求內(nèi)部轉(zhuǎn)發(fā)處理了多少次。 并且無論我們服務(wù)器內(nèi)部轉(zhuǎn)發(fā)了多少次,前端瀏覽器都僅僅只會(huì)認(rèn)為僅僅只轉(zhuǎn)發(fā)了一次,也就是僅僅發(fā)送了一次請(qǐng)求 。因?yàn)槲覀兎?wù)器端雖然進(jìn)行了轉(zhuǎn)發(fā),但是瀏覽器的地址欄上的請(qǐng)求路徑的地址是沒有改變的(還是初始的請(qǐng)求路徑)
重定向:是瀏覽器完成的。具體跳轉(zhuǎn)到哪個(gè)資源,是瀏覽器說了算。當(dāng)瀏覽器請(qǐng)求一個(gè) URL
時(shí),服務(wù)器返回一個(gè)重定向指令,告訴瀏覽器要處理該請(qǐng)求的URL地址已經(jīng)變了,麻煩使用新的URL
再重新發(fā)送新請(qǐng)求。所以,前端瀏覽器是知道我們重定向了多少次的,而且重定向是“重定向幾次,就會(huì)發(fā)送幾次請(qǐng)求”。
相同點(diǎn): 無論是 “轉(zhuǎn)發(fā)”還是 “重定向”都可以跳轉(zhuǎn)到另外一個(gè)資源當(dāng)中,都是不僅僅是 Servlet 資源,也可以是其他的服務(wù)器上合法的資源比如:html等等。
優(yōu)缺點(diǎn)上的區(qū)別:
轉(zhuǎn)發(fā): 可以通過請(qǐng)求域存儲(chǔ)數(shù)據(jù)的方式,實(shí)現(xiàn)多個(gè)Servlet 數(shù)據(jù)的共享。因?yàn)椋憾嗌俅无D(zhuǎn)發(fā)都是只視為一次請(qǐng)求。但是轉(zhuǎn)發(fā)存在刷新問題 。
重定向:沒有刷新問題,但是無法通過請(qǐng)求域存儲(chǔ)數(shù)據(jù)的方式,實(shí)現(xiàn)多個(gè)Servlet 數(shù)據(jù)的共享。因?yàn)椋?ldquo;重定向幾次,就會(huì)發(fā)送幾次請(qǐng)求”。而請(qǐng)求域只在一次請(qǐng)求范圍有效。
轉(zhuǎn)發(fā)與重定向 舉例圖示上的描述:
轉(zhuǎn)發(fā):
重定向:
5. “重寫向”與“轉(zhuǎn)發(fā)”的合理選擇
- 如果在上一個(gè)Servlet當(dāng)中向request域當(dāng)中綁定了數(shù)據(jù),希望從下一個(gè)Servlet當(dāng)中把request域里面的數(shù)據(jù)取出來,使用轉(zhuǎn)發(fā)機(jī)制。
- 剩下所有的請(qǐng)求均使用重定向。(重定向使用較多。)
重定向的目的是當(dāng)Web應(yīng)用升級(jí)后,如果請(qǐng)求路徑發(fā)生了變化,可以將原來的路徑重定向到新路徑,從而避免瀏覽器請(qǐng)求原路徑找不到資源。
6. 總結(jié):
轉(zhuǎn)發(fā) :指內(nèi)部轉(zhuǎn)發(fā)。當(dāng)一個(gè)Servlet處理請(qǐng)求的時(shí)候,它可以決定自己不繼續(xù)處理,而是轉(zhuǎn)發(fā)給另一個(gè)Servlet處理。
轉(zhuǎn)發(fā)是由WEB服務(wù)器來控制的。A資源跳轉(zhuǎn)到B資源,這個(gè)跳轉(zhuǎn)動(dòng)作是Tomcat服務(wù)器內(nèi)部完成的 ,而我們前端也就是瀏覽器端是不知道我們服務(wù)器端對(duì)這個(gè)請(qǐng)求內(nèi)部轉(zhuǎn)發(fā)處理了多少次。 并且無論我們服務(wù)器內(nèi)部轉(zhuǎn)發(fā)了多少次,前端瀏覽器都僅僅只會(huì)認(rèn)為僅僅只轉(zhuǎn)發(fā)了一次,也就是僅僅發(fā)送了一次請(qǐng)求 。因?yàn)槲覀兎?wù)器端雖然進(jìn)行了轉(zhuǎn)發(fā)但是,瀏覽器的地址欄上的請(qǐng)求路徑的地址是沒有改變的(還是初始的請(qǐng)求路徑) 編寫的轉(zhuǎn)發(fā)路徑是不要加項(xiàng)目名的。
重定向: 是指當(dāng)瀏覽器請(qǐng)求一個(gè) URL
時(shí),服務(wù)器返回一個(gè)重定向指令,告訴瀏覽器地址已經(jīng)變了,麻煩使用新的URL
再重新發(fā)送新請(qǐng)求。
- 重定向有兩種: 一種是302響應(yīng),稱為臨時(shí)重定向,一種是301響應(yīng),稱為永久重定向。兩者的區(qū)別是,如果服務(wù)器發(fā)送301永久重定向響應(yīng),瀏覽器會(huì)緩存
/hi
到/hello
這個(gè)重定向的關(guān)聯(lián),下次請(qǐng)求/hi
的時(shí)候,瀏覽器就直接發(fā)送/hello
請(qǐng)求了。 - 說明: 所謂的重定向是將新的路徑交給瀏覽器的地址欄上,然后自動(dòng)執(zhí)行的,而前端的信息獲取是需要指明項(xiàng)目名的,所以:注意:重定向
response.sendRedirect("/項(xiàng)目名/xxx/xx");
的跳轉(zhuǎn)路徑是需要寫明項(xiàng)目名的 。
注意:轉(zhuǎn)發(fā)機(jī)制存在的一個(gè)刷新問題。
轉(zhuǎn)發(fā) 與 重定向的區(qū)別
到此這篇關(guān)于JavaWeb中轉(zhuǎn)發(fā)與重定向的區(qū)別小結(jié)的文章就介紹到這了,更多相關(guān)JavaWeb 轉(zhuǎn)發(fā)與重定向內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Javaweb-HttpServletResponse的sendRedirectch重定向方式
- 詳解Java從后臺(tái)重定向(redirect)到另一個(gè)項(xiàng)目的方法
- 關(guān)于Javaweb的轉(zhuǎn)發(fā)和重定向詳解
- Java通過httpclient比較重定向和請(qǐng)求轉(zhuǎn)發(fā)
- Java請(qǐng)求轉(zhuǎn)發(fā)和請(qǐng)求重定向區(qū)別詳解
- JavaWeb中請(qǐng)求轉(zhuǎn)發(fā)和請(qǐng)求重定向的區(qū)別以及使用
- Javaweb請(qǐng)求轉(zhuǎn)發(fā)及重定向?qū)崿F(xiàn)詳解
- java 轉(zhuǎn)發(fā)和重定向區(qū)別及實(shí)例代碼
- java轉(zhuǎn)發(fā)和重定向的區(qū)別
- Java中forward轉(zhuǎn)發(fā)與redirect重定向的區(qū)別
相關(guān)文章
Java并發(fā)Map面試線程安全數(shù)據(jù)結(jié)構(gòu)全面分析
本文將探討如何在Java中有效地應(yīng)對(duì)這些挑戰(zhàn),介紹一種強(qiáng)大的工具并發(fā)Map,它能夠幫助您管理多線程環(huán)境下的共享數(shù)據(jù),確保數(shù)據(jù)的一致性和高性能,深入了解Java中的并發(fā)Map實(shí)現(xiàn),包括ConcurrentHashMap和ConcurrentSkipListMap,及相關(guān)知識(shí)點(diǎn)2023-09-09Java中的使用及連接Redis數(shù)據(jù)庫(附源碼)
這篇文章主要介紹了Java中的使用及連接Redis數(shù)據(jù)庫(附源碼),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Spring Cloud負(fù)載均衡及遠(yuǎn)程調(diào)用實(shí)現(xiàn)詳解
這篇文章主要介紹了Spring Cloud負(fù)載均衡及遠(yuǎn)程調(diào)用實(shí)現(xiàn)詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08JavaSwing實(shí)現(xiàn)小型學(xué)生管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了JavaSwing實(shí)現(xiàn)小型學(xué)生管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02淺談java對(duì)象結(jié)構(gòu) 對(duì)象頭 Markword
這篇文章主要介紹了淺談java對(duì)象結(jié)構(gòu) 對(duì)象頭 Markword,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10