請求
請求物件
關於請求
顧名思義,意思就是請求一個“物件”
請求不到的,別想了
請求,就是使用者希望從伺服器端索取一些資源,向伺服器發出詢問。在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=/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);
}
}