請求
請求對象
關於請求
顧名思義,意思就是請求一個“對象”
請求不到的,別想了
請求,就是使用者希望從服務器端索取一些資源,向服務器發出詢問。在B/S架構中,就是客戶瀏覽器向服務器發出詢問。在JavaEE工程中,客戶瀏覽器發出詢問,要遵循HTTP協議規定。
請求對象,就是在JavaEE工程中,用於發送請求的對象。我們常用的對象就是ServletRequest和HttpServletRequest,它們的區別就是是否和HTTP協議有關。
常用請求對象

常用請求方法

請求對象的使用示例
常用方法一:請求各種路徑
/*
獲取路徑的相關方法
*/
@WebServlet("/servletDemo01")
public class ServletDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.獲取虛擬目錄名稱 getContextPath()
String contextPath = req.getContextPath();
System.out.println(contextPath);
//2.獲取Servlet映射路徑 getServletPath()
String servletPath = req.getServletPath();
System.out.println(servletPath);
//3.獲取訪問者ip getRemoteAddr()
String ip = req.getRemoteAddr();
System.out.println(ip);
//4.獲取請求消息的數據 getQueryString()
String queryString = req.getQueryString();
System.out.println(queryString);
//5.獲取統一資源標識符 getRequestURI() /request/servletDemo01
String requestURI = req.getRequestURI();
System.out.println(requestURI);
//6.獲取統一資源定位符 getRequestURL() http://localhost:8080/request/servletDemo01
StringBuffer requestURL = req.getRequestURL();
System.out.println(requestURL);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
常用方法二:獲取請求參數以及封裝(非常重要)
我們常常會使用HttpServletRequest對象獲取請求參數,然后將其封裝到實體類中

/*
獲取請求參數信息的相關方法
*/
@WebServlet("/servletDemo03")
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.根據名稱獲取數據 getParameter()
String username = req.getParameter("username");
System.out.println(username);
String password = req.getParameter("password");
System.out.println(password);
System.out.println("--------------------");
//2.根據名稱獲取所有數據 getParameterValues()
String[] hobbies = req.getParameterValues("hobby");
for(String hobby : hobbies) {
System.out.println(hobby);
}
System.out.println("--------------------");
//3.獲取所有名稱 getParameterNames()
Enumeration<String> names = req.getParameterNames();
while(names.hasMoreElements()) {
String name = names.nextElement();
System.out.println(name);
}
System.out.println("--------------------");
//4.獲取所有參數的鍵值對 getParameterMap()
Map<String, String[]> map = req.getParameterMap();
for(String key : map.keySet()) {
String[] values = map.get(key);
System.out.print(key + ":");
for(String value : values) {
System.out.print(value + " ");
}
System.out.println();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
下面通過實例說明幾種封裝方式
需求:我們要實現從網頁填寫學生注冊信息,然后把獲取請求參數並把相應的信息封裝到每一個Student類中。
第一步:編寫一個頁面html程序

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注冊頁面</title>
</head>
<body>
<form action="/request/servletDemo08" method="post" autocomplete="off">
姓名:<input type="text" name="username"> <br>
密碼:<input type="password" name="password"> <br>
愛好:<input type="checkbox" name="hobby" value="study">學習
<input type="checkbox" name="hobby" value="game">游戲 <br>
<button type="submit">注冊</button>
</form>
</body>
</html>
第二步:編寫Student的javabean類,注意其數據成員最好(必須)與html文件表單的name屬性一致
public class Student {
private String username;
private String password;
private String[] hobby;
public Student() {
}
public Student(String username, String password, String[] hobby) {
this.username = username;
this.password = password;
this.hobby = hobby;
}
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[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return "Student{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", hobby=" + Arrays.toString(hobby) +
'}';
}
}
第三步:獲取參數信息,並封裝數據
法一:直接手動封裝(簡單粗暴)
/*
封裝對象-手動方式
*/
@WebServlet("/servletDemo04")
public class ServletDemo04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.獲取所有的數據
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbies = req.getParameterValues("hobby");
//2.封裝學生對象
Student stu = new Student(username,password,hobbies);
//3.輸出對象
System.out.println(stu);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
法二:通過反射封裝
PropertyDescriptor: Describes a Java Bean property hosting validation constraints
調用javabean類的有參構造函數創建對象
構造函數 PropertyDescriptor(E,class); 注意第一個參數是javabean構造函數的第一個形式參數,第二個參數是已經創建的實類的字節碼。
/*
封裝對象-反射方式
*/
@WebServlet("/servletDemo05")
public class ServletDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.獲取所有的數據
Map<String, String[]> map = req.getParameterMap();
//2.封裝學生對象
Student stu = new Student();
//2.1遍歷集合
for(String name : map.keySet()) {
String[] value = map.get(name);
try {
//2.2獲取Student對象的屬性描述器
PropertyDescriptor pd = new PropertyDescriptor(name,stu.getClass());
//2.3獲取對應的setXxx方法
Method writeMethod = pd.getWriteMethod();
//2.4執行方法
if(value.length > 1) {
writeMethod.invoke(stu,(Object)value);
}else {
writeMethod.invoke(stu,value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//3.輸出對象
System.out.println(stu);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
法三:BeanUtils工具類封裝
BeanUtils.populate(stu,map);(實類,參數map)
/*
封裝對象-工具類方式
*/
@WebServlet("/servletDemo06")
public class ServletDemo06 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.獲取所有的數據
Map<String, String[]> map = req.getParameterMap();
//2.封裝學生對象
Student stu = new Student();
try {
BeanUtils.populate(stu,map);
} catch (Exception e) {
e.printStackTrace();
}
//3.輸出對象
System.out.println(stu);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
用流的形式讀取請求信息
/*
流對象獲取數據
*/
@WebServlet("/servletDemo07")
public class ServletDemo07 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//字符流(必須是post方式)
/*BufferedReader br = req.getReader();
String line;
while((line = br.readLine()) != null) {
System.out.println(line);
}*/
//br.close();
//字節流
ServletInputStream is = req.getInputStream();
byte[] arr = new byte[1024];
int len;
while((len = is.read(arr)) != -1) {
System.out.println(new String(arr,0,len));
}
//is.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
請求正文中中文編碼問題
1.POST方式請求
問題:獲取請求正文,會有亂碼問題。是在獲取的時候就已經亂碼了。
解決:是request對象的編碼出問題了。設置request對象的字符集
request.setCharacterEncoding("編碼方式")它只能解決POST的請求方式,GET方式解決不了
/*
中文亂碼
*/
@WebServlet("/servletDemo08")
public class ServletDemo08 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//設置編碼格式
req.setCharacterEncoding("UTF-8");
String username = req.getParameter("username");
System.out.println(username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
2.GET方式請求
問題:GET方式:正文在地址欄username=%D5%C5%C8%FD%D5%C5%C8%FD是已經被編過一次碼了
GET方式請求的正文是在地址欄中,在Tomcat8.5版本及以后,Tomcat服務器已經幫我們解決了,所以不會有亂碼問題了。
而如果我們使用的不是Tomcat服務器,或者Tomcat的版本是8.5以前,那么GET方式仍然會有亂碼問題,解決方式如下:
使用正確的碼表對已經編過碼的數據進行解碼。就是把取出的內容轉成一個字節數組,但是要使用正確的碼表。(ISO-8859-1)再使用正確的碼表進行編碼,把字節數組再轉成一個字符串,需要使用正確的碼表,是看瀏覽器當時用的是什么碼表。
/**
* 在Servlet的doGet方法中添加如下代碼
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
byte[] by = username.getBytes("ISO-8859-1");
username = new String(by,"GBK");
//輸出到瀏覽器:注意響應的亂碼問題已經解決了
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write(username);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
請求轉發(與重定向的區別)
重定向特點:兩次請求,瀏覽器行為,地址欄改變,請求域中的數據會丟失
請求轉發:一次請求,服務器行為,地址欄不變,請求域中的數據不丟失
請求域的作用范圍:當前請求(一次請求),和當前請求的轉發之中
請求發送方:
/*
請求轉發
*/
@WebServlet("/servletDemo09")
public class ServletDemo09 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//設置共享數據
req.setAttribute("encoding","gbk");
//獲取請求調度對象
RequestDispatcher rd = req.getRequestDispatcher("/servletDemo10");
//實現轉發功能
rd.forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
請求接收方:
/*
請求轉發
*/
@WebServlet("/servletDemo10")
public class ServletDemo10 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//獲取共享數據
Object encoding = req.getAttribute("encoding");
System.out.println(encoding);
System.out.println("servletDemo10執行了...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
開啟服務器后進入/servletDemo09之后會在控制台輸出
encoding
servletDemo10執行了...
而此時瀏覽器的url依然是/servletDemo09,不會跳轉
請求重定向
請求重定向:客戶端的一次請求到達后,發現需要借助其他Servlet來實現功能
特點:瀏覽器地址欄會發生改變,兩次請求、請求域對象中不能共享數據,可以重定向到其他服務器
void sendRedirect(String name) 設置重定向
請求發送方:
/*
請求重定向
*/
@WebServlet("/servletDemo06")
public class ServletDemo06 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//設置請求域數據
req.setAttribute("username","zhangsan");
//設置重定向
resp.sendRedirect(req.getContextPath() + "/servletDemo07");
// resp.sendRedirect("https://www.baidu.com");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
請求接收方:
/*
請求重定向
*/
@WebServlet("/servletDemo07")
public class ServletDemo07 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletDemo07執行了...");
Object username = req.getAttribute("username");
System.out.println(username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
輸出:
servletDemo07執行了...
null
因為請求了兩次,請求域中的數據丟失,所以數據不共享。
查看客戶端瀏覽器發現servletDemo06的Status Code是302 表示請求重定向
而servletDemo07的Status Code是202 表示正常。
請求包含
需求:把兩個Servlet的內容合並到一起來響應瀏覽器
問題:HTTP協議的特點是一請求,一響應的方式。所以絕對不可能出現有兩個Servlet同時響應方式。
解決:把兩個Servlet的響應內容合並輸出。
/*
請求包含
*/
@WebServlet("/servletDemo11")
public class ServletDemo11 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletDemo11執行了...");
//獲取請求調度對象
RequestDispatcher rd = req.getRequestDispatcher("/servletDemo12");
//實現包含功能
rd.include(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
/*
請求包含
*/
@WebServlet("/servletDemo12")
public class ServletDemo12 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletDemo12執行了...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
控制台輸出
servletDemo11執行了...
servletDemo12執行了...
而且瀏覽器的url依然是/servletDemo11,不會跳轉
細節
請求轉發的注意事項:負責轉發的Servlet,轉發前后的響應正文丟失,由轉發目的地來響應瀏覽器。
請求包含的注意事項:被包含者的響應消息頭丟失。因為它被包含起來了。
響應
響應對象
關於響應
服務器端收到請求,同時也已經處理完成,把處理的結果告知用戶。
在B/S架構中,響應就是把結果帶回瀏覽器。
常用響應對象
協議無關的對象標准是:ServletResponse接口
協議相關的對象標准是:HttpServletResponse接口

常用方法介紹

常用狀態碼:
| 狀態碼 | 說明 |
|---|---|
| 200 | 執行成功 |
| 302 | 它和307一樣,都是用於重定向的狀態碼。只是307目前已不再使用 |
| 304 | 請求資源未改變,使用緩存。 |
| 400 | 請求錯誤。最常見的就是請求參數有問題 |
| 404 | 請求資源未找到 |
| 405 | 請求方式不被支持 |
| 500 | 服務器運行內部錯誤 |
狀態碼首位含義:
| 狀態碼 | 說明 |
|---|---|
| 1xx | 消息 |
| 2xx | 成功 |
| 3xx | 重定向 |
| 4xx | 客戶端錯誤 |
| 5xx | 服務器錯誤 |
響應對象的使用示例
字節流輸出中文問題
項目中常用的編碼格式是u8,而瀏覽器默認使用的編碼是gbk。導致亂碼!
解決方式一:修改瀏覽器的編碼格式(不推薦,不能讓用戶做修改的動作)
解決方式二:通過輸出流寫出一個標簽:response.getOutputStream().write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'>")
解決方式三:response.setHeader("Content-Type","text/html;charset=UTF-8"); 指定響應頭信息
解決方式四:response.setContentType("text/html;charset=UTF-8"); (常用)
/*
字節流響應消息及亂碼的解決
*/
@WebServlet("/servletDemo01")
public class ServletDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String str = "你好";
resp.setContentType("text/html;charset=UTF-8");
sos.write(str.getBytes("UTF-8"));
sos.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
字符流輸出中文問題
/*
字符流響應消息及亂碼的解決
*/
@WebServlet("/servletDemo02")
public class ServletDemo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String str = "你好";
//解決中文亂碼
resp.setContentType("text/html;charset=UTF-8");
//獲取字符流對象
PrintWriter pw = resp.getWriter();
//pw.println(str);
pw.write(str);
pw.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
響應圖片到瀏覽器
/*
響應圖片到瀏覽器
*/
@WebServlet("/servletDemo03")
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通過文件的相對路徑來獲取文件的絕對路徑
String realPath = getServletContext().getRealPath("/img/hm.png");
System.out.println(realPath);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));
//獲取字節輸出流對象
ServletOutputStream sos = resp.getOutputStream();
//循環讀寫
byte[] arr = new byte[1024];
int len;
while((len = bis.read(arr)) != -1) {
sos.write(arr,0,len);
}
bis.close();
sos.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
控制緩存
resp.setDateHeader("Expires",(System.currentTimeMillis()+時間));
/*
緩存
*/
@WebServlet("/servletDemo04")
public class ServletDemo04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String news = "這是一條很火爆的新聞~~";
//設置緩存時間
resp.setDateHeader("Expires",(System.currentTimeMillis()+1*60*60*1000L));
//設置編碼格式
resp.setContentType("text/html;charset=UTF-8");
//寫出數據
resp.getWriter().write(news);
System.out.println("aaa");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
定時刷新
resp.setHeader("Refresh","定時時間(秒);URL=/虛擬路徑/頁面路徑");
/*
定時刷新
*/
@WebServlet("/servletDemo05")
public class ServletDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String news = "您的用戶名或密碼錯誤,3秒后自動跳轉到登錄頁面...";
//設置編碼格式
resp.setContentType("text/html;charset=UTF-8");
//寫出數據
resp.getWriter().write(news);
//設置響應消息頭定時刷新
resp.setHeader("Refresh","3;URL=/response/login.html");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
文件下載
/*
文件下載
*/
@WebServlet("/servletDemo08")
public class ServletDemo08 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.創建字節輸入流,關聯讀取的文件
//獲取文件的絕對路徑
String realPath = getServletContext().getRealPath("/img/hm.png");
//創建字節輸出流對象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));
//2.設置響應頭支持的類型 應用支持的類型為字節流
/*
Content-Type 消息頭名稱 支持的類型
application/octet-stream 消息頭參數 應用類型為字節流
*/
resp.setHeader("Content-Type","application/octet-stream");
//3.設置響應頭以下載方式打開 以附件形式處理內容
/*
Content-Disposition 消息頭名稱 處理的形式
attachment;filename= 消息頭參數 附件形式進行處理
*/
resp.setHeader("Content-Disposition","attachment;filename=" + System.currentTimeMillis() + ".png");
//4.獲取字節輸出流對象
ServletOutputStream sos = resp.getOutputStream();
//5.循環讀寫文件
byte[] arr = new byte[1024];
int len;
while((len = bis.read(arr)) != -1) {
sos.write(arr,0,len);
}
//6.釋放資源
bis.close();
sos.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
