卷妹的成長日記之javaweb day5
👩💻部落格首頁:京與舊鋪的部落格首頁
✨歡迎關注🖱點贊🎀收藏⭐留言✒
🔮本文由京與舊鋪原創,
😘系列專欄:java學習
👕參考網站:動力節點
💻首發時間:🎞2022年8月10日🎠
🎨你做三四月的事,八九月就會有答案,一起加油吧
🀄如果覺得部落客的文章還不錯的話,請三連支援一下部落客哦
🛒導航小助手🎪
文章目錄
- 卷妹的成長日記之javaweb day5
- 🛒導航小助手🎪
- 模闆方法設計模式
- HttpServlet源碼分析
- HTTP響應協定的具體封包:
HTTP/1.1 200 ok 狀态行
Content-Type: text/html;charset=UTF-8 響應頭
Content-Length: 160
Date: Mon, 08 Nov 2021 13:19:32 GMT
Keep-Alive: timeout=20
Connection: keep-alive
空白行
<!doctype html> 響應體
<html>
<head>
<title>from get servlet</title>
</head>
<body>
<h1>from get servlet</h1>
</body>
</html>
- 狀态行
- 三部分組成
- 第一部分:協定版本号(HTTP/1.1)
- 第二部分:狀态碼(HTTP協定中規定的響應狀态号。不同的響應結果對應不同的号碼。)
- 200 表示請求響應成功,正常結束。
- 404表示通路的資源不存在,通常是因為要麼是你路徑寫錯了,要麼是路徑寫對了,但是伺服器中對應的資源并沒有啟動成功。總之404錯誤是前端錯誤。
- 405表示前端發送的請求方式與後端請求的處理方式不一緻時發生:
- 比如:前端是POST請求,後端的處理方式按照get方式進行處理時,發生405
- 比如:前端是GET請求,後端的處理方式按照post方式進行處理時,發生405
- 500表示伺服器端的程式出現了異常。一般會認為是伺服器端的錯誤導緻的。
- 以4開始的,一般是浏覽器端的錯誤導緻的。
- 以5開始的,一般是伺服器端的錯誤導緻的。
- 第三部分:狀态的描述資訊
- ok 表示正常成功結束。
- not found 表示資源找不到。
- 響應頭:
- 響應的内容類型
- 響應的内容長度
- 響應的時間
- …
- 空白行:
- 用來分隔“響應頭”和“響應體”的。
- 響應體:
- 響應體就是響應的正文,這些内容是一個長的字元串,這個字元串被浏覽器渲染,解釋并執行,最終展示出效果。
- 怎麼檢視的協定内容?
- 使用chrome浏覽器:F12。然後找到network,通過這個面闆可以檢視協定的具體内容。
- 怎麼向伺服器發送GET請求,怎麼向伺服器發送POST請求?
- 到目前為止,隻有一種情況可以發送POST請求:使用form表單,并且form标簽中的method屬性值為:method=“post”。
- 其他所有情況一律都是get請求:
- 在浏覽器位址欄上直接輸入URL,敲回車,屬于get請求。
- 在浏覽器上直接點選超連結,屬于get請求。
- 使用form表單送出資料時,form标簽中沒有寫method屬性,預設就是get
- 或者使用form的時候,form标簽中method屬性值為:method=“get”
- …
- GET請求和POST請求有什麼差別?
- get請求發送資料的時候,資料會挂在URI的後面,并且在URI後面添加一個“?”,"?"後面是資料。這樣會導緻發送的資料回顯在浏覽器的位址欄上。(get請求在“請求行”上發送資料)
- http://localhost:8080/servlet05/getServlet?username=zhangsan&userpwd=1111
- post請求發送資料的時候,在請求體當中發送。不會回顯到浏覽器的位址欄上。也就是說post發送的資料,在浏覽器位址欄上看不到。(post在“請求體”當中發送資料)
- get請求隻能發送普通的字元串。并且發送的字元串長度有限制,不同的浏覽器限制不同。這個沒有明确的規範。
- get請求無法發送大資料量。
- post請求可以發送任何類型的資料,包括普通字元串,流媒體等資訊:視訊、聲音、圖檔。
- post請求可以發送大資料量,理論上沒有長度限制。
- get請求在W3C中是這樣說的:get請求比較适合從伺服器端擷取資料。
- post請求在W3C中是這樣說的:post請求比較适合向伺服器端傳送資料。
- get請求是安全的。get請求是絕對安全的。為什麼?因為get請求隻是為了從伺服器上擷取資料。不會對伺服器造成威脅。(get本身是安全的,你不要用錯了。用錯了之後又冤枉人家get不安全,你這樣不好(太壞了),那是你自己的問題,不是get請求的問題。)
- post請求是危險的。為什麼?因為post請求是向伺服器送出資料,如果這些資料通過後門的方式進入到伺服器當中,伺服器是很危險的。另外post是為了送出資料,是以一般情況下攔截請求的時候,大部分會選擇攔截(監聽)post請求。
- get請求支援緩存。
- https://n.sinaimg.cn/finance/590/w240h350/20211101/b40c-b425eb67cabc342ff5b9dc018b4b00cc.jpg
- 任何一個get請求最終的“響應結果”都會被浏覽器緩存起來。在浏覽器緩存當中:
- 一個get請求的路徑a 對應 一個資源。
- 一個get請求的路徑b 對應 一個資源。
- 一個get請求的路徑c 對應 一個資源。
- …
- 實際上,你隻要發送get請求,浏覽器做的第一件事都是先從本地浏覽器緩存中找,找不到的時候才會去伺服器上擷取。這種緩存機制目的是為了提高使用者的體驗。
- 有沒有這樣一個需求:我們不希望get請求走緩存,怎麼辦?怎麼避免走緩存?我希望每一次這個get請求都去伺服器上找資源,我不想從本地浏覽器的緩存中取。
- 隻要每一次get請求的請求路徑不同即可。
- https://n.sinaimg.cn/finance/590/w240h350/20211101/7cabc342ff5b9dc018b4b00cc.jpg?t=789789787897898
- https://n.sinaimg.cn/finance/590/w240h350/20211101/7cabc342ff5b9dc018b4b00cc.jpg?t=789789787897899
- https://n.sinaimg.cn/finance/590/w240h350/20211101/7cabc342ff5b9dc018b4b00cc.jpg?t=系統毫秒數
- 怎麼解決?可以在路徑的後面添加一個每時每刻都在變化的“時間戳”,這樣,每一次的請求路徑都不一樣,浏覽器就不走緩存了。
- post請求不支援緩存。(POST是用來修改伺服器端的資源的。)
- post請求之後,伺服器“響應的結果”不會被浏覽器緩存起來。因為這個緩存沒有意義。
- GET請求和POST請求如何選擇,什麼時候使用GET請求,什麼時候使用POST請求?
- 怎麼選擇GET請求和POST請求呢?衡量标準是什麼呢?你這個請求是想擷取伺服器端的資料,還是想向伺服器發送資料。如果你是想從伺服器上擷取資源,建議使用GET請求,如果你這個請求是為了向伺服器送出資料,建議使用POST請求。
- 大部分的form表單送出,都是post方式,因為form表單中要填寫大量的資料,這些資料是收集使用者的資訊,一般是需要傳給伺服器,伺服器将這些資料儲存/修改等。
- 如果表單中有敏感資訊,還是建議适用post請求,因為get請求會回顯敏感資訊到浏覽器位址欄上。(例如:密碼資訊)
- 做檔案上傳,一定是post請求。要傳的資料不是普通文本。
- 其他情況都可以使用get請求。
- 不管你是get請求還是post請求,發送的請求資料格式是完全相同的,隻不過位置不同,格式都是統一的:
- name=value&name=value&name=value&name=value
- name是什麼?
- 以form表單為例:form表單中input标簽的name。
- value是什麼?
- 以form表單為例:form表單中input标簽的value。
模闆方法設計模式
- 什麼是設計模式?
- 某個問題的固定的解決方案。(可以被重複使用。)
- 你知道哪些設計模式?
- GoF設計模式:
- 通常我們所說的23種設計模式。(Gang of Four:4人組提出的設計模式)
- 單例模式
- 工廠模式
- 代理模式
- 門面模式
- 責任鍊設計模式
- 觀察者模式
- 模闆方法設計模式
- …
- JavaEE設計模式:
- DAO
- DTO
- VO
- PO
- pojo
- …
- …
- 什麼是模闆方法設計模式?
- 在模闆類的模闆方法當中定義核心算法骨架,具體的實作步驟可以延遲到子類當中完成。
- 模闆類通常是一個抽象類,模闆類當中的模闆方法定義核心算法,這個方法通常是final的(但也可以不是final的)
- 模闆類當中的抽象方法就是不确定實作的方法,這個不确定怎麼實作的事兒交給子類去做。
HttpServlet源碼分析
- HttpServlet類是專門為HTTP協定準備的。比GenericServlet更加适合HTTP協定下的開發。
- HttpServlet在哪個包下?
- jakarta.servlet.http.HttpServlet
- 到目前為止我們接觸了servlet規範中哪些接口?
- jakarta.servlet.Servlet 核心接口(接口)
- jakarta.servlet.ServletConfig Servlet配置資訊接口(接口)
- jakarta.servlet.ServletContext Servlet上下文接口(接口)
- jakarta.servlet.ServletRequest Servlet請求接口(接口)
- jakarta.servlet.ServletResponse Servlet響應接口(接口)
- jakarta.servlet.ServletException Servlet異常(類)
- jakarta.servlet.GenericServlet 标準通用的Servlet類(抽象類)
- http包下都有哪些類和接口呢?jakarta.servlet.http.*;
- jakarta.servlet.http.HttpServlet (HTTP協定專用的Servlet類,抽象類)
- jakarta.servlet.http.HttpServletRequest (HTTP協定專用的請求對象)
- jakarta.servlet.http.HttpServletResponse (HTTP協定專用的響應對象)
- HttpServletRequest對象中封裝了什麼資訊?
- HttpServletRequest,簡稱request對象。
- HttpServletRequest中封裝了請求協定的全部内容。
- Tomcat伺服器(WEB伺服器)将“請求協定”中的資料全部解析出來,然後将這些資料全部封裝到request對象當中了。
- 也就是說,我們隻要面向HttpServletRequest,就可以擷取請求協定中的資料。
- HttpServletResponse對象是專門用來響應HTTP協定到浏覽器的。
- 回憶Servlet生命周期?
- 使用者第一次請求
- Tomcat伺服器通過反射機制,調用無參數構造方法。建立Servlet對象。(web.xml檔案中配置的Servlet類對應的對象。)
- Tomcat伺服器調用Servlet對象的init方法完成初始化。
- Tomcat伺服器調用Servlet對象的service方法處理請求。
- 使用者第二次請求
- Tomcat伺服器調用Servlet對象的service方法處理請求。
- 使用者第三次請求
- Tomcat伺服器調用Servlet對象的service方法處理請求。
- …
- Tomcat伺服器調用Servlet對象的service方法處理請求。
- 伺服器關閉
- Tomcat伺服器調用Servlet對象的destroy方法,做銷毀之前的準備工作。
- Tomcat伺服器銷毀Servlet對象。
- HttpServlet源碼分析:
public class HelloServlet extends HttpServlet {
// 使用者第一次請求,建立HelloServlet對象的時候,會執行這個無參數構造方法。
public HelloServlet() {
}
//override 重寫 doGet方法
//override 重寫 doPost方法
}
public abstract class GenericServlet implements Servlet, ServletConfig,
java.io.Serializable {
// 使用者第一次請求的時候,HelloServlet對象第一次被建立之後,這個init方法會執行。
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
// 使用者第一次請求的時候,帶有參數的init(ServletConfig config)執行之後,會執行這個沒有參數的init()
public void init() throws ServletException {
// NOOP by default
}
}
// HttpServlet模闆類。
public abstract class HttpServlet extends GenericServlet {
// 使用者發送第一次請求的時候這個service會執行
// 使用者發送第N次請求的時候,這個service方法還是會執行。
// 使用者隻要發送一次請求,這個service方法就會執行一次。
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
// 将ServletRequest和ServletResponse向下轉型為帶有Http的HttpServletRequest和HttpServletResponse
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException(lStrings.getString("http.non_http"));
}
// 調用重載的service方法。
service(request, response);
}
// 這個service方法的兩個參數都是帶有Http的。
// 這個service是一個模闆方法。
// 在該方法中定義核心算法骨架,具體的實作步驟延遲到子類中去完成。
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 擷取請求方式
// 這個請求方式最終可能是:""
// 注意:request.getMethod()方法擷取的是請求方式,可能是七種之一:
// GET POST PUT DELETE HEAD OPTIONS TRACE
String method = req.getMethod();
// 如果請求方式是GET請求,則執行doGet方法。
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
// 如果請求方式是POST請求,則執行doPost方法。
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
// 報405錯誤
String msg = lStrings.getString("http.method_get_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 報405錯誤
String msg = lStrings.getString("http.method_post_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
}
/*
通過以上源代碼分析:
假設前端發送的請求是get請求,後端程式員重寫的方法是doPost
假設前端發送的請求是post請求,後端程式員重寫的方法是doGet
會發生什麼呢?
發生405這樣的一個錯誤。
405表示前端的錯誤,發送的請求方式不對。和伺服器不一緻。不是伺服器需要的請求方式。
通過以上源代碼可以知道:隻要HttpServlet類中的doGet方法或doPost方法執行了,必然405.
怎麼避免405的錯誤呢?
後端重寫了doGet方法,前端一定要發get請求。
後端重寫了doPost方法,前端一定要發post請求。
這樣可以避免405錯誤。
這種前端到底需要發什麼樣的請求,其實應該後端說了算。後端讓發什麼方式,前端就得發什麼方式。
有的人,你會看到為了避免405錯誤,在Servlet類當中,将doGet和doPost方法都進行了重寫。
這樣,确實可以避免405的發生,但是不建議,405錯誤還是有用的。該報錯的時候就應該讓他報錯。
如果你要是同時重寫了doGet和doPost,那還不如你直接重寫service方法好了。這樣代碼還能
少寫一點。
*/
- 我們編寫的HelloServlet直接繼承HttpServlet,直接重寫HttpServlet類中的service()方法行嗎?
- 可以,隻不過你享受不到405錯誤。享受不到HTTP協定專屬的東西。
- 到今天我們終于得到了最終的一個Servlet類的開發步驟:
- 第一步:編寫一個Servlet類,直接繼承HttpServlet
- 第二步:重寫doGet方法或者重寫doPost方法,到底重寫誰,javaweb程式員說了算。
- 第三步:将Servlet類配置到web.xml檔案當中。
- 第四步:準備前端的頁面(form表單),form表單中指定請求路徑即可。