注冊用戶即可下載全站資源 關注Java幫幫微信公眾號
 

Web-第九天 ServletContext&Response&request學習【悟空教程】

71
發表時間:2018-11-07 16:30來源:Java幫幫-微信公眾號

ServletContext & Response

今日內容介紹

  • 案例一:記錄網站的登錄成功的人數

  • 案例二:文件下載

  • 案例三:點擊切換驗證碼

今日內容學習目標

  • 掌握ServletContext對象的使用

  • 使用response成功發送中文數據

  • 使用response可以設置響應頭

第1章 案例:記錄網站的登錄成功的人數.

1.1 案例需求:

登錄成功后,5秒后跳轉到某個頁面,在頁面中顯示您是第x位登錄成功的用戶.


1.2 相關知識點:

1.2.1 ServletContext

服務器啟動的時候,為每個WEB應用創建一個單獨的ServletContext對象,我們可以使用這個對象存取數據,用這個對象存取的數據可以在整個WEB應用中獲得。可以使用如下方法存取數據

方法名

描述

setAttribute(String name,Object object)

ServletContext中存數據

getAttribute(String name)

ServletContext中取數據

removeAttribute(name)

ServletContext中移除數據

1.3 案例分析:


1.4 代碼實現:

/**

* 登錄代碼的Servlet

*/

public class UserCountServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override

public void init() throws ServletException {

// 初始化一個變量count的值為0.

int count = 0;

// 將這個值存入到ServletContext.

this.getServletContext().setAttribute("count", count);

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

try {

response.setContentType("text/html;charset=UTF-8");

// 1.接收表單提交的參數.

String username = request.getParameter("username");

String password = request.getParameter("password");

// 2.封裝到實體對象中.

User user = new User();

user.setUsername(username);

user.setPassword(password);

// 3.調用業務層處理數據.

UserService userService = new UserService();

User existUser = userService.login(user);

// 4.根據處理結果顯示信息(頁面跳轉).

if(existUser == null){

// 登錄失敗

response.getWriter().println("<h1>登錄失敗:用戶名或密碼錯誤!</h1>");

}else{

// 登錄成功

// 記錄次數:

int count = (int) this.getServletContext().getAttribute("count");

count++;

this.getServletContext().setAttribute("count", count);

response.getWriter().println("<h1>登錄成功:您好:"+existUser.getNickname()+"</h1>");

response.getWriter().println("<h3>頁面將在5秒后跳轉!</h3>");

response.setHeader("Refresh", "5;url=/day09/CountServlet");

}

} catch (Exception e) {

e.printStackTrace();

}

}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

}


public class CountServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

// 獲得Count的值。

response.setContentType("text/html;charset=UTF-8");

int count = (int) this.getServletContext().getAttribute("count");

response.getWriter().println("<h1>您是第"+count+"位登錄成功的用戶!</h1>");

}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

}

1.5 總結:

1.5.1 ServletContext:讀取WEB工程下的文件

ServletContext對象,tomcat為每一個web項目單獨創建的一個上下文(知上知下貫穿全文)對象。就有功能:

1.可以在多個servlet之間共享數據

存放:setAttribute()

獲得:getAttribute()

刪除:removeAttribute()

2.可以獲得當前WEB項目中的指定資源(文件)

3.可以進行整個web項目初始化數據設置

在web.xml可以給整個web項目配置初始化參數

<!-- 全局初始化參數(整個項目) -->

<context-param>

<param-name>參數名</param-name>

<param-value>參數值</param-value>

</context-param>

在實際開發中,有時候可能會需要讀取Web應用中的一些資源文件,比如配置文件,圖片等。為此,在ServletContext接口中定義了一些讀取Web資源的方法,這些方法是依靠Servlet容器來實現的。Servlet容器根據資源文件名相對于Web應用的路徑,返回關聯資源文件的IO流、資源文件在文件系統的絕對路徑等。

方法說明

功能描述

Set getResourcePaths(String path)

返回一個Set集合,集合中包含資源目錄中子目錄和文件的路徑名稱。參數path必須以正斜線(/)開始,指定匹配資源的部分路徑

String getRealPath(String path)

返回資源文件在服務器文件系統上的真實路徑(文件的絕對路徑)。參數path代表資源文件的虛擬路徑,它應該以正斜線開始(/)開始,“/”表示當前Web應用的根目錄,如果Servlet容器不能將虛擬路徑轉換為文件系統的真實路徑,則返回null

URL getResource(String path)

返回映射到某個資源文件的URL對象。參數path必須以正斜線(/)開始,“/”表示當前Web應用的根目錄

InputStream getResourceAsStream(String path)

返回映射到某個資源文件的InputStream輸入流對象。參數path傳遞規則和getResource()方法完全一致

了解了ServletContext接口中用于獲得Web資源路徑的方法后,接下來通過一個案例,分步驟演示如何使用ServletContext對象讀取資源文件


1.5.2 代碼實現

  • 加載配置文件

package cn.com.javahelp.servlet;

import java.io.*;

import java.util.Properties;

import javax.servlet.*;

import javax.servlet.http.*;

public class ReadFileServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

      HttpServletResponse response)throws ServletException, IOException {

 ServletContext context = this.getServletContext();

 PrintWriter out = response.getWriter();

       //獲取相對路徑中的輸入流對象

 InputStream in = context.getResourceAsStream("/WEB-INF/classes/javahelp.properties");

 Properties pros = new Properties();

 pros.load(in);

 out.println("Company=" + pros.getProperty("Company") + "<br>");

 out.println("Address=" + pros.getProperty("Address") + "<br>");

}

public void doPost(HttpServletRequest request,

      HttpServletResponse response)throws ServletException, IOException {

 this.doGet(request, response);

}

}

  • 開發者需要獲取的是資源的絕對路徑。接下來,通過使用getRealPath(String path)方法獲取資源文件的絕對路徑。

package cn.com.javahelp.servlet;

import java.io.*;

import java.util.Properties;

import javax.servlet.*;

import javax.servlet.http.*;

public class ReadFileServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

     HttpServletResponse response)throws ServletException, IOException {

 PrintWriter out = response.getWriter();

  ServletContext context = this.getServletContext();

  //獲取文件絕對路徑

 String path = context

                        .getRealPath("/WEB-INF/classes/javahelp.properties");

 FileInputStream in = new FileInputStream(path);

 Properties pros = new Properties();

 pros.load(in);

 out.println("Company=" + pros.getProperty("Company") + "<br>");

 out.println("Address=" + pros.getProperty("Address") + "<br>");

}

public void doPost(HttpServletRequest request,

 HttpServletResponse response)throws ServletException, IOException {

 this.doGet(request, response);

}

}

1.5.3 初始化參數

  • servlet的初始化參數

通過ServletConfig對象獲得

  • 整個項目的初始化參數

通過ServletContext對象獲得

1.5.4 擴展:類加載器讀取文件

public static void readFile() throws IOException{

// 使用類的加載器來讀取文件.

// 類的加載器用來加載class文件,class文件加載到內存.

InputStream is = ReadFileUtils.class.getClassLoader().getResourceAsStream("db.properties");

Properties properties = new Properties();

properties.load(is);

String driverClass = properties.getProperty("driverClass");

String url = properties.getProperty("url");

String username = properties.getProperty("username");

String password = properties.getProperty("password");

System.out.println(driverClass);

System.out.println(url);

System.out.println(username);

System.out.println(password);

}

第2章 案例:完成文件的下載.

2.1 需求:

在登錄成功后,頁面跳轉到文件下載的列表的頁面,點擊列表中的某些鏈接,下載文件.


2.2 相關知識點:

2.2.1 HttpServletResponse對象

Servlet API中,定義了一個HttpServletResponse接口,它繼承自ServletResponse接口,專門用來封裝HTTP響應消息。由于HTTP響應消息分為狀態行、響應消息頭、消息體三部分,因此,在HttpServletResponse接口中定義了向客戶端發送響應狀態碼、響應消息頭、響應消息體的方法,接下來,本節將針對這些方法進行詳細的講解。


2.2.1.1 發送狀態碼相關的方法

當Servlet向客戶端回送響應消息時,需要在響應消息中設置狀態碼。為此,在HttpServletResponse接口中,定義了兩個發送狀態碼的方法,具體如下。

1).setStatus(int status)方法

該方法用于設置HTTP響應消息的狀態碼,并生成響應狀態行。由于響應狀態行中的狀態描述信息直接與狀態碼相關,而HTTP版本由服務器確定,因此,只要通過setStatus(int status)方法設置了狀態碼,即可實現狀態行的發送。需要注意的是,正常情況下,Web服務器會默認產生一個狀態碼為200的狀態行。


2).sendError(int sc)方法

該方法用于發送表示錯誤信息的狀態碼,例如,404狀態碼表示找不到客戶端請求的資源。在response對象中,提供了兩個重載的sendError(int sc)方法,具體如下:

public void sendError(int code) throws java.io.IOException

public void sendError(int code, String message) throws java.io.IOException

在上面重載的兩個方法中,第一個方法只是發送錯誤信息的狀態碼,而第二個方法除了發送狀態碼外,還可以增加一條用于提示說明的文本信息,該文本信息將出現在發送給客戶端的正文內容中。


2.2.1.2 發送響應消息頭相關的方法

當Servlet向客戶端發送響應消息時,由于HTTP協議的響應頭字段有很多種,為此,在HttpServletResponse接口中,定義了一系列設置HTTP響應頭字段的方法,如表4-1所示。

表4-1 設置響應消息頭字段的方法

      方法聲明

功能描述

void addHeader(String name, String value)

這兩個方法都是用來設置HTTP協議的響應頭字段,其中,參數name用于指定響應頭字段的名稱,參數value用于指定響應頭字段的值。不同的是,addHeader()方法可以增加同名的響應頭字段,而setHeader()方法則會覆蓋同名的頭字段

void setHeader(String name, String value)

void addIntHeader(String name,

int value)

這兩個方法專門用于設置包含整數值的響應頭。避免了使用addHeader()setHeader()方法時,需要將int類型的設置值轉換為String類型的麻煩

void setIntHeader(String name,

int value)

void setContentLength(int len)

該方法用于設置響應消息的實體內容的大小,單位為字節。對于HTTP協議來說,這個方法就是設置Content-Length響應頭字段的值

void setContentType(String type)

該方法用于設置Servlet輸出內容的MIME類型,對于HTTP議來說,就是設置Content-Type響應頭字段的值。例如,如果發送到客戶端的內容是jpeg格式的圖像數據,就需要將響應頭字段的類型設置為“image/jpeg”。需要注意的是,如果響應的內容為文本,setContentType()方法的還可以設置字符編碼,如:text/html;charset=UTF-8

void setCharacterEncoding(String charset)

該方法用于設置輸出內容使用的字符編碼,對HTTP 協議來說,就是設置Content-Type頭字段中的字符集編碼部分。一般不使用。

需要注意的是,在表4-1列舉的一系列方法中,addHeader()setHeader()addIntHeader()setIntHeader()方法都是用于設置各種頭字段的,而setContetType() setCharacterEncoding()方法用于設置字符編碼,這些設置字符編碼的方法可以有效解決亂碼問題。


2.2.1.3 發送響應消息體相關的方法

由于在HTTP響應消息中,大量的數據都是通過響應消息體傳遞的,因此,ServletResponse遵循以IO流傳遞大量數據的設計理念。在發送響應消息體時,定義了兩個與輸出流相關的方法,具體如下。

1).getOutputStream()方法

該方法所獲取的字節輸出流對象為ServletOutputStream類型。由于ServletOutputStream是OutputStream的子類,它可以直接輸出字節數組中的二進制數據。因此,要想輸出二進制格式的響應正文,就需要使用getOutputStream()方法。


2).getWriter()方法

該方法所獲取的字符輸出流對象為PrintWriter類型。由于PrintWriter類型的對象可以直接輸出字符文本內容,因此,要想輸出內容全為字符文本的網頁文檔,需要使用getWriter()方法。

了解了response對象發送響應消息體的兩個方法后,接下來,通過一個案例來學習這兩個方法的使用。

在Eclipse中創建Web項目chapter04,在項目的src目錄下,新建一個名稱為cn.com.javahelp. chapter04.response的包,在包中編寫一個名為PrintServlet的Servlet類,該類中使用了response對象的getOutPutStream()方法獲取輸出流對象,如文件4-1所示。

文件4-1 PrintServlet.java

package cn.com.javahelp.chapter04.response;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class PrintServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

HttpServletResponse response)throws ServletException, IOException {

String data = "javahelp";

// 獲取字節輸出流對象

 OutputStream out = response.getOutputStream();

out.write(data.getBytes());// 輸出信息

}

public void doPost(HttpServletRequest request,

HttpServletResponse response)throws ServletException, IOException {

doGet(request, response);

}

}

web.xml中配置完PrintServlet的映射后,啟動Tomcat服務器,在瀏覽器的地址欄中輸入地址“http://localhost:8080/chapter04/PrintServlet”訪問PrintServlet,瀏覽器的顯示結果如圖4-2所示。

圖 4-2

從圖4-2中可以看出,瀏覽器顯示出了response對象響應的數據。由此可見,response對象的getOutputStream()方法可以很方便的發送響應消息體。

接下來,對文件4-1進行修改,使用getWriter()方法發送消息體,修改后的代碼如文件4-2所示。

文件4-2 PrintServlet.java

package cn.com.javahelp.chapter04.response;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class PrintServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

  HttpServletResponse response)throws ServletException, IOException {

 String data = "javahelp";

 // 獲取字符輸出流對象

 PrintWriter print = response.getWriter();

 print.write(data); // 輸出信息

}

public void doPost(HttpServletRequest request,

        HttpServletResponse response)throws ServletException, IOException {

 doGet(request, response);

}

}


重啟Tomcat服務器,在瀏覽器的地址欄中輸入地址 “http://localhost:8080/chapter04/PrintServlet”再次訪問PrintServlet,瀏覽器的顯示結果同樣如圖4-2所示。

注意:

雖然response對象的getOutputStream()和getWriter()方法都可以發送響應消息體,但是,它們之間互相排斥,不可同時使用,否則會發生IllegalStateException異常,如圖4-3所示。

圖4-3

4-3中發生異常的原因就是在Servlet中,調用response.getWriter() 方法之前已經調用了response.getOutputStream() 方法。


2.2.2 文件下載原理

對于文件下載,相信讀者并不會陌生,因為通常在上網時所下的圖片、文檔和影片等都是文件下載的范疇。現在很多網站都提供了下載各類資源的功能,因此在學習Web開發過程中,有必要學習文件下載的實現方式。

實現文件下載功能比較簡單,通常情況下,不需要使用第三方組件實現,而是直接使用Servlet類和輸入/輸出流實現。與訪問服務器文件不同的是,要實現文件的下載,不僅需要指定文件的路徑,還需要在HTTP協議中設置兩個響應消息頭,具體如下:

//設定接收程序處理數據的方式

Content-Disposition: attachment; filename =

//設定實體內容的MIME類型

Content-Typeapplication/x-msdownload

瀏覽器通常會直接處理響應的實體內容,需要在HTTP響應消息中設置兩個響應消息頭字段,用來指定接收程序處理數據內容的方式為下載方式。當單擊“下載”超鏈接時,系統將請求提交到對應的Servlet。在該Servlet中,首先獲取下載文件的地址,并根據該地址創建文件字節輸入流,然后通過該流讀取下載文件內容,最后將讀取的內容通過輸出流寫到目標文件中。


2.3 代碼實現

public class DownloadServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

// 1.接收參數

String filename = request.getParameter("filename");

// 2.完成文件下載:

// 2.1設置Content-Type

String type = this.getServletContext().getMimeType(filename);

response.setHeader("Content-Type", type);

// 2.2設置Content-Disposition

response.setHeader("Content-Disposition", "attachment;filename="+filename);

// 2.3設置文件的InputStream.

String realPath = this.getServletContext().getRealPath("/download/"+filename);

InputStream is = new FileInputStream(realPath);

// 獲得response的輸出流:

OutputStream os = response.getOutputStream();

int len = 0;

byte[] b = new byte[1024];

while((len = is.read(b))!= -1){

os.write(b, 0, len);

}

is.close();

}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

}


2.4 總結:

2.4.1 中文文件的下載:

  • Firefox瀏覽器下載中文文件的時候采用的是Base64的編碼.

  • IE瀏覽器,谷歌瀏覽器等,下載中文文件的時候采用的URL的編碼.

  • 可以使用工具類“DownloadUtils.java”進行處理

/**

* 文件下載的Servlet

*/

public class DownloadServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

// 1.接收參數

String filename = new String(request.getParameter("filename"));

System.out.println(filename);

// 2.完成文件下載:

// 2.1設置Content-Type

String type = this.getServletContext().getMimeType(filename);

response.setHeader("Content-Type", type);

// 2.3設置文件的InputStream.

String realPath = this.getServletContext().getRealPath("/download/"+filename);

// 根據瀏覽器的類型處理中文文件的亂碼問題:

String agent = request.getHeader("User-Agent");

System.out.println(agent);

if(agent.contains("Firefox")){

filename = base64EncodeFileName(filename);

}else{

filename = URLEncoder.encode(filename,"UTF-8");

}


// 2.2設置Content-Disposition

response.setHeader("Content-Disposition", "attachment;filename="+filename);

InputStream is = new FileInputStream(realPath);

// 獲得response的輸出流:

OutputStream os = response.getOutputStream();

int len = 0;

byte[] b = new byte[1024];

while((len = is.read(b))!= -1){

os.write(b, 0, len);

}

is.close();

}

public static String base64EncodeFileName(String fileName) {

BASE64Encoder base64Encoder = new BASE64Encoder();

try {

return "=?UTF-8?B?"

+ new String(base64Encoder.encode(fileName

.getBytes("UTF-8"))) + "?=";

} catch (UnsupportedEncodingException e) {

e.printStackTrace();

throw new RuntimeException(e);

}

}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

}


2.4.2 response解決中文輸出亂碼問題

在開發中,如果需要響應給瀏覽器中文數據,我們需要使用getWriter()方法,tomcat使用getWriter()處理字符時,默認編碼時ISO8859-1。響應給瀏覽器的數據,此時為亂碼。在開發中我們需要使用setCharacterEncoding方法來設置編碼。此時響應給瀏覽器的數據為正常數據,但瀏覽器查看編碼,可能使我們查看到內容仍為亂碼,需要使用響應頭 content-type進行設置。

在HttpServletResponse對象中,提供了兩種解決亂碼的方案,具體如下:

  • 第一種方式:

// 設置HttpServletResponse使用utf-8編碼,此數據提供給tomcat使用。

response.setCharacterEncoding("utf-8");

// 通知瀏覽器查看編碼為utf-8

response.setHeader("Content-Type","text/html;charset=utf-8");

  • 第二種方式:

// 使用通用方法,setContentType底層自動調用了setCharacterEncoding

response.setContentType("text/html;charset=utf-8");

通常情況下,為了使代碼更加簡潔,會采用第二種方式。實現代碼如下文所示:

package cn.com.javahelp.chapter04.response;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class ChineseServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

      HttpServletResponse response) throws ServletException, IOException {

        //設置字符編碼

         response.setContentType("text/html;charset=utf-8");

 String data="中國";

 PrintWriter out = response.getWriter();

 out.println(data);

}

public void doPost(HttpServletRequest request,

      HttpServletResponse response) throws ServletException, IOException {

 doGet(request,response);

}

}

啟動Tomcat服務器,在瀏覽器的地址欄中輸入地址“http://localhost:8080/day14/ChineseServlet”訪問ChineseServlet,瀏覽器顯示出了正確的中文字符,如下圖所示。


第3章 案例:點擊切換驗證碼

3.1 需求:

在訪問登錄頁面時,需要生產驗證碼。從而防止用戶使用程序惡意登錄。


3.2 分析

3.3 代碼實現

  • 步驟1:修改login.jsp頁面,確定驗證碼圖片顯示的位置

  • 步驟2:拷貝VerifyCodeServlet實現類

public class VerifyCodeServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

//生成圖片

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

//1 高和寬

int height = 30;

int width = 60;

String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";

Random random = new Random();

//2 創建一個圖片

BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

//3 獲得畫板

Graphics g = image.getGraphics();

//4 填充一個矩形

// * 設置顏色

g.setColor(Color.BLACK);

g.fillRect(0, 0, width, height);

g.setColor(Color.WHITE);

g.fillRect(1, 1, width-2, height-2);

// * 設置字體

g.setFont(new Font("宋體", Font.BOLD|Font.ITALIC, 25));

//5 寫隨機字

for(int i = 0 ; i < 4 ; i ++){

// 設置顏色--隨機數

g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));

// 獲得隨機字

int index = random.nextInt(data.length());

String str = data.substring(index, index + 1);

// 寫入

g.drawString(str, width/6 * (i + 1), 20);

}

//6 干擾線

for(int i = 0 ; i < 3 ; i ++){

// 設置顏色--隨機數

g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));

// 隨機繪制先

g.drawLine(random.nextInt(width), random.nextInt(height), random.nextInt(width),random.nextInt(height));

// 隨機點

g.drawOval(random.nextInt(width), random.nextInt(height), 2, 2);

}

//end 將圖片響應給瀏覽器

ImageIO.write(image, "jpg", response.getOutputStream());

}

protected void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

}

  • 步驟3:編寫web.xml文件

<servlet>

<servlet-name>VerifyCodeServlet</servlet-name>

<servlet-class>cn.com.javahelp.web.servlet.VerifyCodeServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>VerifyCodeServlet</servlet-name>

<url-pattern>/VerifyCodeServlet</url-pattern>

</servlet-mapping>

3.4 點擊切換

  • 添加點擊圖片或文字,更換驗證碼

  • 代碼實現,點擊圖片或文件,都可以更換驗證碼圖片。

<div>

<img id="imgId" onclick="changeImg()" style="cursor: pointer;"

src="${pageContext.request.contextPath}/VerifyCodeServlet"/>  

<a href="javascript:void(0)" onclick="changeImg()" >換一張</a>

<script type="text/javascript">

function changeImg(){

$("#imgId").attr("src","${pageContext.request.contextPath}/VerifyCodeServlet?t="+new Date());

}

</script>

</div>

request

今日內容介紹

  • 案例一:完成用戶注冊的案例

  • 案例二:完善用戶登錄,顯示錯誤信息

今日內容學習目標

  • 使用request獲得表單請求參數

  • 使用request完成請求轉發,并成功在一次請求中傳遞數據

第4章 案例:完成用戶注冊的功能:

4.1 需求:

網站首頁上點擊注冊的鏈接,跳轉到注冊頁面,在注冊頁面中輸入信息.完成注冊:(將數據保存到數據庫中).


4.2 相關知識點:

4.2.1 HttpServletRequest對象

Servlet API中,定義了一個HttpServletRequest接口,它繼承自ServletRequest接口,專門用來封裝HTTP請求消息。由于HTTP請求消息分為請求行、請求消息頭和請求消息體三部分,因此,在HttpServletRequest接口中定義了獲取請求行、請求頭和請求消息體的相關方法,接下來,本節將針對這些方法進行詳細地講解。


4.2.1.1 獲取請求行信息的相關方法

當訪問Servlet時,會在請求消息的請求行中,包含請求方法、請求資源名、請求路徑等信息,為了獲取這些信息,在HttpServletRequest接口中,定義了一系列用于獲取請求行的方法,如表4-2所示。

表4-2 獲取請求行的相關方法

方法聲明

功能描述

String getMethod()

該方法用于獲取HTTP請求消息中的請求方式(如GET、POST等)

String getRequestURI()

該方法用于獲取請求行中資源名稱部分,即位于URL的主機和端口之后、參數部分之前的部分

String getQueryString()

該方法用于獲取請求行中的參數部分,也就是資源路徑后面問號(?)以后的所有內容

String getProtocol()

該方法用于獲取請求行中的協議名和版本,例如,HTTP/1.0或HTTP/1.1

String getContextPath()

該方法用于獲取請求URL中屬于WEB應用程序的路徑,這個路徑以“/”開頭,表示相對于整個WEB站點的根目錄,路徑結尾不含“/”。如果請求URL屬于WEB站點的根目錄,那么返回結果為空字符串("")

String getServletPath()

該方法用于獲取Servlet的名稱或Servlet所映射的路徑

String getRemoteAddr()

該方法用于獲取請求客戶端的IP地址,其格式類似于“192.168.0.3

String getRemoteHost()

該方法用于獲取請求客戶端的完整主機名,其格式類似于“pc1.polkscan.com”。需要注意的是,如果無法解析出客戶機的完整主機名,該方法將會返回客戶端的IP地址

int getRemotePort()

該方法用于獲取請求客戶端網絡連接的端口號

String getLocalAddr()

該方法用于獲取Web服務器上接收當前請求網絡連接的IP地址

String getLocalName()

該方法用于獲取Web服務器上接收當前網絡連接IP所對應的主機名

int getLocalPort()

該方法用于獲取Web服務器上接收當前網絡連接的端口號

String getServerName()

該方法用于獲取當前請求所指向的主機名,即HTTP請求消息中Host頭字段所對應的主機名部分

int getServerPort()

該方法用于獲取當前請求所連接的服務器端口號,即如果HTTP請求消息中Host頭字段所對應的端口號部分

String getScheme()

該方法用于獲取請求的協議名,例如httphttpsftp

StringBuffer getRequestURL()

該方法用于獲取客戶端發出請求時的完整URL,包括協議、服務器名、端口號、資源路徑等信息,但不包括后面的查詢參數部分。注意,getRequestURL()方法返回的結果是StringBuffer類型,而不是String類型,這樣更便于對結果進行修改

在表4-2中,列出了一系列用于獲取請求消息行信息的方法,為了使讀者更好地理解這些方法,接下來,通過一個案例來演示這些方法的使用。

在chapter04項目的src目錄下,新建一個名稱為cn.com.javahelp.request的包,在包中編寫一個名為RequestLineServlet的類,該類中編寫了用于獲取請求行中相關信息的方法,如文件4-3所示。

文件4-3  RequestLineServlet.java

package cn.com.javahelp.request;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class RequestLineServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

      HttpServletResponse response)throws ServletException, IOException {

 response.setContentType("text/html;charset=utf-8");

 PrintWriter out = response.getWriter();

 // 獲取請求行的相關信息

 out.println("getMethod : " + request.getMethod() + "<br>");

 out.println("getRequestURI : " + request.getRequestURI() + "<br>");

 out.println("getQueryString:"+request.getQueryString() + "<br>");

 out.println("getProtocol : " + request.getProtocol() + "<br>");

 out.println("getContextPath:"+request.getContextPath() + "<br>");

 out.println("getPathInfo : " + request.getPathInfo() + "<br>");

 out.println("getPathTranslated : "

                      + request.getPathTranslated() + "<br>");

 out.println("getServletPath:"+request.getServletPath() + "<br>");

 out.println("getRemoteAddr : " + request.getRemoteAddr() + "<br>");

 out.println("getRemoteHost : " + request.getRemoteHost() + "<br>");

 out.println("getRemotePort : " + request.getRemotePort() + "<br>");

 out.println("getLocalAddr : " + request.getLocalAddr() + "<br>");

 out.println("getLocalName : " + request.getLocalName() + "<br>");

 out.println("getLocalPort : " + request.getLocalPort() + "<br>");

 out.println("getServerName : " + request.getServerName() + "<br>");

 out.println("getServerPort : " + request.getServerPort() + "<br>");

 out.println("getScheme : " + request.getScheme() + "<br>");

 out.println("getRequestURL : " + request.getRequestURL() + "<br>");

}

public void doPost(HttpServletRequest request,

       HttpServletResponse response)throws ServletException, IOException {

 doGet(request, response);

}

}

web.xml中配置完RequestLineServlet的映射后,啟動Tomcat服務器,在瀏覽器的地址欄中輸入地址

http://localhost:8080/ day15/RequestLineServlet”訪問RequestLineServlet,瀏覽器的顯示結果如圖4-14所示。

圖4-14

從圖4-14中可以看出,瀏覽器顯示出了請求RequestLineServlet時,發送的請求行信息。由此可見,通過HttpServletRequest對象可以很方便的獲取到請求行的相關信息。


4.2.1.2 獲取請求消息頭的相關方法

當請求Servlet時,需要通過請求頭向服務器傳遞附加信息,例如,客戶端可以接收的數據類型,壓縮方式,語言等等。為此,在HttpServletRequest接口中,定義了一系列用于獲取HTTP請求頭字段的方法,如表4-3所示。

表4-3 獲取請求消息頭的方法

方法聲明

功能描述

String getHeader(String name)

該方法用于獲取一個指定頭字段的值,如果請求消息中沒有包含指定的頭字段,getHeader()方法返回null;如果請求消息中包含有多個指定名稱的頭字段,getHeader()方法返回其中第一個頭字段的值

Enumeration getHeaders(String name)

該方法返回一個Enumeration集合對象,該集合對象由請求消息中出現的某個指定名稱的所有頭字段值組成。在多數情況下,一個頭字段名在請求消息中只出現一次,但有時候可能會出現多次

Enumeration getHeaderNames()

該方法用于獲取一個包含所有請求頭字段的Enumeration對象

int getIntHeader(String name)

該方法用于獲取指定名稱的頭字段,并且將其值轉為int類型。需要注意的是,如果指定名稱的頭字段不存在,返回值為-1;如果獲取到的頭字段的值不能轉為int類型,將發生NumberFormatException異常

Long getDateHeader(String name)

該方法用于獲取指定頭字段的值,并將其按GMT時間格式轉換成一個代表日期/時間的長整數,這個長整數是自197011000秒算起的以毫秒為單位的時間值

在表4-3中,列出了一系列用于讀取HTTP請求消息頭字段的方法,為了更好地掌握這些方法,接下來通過一個案例來學習這些方法的使用。

在cn.com.javahelp.request包中編寫一個名為RequestHeadersServlet的類,該類使用getHeaderNames()方法來獲取請求消息頭信息,如文件4-4所示。

文件4-4 RequestHeadersServlet.java

package cn.com.javahelp.request;

import java.io.IOException;

import java.io.PrintWriter;

import java.util.Enumeration;

import javax.servlet.*;

import javax.servlet.http.*;

public class RequestHeadersServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

        HttpServletResponse response)throws ServletException, IOException {

 response.setContentType("text/html;charset=utf-8");

 PrintWriter out = response.getWriter();

         // 獲取請求消息中所有頭字段

 Enumeration headerNames = request.getHeaderNames();

 // 使用循環遍歷所有請求頭,并通過getHeader()方法獲取一個指定名稱的頭字段

 while (headerNames.hasMoreElements()) {

 String headerName = (String) headerNames.nextElement();

 out.print(headerName + " : "

                       + request.getHeader(headerName)+ "<br>");

 }

}

public void doPost(HttpServletRequest request,

     HttpServletResponse response)throws ServletException, IOException {

 doGet(request, response);

}

}

web.xml中配置完RequestHeadersServlet映射后,啟動Tomcat服務器,在瀏覽器的地址欄中輸入地址“http://localhost:8080/day15/RequestHeadersServlet”訪問RequestHeadersServlet,瀏覽器的顯示結果如圖4-15所示。


4.2.1.3 獲取請求參數

在實際開發中,經常需要獲取用戶提交的表單數據,例如,用戶名、密碼、電子郵件等,為了方便獲取表單中的請求參數,在HttpServletRequest接口中,定義了一系列獲取請求參數的方法,如表4-4所示。

表4-4 獲取請求參數的方法

      方法聲明

功能描述

String getParameter(String name)

該方法用于獲取某個指定名稱的參數值,如果請求消息中沒有包含指定名稱的參數,getParameter()方法返回null;如果指定名稱的參數存在但沒有設置值,則返回一個空串;如果請求消息中包含有多個該指定名稱的參數,getParameter()方法返回第一個出現的參數值

String[] getParameterValues(String name)

HTTP請求消息中可以有多個相同名稱的參數(通常由一個包含有多個同名的字段元素的FORM表單生成),如果要獲得HTTP請求消息中的同一個參數名所對應的所有參數值,那么就應該使用getParameterValues()方法,該方法用于返回一個String類型的數組

Enumeration getParameterNames()

getParameterNames()方法用于返回一個包含請求消息中所有參數名的Enumeration對象,在此基礎上,可以對請求消息中的所有參數進行遍歷處理

Map getParameterMap()

getParameterMap()方法用于將請求消息中的所有參數名和值裝入進一個Map對象中返回

4-4中,列出了HttpServletRequest獲取請求參數的一系列方法。其中,getParameter()方法用于獲取某個指定的參數,而getParameterValues()方法用于獲取多個同名的參數。接下來,通過一個具體的案例,分步驟講解這兩個方法的使用,具體如下:


1)在day15項目的WebContent根目錄下編寫一個表單文件form.html,如文件4-5所示。

文件4-5 form.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

                         "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>Insert title here</title>

</head>

<body>

<form action="/day15/RequestParamsServlet" method="POST">

用戶名:<input type="text" name="username"><br>

  碼:<input type="password" name="password">

     <br>

愛好:

<input type="checkbox" name="hobby" value="sing">唱歌

<input type="checkbox" name="hobby" value="dance">跳舞

<input type="checkbox" name="hobby" value="football">足球<br>

<input type="submit" value="提交">

</form>

</body>

</html>


2)在cn.com.javahelp.request包中編寫一個名稱為RequestParamsServletServlet類,使用該Servlet獲取請求參數,如文件4-6所示。

文件4-6 RequestParamsServlet.java

package cn.com.javahelp.request;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class RequestParamsServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

     HttpServletResponse response)throws ServletException, IOException {

 String name = request.getParameter("username");

String password = request.getParameter("password");

 System.out.println("用戶名:" + name);

 System.out.println("密  碼:" + password);

 // 獲取參數名為“hobby”的值

 String[] hobbys = request.getParameterValues("hobby");

 System.out.print("愛好:");

for (int i = 0; i < hobbys.length; i++) {

 System.out.print(hobbys[i] + ", ");

 }

}

public void doPost(HttpServletRequest request,

      HttpServletResponse response)throws ServletException, IOException {

 doGet(request, response);

}

}

在文件4-14中,由于參數名為“hobby”的值可能有多個,因此,需要使用getParameterValues()方法,獲取多個同名參數的值,返回一個String類型的數組,通過遍歷數組,打印出每個“hobby”參數對應的值。


3)在web.xml中配置完RequestParamsServlet映射后,啟動Tomcat服務器,在瀏覽器的地址欄中輸入地址“http://localhost:8080/ day15/form.html”訪問form.html頁面,并填寫表單相關信息,填寫后的頁面如圖4-18所示。

圖4-18 運行結果

4)單擊圖4-18所示的“提交”按鈕,在Eclipse的控制臺打印出了每個參數的信息,如圖4-19所示。

圖4-19 運行結果

4.2.2 BeanUtils

BeanUtils 是 Apache commons組件的成員之一,主要用于簡化JavaBean封裝數據的操作。它可以給JavaBean封裝一個字符串數據,也可以將一個表單提交的所有數據封裝到JavaBean中。

使用第三方工具,需要導入jar包:

BeanUtils工具常用工具類有兩個:BeanUtilsConvertUtilsBeanUtils用于封裝數據,ConvertUtils用于處理類型轉換,常用API如下


方法

描述

BeanUtils對象

populate(Object bean, Map<String,String[]> properties)  

Map數據封裝到指定Javabean中,一般用于將表單的所有數據封裝到javabean




ConvertUtils

register(Converter converter, Class clazz)

注冊類型轉換器

  • 提供JavaBean User ,并提供對應的構造方法

public class User {

private String username;

private String password;

private int age;

private Date birthday;

public User() {

}


public User(String username, String password, int age, Date birthday) {

...

}

  • 功能1:封裝表單數據,使用Map 模擬 request.getParameterMap()

@Test

public void demo01() throws Exception{

//map數據封裝給user

Map<String,String[]> properties = new HashMap<String,String[]>();

properties.put("username", new String[]{"jack"});

properties.put("password", new String[]{"1234"});

properties.put("age", new String[]{"18"});

User user = new User();

// 封裝數據

BeanUtils.populate(user, properties);

System.out.println(user);

}

  • 功能2:封裝表單數據,使用類型轉換器

@Test

public void demo02() throws Exception{

//轉換器

Map<String,String[]> properties = new HashMap<String,String[]>();

properties.put("username", new String[]{"jack"});

properties.put("password", new String[]{"1234"});

properties.put("age", new String[]{"18"});

properties.put("birthday", new String[]{"2012-12-21"});

User user = new User();

//1 創建BeanUtils提供時間轉換器

DateConverter dateConverter = new DateConverter();

//2 設置需要轉換的格式

dateConverter.setPattern("yyyy-MM-dd");

//3 注冊轉換器

ConvertUtils.register(dateConverter, java.util.Date.class);

//4 封裝數據

BeanUtils.populate(user, properties);

System.out.println(user);

}

  • 功能3:編寫工具類

public class MyBeanUtils {

/**

* 將數據封裝給JavaBean,支持時間類型轉換

* @param user

* @param properties

*/

public static void populate(Object user, Map<String,String[]> properties){

try {

//1 創建BeanUtils提供時間轉換器

DateConverter dateConverter = new DateConverter();

//2 設置需要轉換的格式

dateConverter.setPatterns(new String[]{"yyyy-MM-dd","yyyy-MM-dd HH:mm:ss"});

//3 注冊轉換器

ConvertUtils.register(dateConverter, java.util.Date.class);

//4 封裝數據

BeanUtils.populate(user, properties);

} catch (Exception e) {

throw new RuntimeException(e);

}

}

}

使用

  • 工具類:高級(了解)

/**

* 高級封裝,不需要new javabean

* @param beanClass

* @param properties

* @return

*/

public static <T> T populate(Class<T> beanClass, Map<String,String[]> properties){

try {

//1 使用反射創建實例

T bean = beanClass.newInstance();

//2.1 創建BeanUtils提供時間轉換器

DateConverter dateConverter = new DateConverter();

//2.2 設置需要轉換的格式

dateConverter.setPatterns(new String[]{"yyyy-MM-dd","yyyy-MM-dd HH:mm:ss"});

//2.3 注冊轉換器

ConvertUtils.register(dateConverter, java.util.Date.class);


//3 封裝數據

BeanUtils.populate(bean, properties);

return bean;

} catch (Exception e) {

throw new RuntimeException(e);

}

}

使用

4.3 代碼實現

package cn.com.javahelp.e_regist;

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

/**

* 用戶注冊的Servlet

*/

public class RegistServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

try{

request.setCharacterEncoding("UTF-8");

// 1.接收參數

String username = request.getParameter("username");

String password = request.getParameter("password");

String email = request.getParameter("email");

String name = request.getParameter("name");

String sex = request.getParameter("sex");

String telephone = request.getParameter("telephone");

// 2.封裝數據

User user = new User();

user.setUsername(username);

user.setPassword(password);

user.setEmail(email);

user.setName(name);

user.setSex(sex);

user.setTelephone(telephone);


// 3.調用業務層處理數據

UserService userService = new UserService();

userService.regist(user);

// 4.頁面跳轉

response.sendRedirect(request.getContextPath()+"/login.htm");

}catch(Exception e){

e.printStackTrace();

}

}


protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

}


4.4 總結:

4.4.1 解決請求參數的中文亂碼問題

在填寫表單數據時,難免會輸入中文,如姓名、公司名稱等。在文件4-13中,由于HTML設置了瀏覽器在傳遞請求參數時,采用的編碼方式是UTF-8,但在解碼時采用的是默認的ISO8859-1,因此會導致亂碼的出現。在瀏覽器的地址欄中輸入地址“http://localhost:8080/day15/form.html”再次訪問form.html頁面,輸入用戶名為“悟空教程”以及相關表單信息,如圖4-20所示。

圖4-20 運行結果

單擊圖4-20中的“提交”按鈕,這時,控制臺打印出了每個參數的值,具體如圖4-21所示。

圖4-21 運行結果

從圖4-21可以看出,當輸入的用戶名為中文時,出現了亂碼問題。通過本任務的學習,讀者將學會如何處理請求參數的中文亂碼問題。


【實現步驟】

1.設置編碼方式

HttpServletRequest接口中,提供了一個setCharacterEncoding()方法,該方法用于設置request對象的解碼方式,接下來,對文件4-6進行修改,修改后的代碼如文件4-7所示。該方法用于返回請求消息的實體部分的字符集編碼

文件4-7 RequestParamsServlet.java

package cn.com.javahelp.chapter04.request;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class RequestParamsServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

       HttpServletResponse response)throws ServletException, IOException {

//設置request對象的解碼方式

        request.setCharacterEncoding("utf-8");

String name = request.getParameter("username");

String password = request.getParameter("password");

System.out.println("用戶名:" + name);

System.out.println("密   碼:" + password);

String[] hobbys = request.getParameterValues("hobby");

System.out.print("愛好:");

for (int i = 0; i < hobbys.length; i++) {

System.out.print(hobbys[i] + ", ");

}

}

public void doPost(HttpServletRequest request,

       HttpServletResponse response)throws ServletException, IOException {

doGet(request, response);

}

}


2.查看運行結果

啟動Tomcat服務器,再次訪問form.html頁面,輸入中文用戶名“悟空教程”以及相關表單信息,控制臺打印的結果如圖4-22所示。

圖4-22 運行結果

從圖4-22可以看出,控制臺輸出的參數信息沒有出現亂碼。需要注意的是,這種解決亂碼的方式只對POST方式有效,而對GET方式無效。為了驗證GET方式的演示效果,接下來,將form.html文件中method屬性的值改為“GET”。重新訪問form.html頁面并填寫中文信息,控制臺的打印結果如圖4-23所示。

圖4-23 運行結果

從圖4-23中可以看出,使用GET方式提交表單,用戶名出現了亂碼,這就驗證了setCharacterEncoding()方法只對POST提交方式有效的結論。為了解決GET方式提交表單時出現的中文亂碼問題,接下來,對文件4-7進行修改,增加一行代碼,如下所示:

name=new String(name.getBytes("iso8859-1"),"utf-8");

重啟Tomcat服務器,再次訪問form.html網頁,輸入中文用戶名“悟空教程”,這時,控制臺的打印結果沒有出現亂碼,如圖4-24所示。

圖4-24

總結

第5章 案例:登錄錯誤提示信息

5.1 需求

完善“登錄”案例,如果用戶填寫信息有誤,在當前表單頁面中給出相應的提示。


5.2 相關知識點

5.2.1 通過Request對象傳遞數據

Request對象不僅可以獲取一系列數據,還可以通過屬性傳遞數據。在ServletRequest接口中,定義了一系列操作屬性的方法,具體如下:

  • setAttribute()方法

該方法用于將一個對象與一個名稱關聯后存儲進ServletRequest對象中,其完整語法定義如下:

public void setAttribute(java.lang.String name,java.lang.Object o);

需要注意的是,如果ServletRequest對象中已經存在指定名稱的屬性,setAttribute()方法將會先刪除原來的屬性,然后再添加新的屬性。如果傳遞給setAttribute()方法的屬性值對象為null,則刪除指定名稱的屬性,這時的效果等同于removeAttribute()方法。

  • getAttribute()方法

該方法用于從ServletRequest對象中返回指定名稱的屬性對象,其完整的語法定義如下:

public java.lang.String getAttribute (java.lang.String name);

  • removeAttribute()方法

該方法用于從ServletRequest對象中刪除指定名稱的屬性,其完整的語法定義如下:

public void removeAttribute(java.lang.String name);

  • getAttributeNames()方法

該方法用于返回一個包含ServletRequest對象中的所有屬性名的Enumeration對象,在此基礎上,可以對ServletRequest對象中的所有屬性進行遍歷處理。getAttributeNames()方法的完整語法定義如下:

public java.util.Enumeration getAttributeNames();

需要注意的是,只有屬于同一個請求中的數據才可以通過ServletRequest對象傳遞數據。關于ServletRequest對象操作屬性的具體用法,將在后面的小節進行詳細講解。在此,大家只需了解即可。


5.2.2 RequestDispatcher對象的應用

5.2.2.1 RequestDispatcher接口

當一個Web資源收到客戶端的請求后,如果希望服務器通知另外一個資源去處理請求,這時,除了使用sendRedirect()方法實現請求重定向外,還可以通過RequestDispatcher接口的實例對象來實現。在ServletRequest接口中定義了一個獲取RequestDispatcher對象的方法,如表4-5所示。

表4-5  獲取RequestDispatcher對象的方法

方法聲明

功能描述

getRequestDispatcher(String path)

返回封裝了某個路徑所指定資源的RequestDispatcher對象。其中,參數path須以 “/開頭,用于表示當前Web應用的根目錄。需要注意的是,WEB-INF目錄中的內容對RequestDispatcher對象也是可見的,因此,傳遞給getRequestDispatcher(String path)方法的資源可以是WEB-INF目錄中的文件

獲取到RequestDispatcher對象后,最重要的工作就是通知其它Web資源處理當前的Servlet請求,為此,在RequestDispatcher接口中,定義了兩個相關方法,如表4-6所示。

表4-6 RequestDispatcher接口的方法

方法聲明

功能描述

forward(ServletRequest request,

ServletResponse response)

該方法用于將請求從一個Servlet傳遞給另外的一個Web資源。在Servlet中,可以對請求做一個初步處理,然后通過調用這個方法,將請求傳遞給其它資源進行響應。需要注意的是,該方法必須在響應提交給客戶端之前被調用,否則將拋出IllegalStateException異常

include(ServletRequest request,

ServletResponse response)

該方法用于將其它的資源作為當前響應內容包含進來

4-6列舉的兩個方法中,forward()方法可以實現請求轉發,include()方法可以實現請求包含,關于請求轉發相關知識,將在下面的小節中進行詳細講解。

請求包含:就是將當前頁面和被包含頁面的輸出結果,合并一起響應給瀏覽器。


5.2.2.2 請求轉發

Servlet中,如果當前Web資源不想處理請求時,可以通過forward()方法將當前請求傳遞給其它的Web資源進行處理,這種方式稱為請求轉發。為了使讀者更好地理解使用forward()方法實現請求轉發的工作原理,接下來通過一張圖來描述,如圖4-25所示。

圖4-25 forward()方法的工作原理

從圖4-25中可以看出,當客戶端訪問Servlet1時,可以通過forward()方法將請求轉發給其它Web資源,其它Web資源處理完請求后,直接將響應結果返回到客戶端。

了解了forward()方法的工作原理后,接下來,通過一個案例來學習forward()方法的使用。

day15項目的cn.com.javahelp.request包中編寫一個名為RequestForwardServletServlet類,該類使用forword()方法將請求轉發到一個新的Servlet頁面,如文件4-8所示。


文件4-8 RequestForwardServlet.java

package cn.com.javahelp.request;

import java.io.IOException;

import javax.servlet.*;

import javax.servlet.http.*;

public class RequestForwardServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

       HttpServletResponse response)throws ServletException, IOException {

 response.setContentType("text/html;charset=utf-8");

 // 將數據存儲到request對象中

 request.setAttribute("company", "上海滿吉教育科技有限公司");

 RequestDispatcher dispatcher = request.getRequestDispatcher("/ResultServlet");

 dispatcher.forward(request, response);

}

public void doPost(HttpServletRequest request,

      HttpServletResponse response)throws ServletException, IOException {

doGet(request, response);

}

}

通過使用forward()方法,將當前Servlet的請求轉發到ResultServlet頁面,在cn.com.javahelp.request包中編寫一個名為ResultServletServlet類,該類用于獲取RequestForwardServlet類中保存在request對象中的數據并輸出,ResultServlet類的代碼實現。


ResultServlet.java

package cn.com.javahelp.request;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

public class ResultServlet extends HttpServlet {

public void doGet(HttpServletRequest request,

     HttpServletResponse response)throws ServletException, IOException {

 response.setContentType("text/html;charset=utf-8");

          // 獲取PrintWriter對象用于輸出信息

 PrintWriter out = response.getWriter();

 // 獲取request請求對象中保存的數據

 String company = (String) request.getAttribute("company");

 if (company != null) {

  out.println("公司名稱:" + company + "<br>");

 }

}

public void doPost(HttpServletRequest request,

    HttpServletResponse response)throws ServletException, IOException {

 doGet(request, response);

}

}

web.xml中,添加完兩個Servlet的映射信息后,啟動Tomcat服務器,在瀏覽器中輸入地址http://localhost:8080/day15/RequestForwardServlet”訪問RequestForwardServlet,瀏覽器的顯示結果如圖4-26所示。

圖4-26 運行結果

5.3 分析


5.4 實現

  • 步驟1:修改“UserLoginServlet”,登錄錯誤時,使用request設置設置錯誤信息,并請求轉發到“login.jsp”.

    • 1.創建“login.jsp”

    • 2.添加頁面編碼(固定寫法)

<%@ page language="java" contentType="text/html; charset=UTF-8"

   pageEncoding="UTF-8"%>

    • 3.復制“login.html”內容到“login.jsp”

// * request作用設置錯誤信息

request.setAttribute("msg", "用戶名或密碼不匹配");

// * 請求轉發到登錄頁面

request.getRequestDispatcher("login.jsp").forward(request, response);

  • 步驟2:在“login.jsp”頁面顯示錯誤信息(固定寫法)

<%=request.getAttribute("msg") %>

5.5 總結

5.5.1 重定向和轉發的區別:(redirectforward的區別)

* 1.重定向的地址欄會發生變化,轉發的地址欄不變.

* 2.重定向兩次請求兩次響應,轉發一次請求一次響應.

* 3.重定向路徑需要加工程名,轉發的路徑不需要加工程名.

* 4.重定向可以跳轉到任意網站,轉發只能在服務器內部進行轉發.

第6章 總結


擴展知識

第7章 案例用戶注冊(使用beanutils)

7.1 創建數據庫導入sql語句 引入jar包  工具類 數據庫配置文件

7.2 創建servlet

import java.io.IOException;

import java.io.UnsupportedEncodingException;

import java.lang.reflect.InvocationTargetException;

import java.util.Map;

import java.util.UUID;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.BeanUtils;

import cn.com.javahelp.domain.User;

import cn.com.javahelp.service.UserService;

public class RegisterServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

request.setCharacterEncoding("UTF-8");

//1、獲得請求數據

Map<String, String[]> map = request.getParameterMap();

//2、封裝實體

User user = new User();


//使用BeanUtils進行映射封裝(map中的key的名稱與實體中的屬性名稱進行映射)

//bean代表要被封裝實體  properties代表存儲數據的map

try {

BeanUtils.populate(user, map);

} catch (IllegalAccessException | InvocationTargetException e) {

e.printStackTrace();

}

//手動封裝uid

String uid = UUID.randomUUID().toString();

user.setUid(uid);


//3、傳遞數據到業務對象的方法

UserService service = new UserService();

//4、接受業務方法的處理結果

boolean isRegistSuccess = service.regist(user);

//5、為客戶響應

if(isRegistSuccess){

//注冊成功 返回登錄頁面

response.sendRedirect(request.getContextPath()+"/login.jsp");

}else{

response.setContentType("text/html;charset=UTF-8");

response.getWriter().write("對不起,注冊失敗,請聯系管理員");

}

}

public void regist1(HttpServletRequest request, HttpServletResponse response)throws IOException{

request.setCharacterEncoding("UTF-8");

//1、獲得請求數據

String username = request.getParameter("username");

String password = request.getParameter("password");

String email = request.getParameter("email");

String name = request.getParameter("name");

String sex = request.getParameter("sex");

String birthday = request.getParameter("birthday");

//2、封裝實體

User user = new User();

//UUID:隨機產生的永不不重復的字符串32

String uid = UUID.randomUUID().toString();//36java產生uuid有四個-

user.setUid(uid);

user.setUsername(username);

user.setPassword(password);

user.setEmail(email);

user.setName(name);

user.setSex(sex);

user.setBirthday(birthday);

//3、傳遞數據到業務對象的方法

UserService service = new UserService();

//4、接受業務方法的處理結果

boolean isRegistSuccess = service.regist(user);

//5、為客戶響應

if(isRegistSuccess){

//注冊成功 返回登錄頁面

response.sendRedirect(request.getContextPath()+"/login.jsp");

}else{

response.setContentType("text/html;charset=UTF-8");

response.getWriter().write("對不起,注冊失敗,請聯系管理員");

}

}

public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

doGet(request, response);

}

}

以下代碼編寫根據servlet需求不斷的迭代


7.3 修改頁面name屬性 及action屬性


7.4 創建實體類(根據表來寫)

生成set&get方法,實際開發中一般都是set不會做無參,無參只是方便教學

public class User {

/*`uid` varchar(50) NOT NULL,

 `username` varchar(20) DEFAULT NULL,

 `password` varchar(50) DEFAULT NULL,

 `name` varchar(20) DEFAULT NULL,

 `email` varchar(30) DEFAULT NULL,

 `telephone` varchar(20) DEFAULT NULL,

 `birthday` date DEFAULT NULL,

 `sex` varchar(10) DEFAULT NULL,

 `state` int(11) DEFAULT NULL,

 `code` varchar(64) DEFAULT NULL*/


private String uid;//唯一性標識 主鍵

private String username;//用戶名

private String password;//密碼

private String name;//真實姓名

private String email;//郵箱

private String telephone;

private String birthday;//生日

private String sex;//性別

private int state;

private String code;



public String getUid() {

return uid;

}

public void setUid(String uid) {

this.uid = uid;

}

public String getUsername() {

return username;

}

public void setUsername(String username) {

this.username = username;

}

public String getPassword() {

return password;

}

public void setPassword(String password) {

this.password = password;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}