請求

請求物件

關於請求

顧名思義,意思就是請求一個“物件”

請求不到的,別想了

請求,就是使用者希望從伺服器端索取一些資源,向伺服器發出詢問。在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(String,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,不會跳轉

請求重定向

resp.sendRedirect(req.getContextPath() + "/servletDemo07");

請求傳送方:

/*
請求重定向
*/
@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);
}
}

請求包含

需求:把兩個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=/虛擬路徑/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);
}
}