文章目錄
- Servlet API
- HttpServlet
- 處理請求
- HttpServletRequest
- 打印請求信息
- 前端給后端傳參
Servlet API
Servlet中常用的API
HttpServlet
實際開發的時候主要重寫 doXXX 方法, 很少會重寫 init / destory / service
destory
服務器終止的時候會調用.
//下面的注解把當前類和一個HTTP請求的路徑關聯起來
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {@Overridepublic void init() throws ServletException {//重寫init 插入自己"初始化"相關的邏輯System.out.println("init");}@Overridepublic void destroy() {System.out.println("destroy");}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//這個只是在服務器的控制臺打印System.out.println("hello world");//要想把hello world返回到客戶端,需要使用下面的代碼//getWriter 會得到一個Writer對象resp.getWriter().write("hello world");}
}
上面的destroy能不能被執行到有待商榷,如果是通過Smart Tomcat 的停止按鈕,這個操作本質上是通過Tomcat的8005端口,主動停止,可以觸發destroy.如果是直接殺進程,此時可能就來不及執行destroy就沒了.因此不太推薦使用destroy.
service
每次收到http請求就出觸發(路徑匹配)
@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {super.service(req, resp);}
init / destory / service 這三個方法是HttpServlet中最關鍵的三個方法.
Servlet的聲明周期是怎么回事?
- 開始的時候執行init
- 每次收到請求執行service
- 銷毀之前執行destroy
處理請求
@WebServlet("/method")
public class MethodServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("doGet");resp.getWriter().write("doGet");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("doPost");resp.getWriter().write("doPost");}@Overrideprotected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("doPut");resp.getWriter().write("doPut");}@Overrideprotected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("doDelete");resp.getWriter().write("doDelete");}
}
我們刷新頁面只有doGet請求,想要獲得別的請求可以利用postman這個軟件來實現:
也可以通過ajax來構造請求:先在webapp目錄下創建test.html.
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><!-- 使用這個頁面來構造ajax請求 --><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script><script>$.ajax({type: 'get',url: 'method',success: function(body, status){console.log(body);}});</script>
</body>
</html>
然后重啟服務器,在網址欄輸入:http://127.0.0.1:8080/hello_servlet/test.html
然后刷新在控制臺就能看到doGet請求了.想要獲取別的請求,直接在代碼中改就可以了,需要注意的是我們在編寫Servlet代碼的時候,每次修改代碼,要及得重新啟動服務器.
此處寫的路徑是相對路徑,相對路徑的基準目錄是該html所在的路徑.此處寫method就相當于在http://127.0.0.1:8080/hello_servlet
的基礎上,再拼上一個method
絕對路徑的寫法:
HttpServletRequest
這個類表示的是HTTP請求.這個對象是Tomcat自動構造的,Tomcat會實現監聽端口,接受連接,解析請求,構造請求對象等一系列工作.
下面的表格就是一些典型的方法:
方法 | 描述 |
---|---|
String getProtocol() | 返回請求協議的名稱和版本 |
String getMethod() | 返回請求的 HTTP 方法的名稱,例如,GET、POST 或 PUT |
String getRequestURI() | 從協議名稱直到 HTTP 請求的第一行的查詢字符串中,返回該請求的 URL 的一部分 |
String getContextPath() | 返回指示請求上下文的請求 URI 部分 |
String getQueryString() | 返回包含在路徑后的請求 URL 中的查詢字符串 |
Enumeration getParameterNames() | 返回一個 String 對象的枚舉,包含在該請求中包含的參數的名稱 |
String getParameter(Stringname) | 以字符串形式返回請求參數的值,或者如果參數不存在則返回null |
String[] getParameterValues(String name) | 返回一個字符串對象的數組,包含所有給定的請求參數的值,如果參數不存在則返回 null |
Enumeration getHeaderNames() | 返回一個枚舉,包含在該請求中包含的所有的頭名 |
String getHeader(String name) | 以字符串形式返回指定的請求頭的值 |
String getCharacterEncoding() | 返回請求主體中使用的字符編碼的名稱 |
String getContentType() | 返回請求主體的 MIME 類型,如果不知道類型則返回 null |
int getContentLength() | 以字節為單位返回請求主體的長度,并提供輸入流,或者如果長度未知則返回 -1 |
InputStream getInputStream() | 用于讀取請求的 body 內容. 返回一個 InputStream 對象 |
打印請求信息
@WebServlet("/showRequest")
public class ShowRequestServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//設置響應的 content-type 告訴瀏覽器 響應的body里的格式是啥樣的resp.setContentType("text/html");//搞個 StringBuilder 把這些api的結果拼起來,統一寫回到響應中StringBuilder stringBuilder = new StringBuilder();stringBuilder.append(req.getProtocol());stringBuilder.append("<br>");stringBuilder.append(req.getMethod());stringBuilder.append("<br>");stringBuilder.append(req.getRequestURI());stringBuilder.append("<br>");stringBuilder.append(req.getContextPath());stringBuilder.append("<br>");stringBuilder.append(req.getQueryString());stringBuilder.append("<br>");//獲取到 header 中所有的鍵值對Enumeration<String> headerNames = req.getHeaderNames();while (headerNames.hasMoreElements()){String headerName = headerNames.nextElement();stringBuilder.append(headerName + ": " + req.getHeader(headerName));stringBuilder.append("<br>");}resp.getWriter().write(stringBuilder.toString());}
}
在瀏覽器通過 URL http://127.0.0.1:8080/hello_servlet/showRequest
訪問, 可以看到:
前端給后端傳參
1.通過GET里的query string
在前端給后端傳兩個數字,一個是同學的studentId,一個是classId
@WebServlet("/getParameter")
public class GetParameterServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//預期瀏覽器會發一個形如 /getParameter?studentId=10&classId=20 這樣的請求// 借助req 里的 getParameter方法就能拿到 query string 中的鍵值對內容//getParameter 得到的是 String 類型的結果String studentId = req.getParameter("studentId");String classId = req.getParameter("classId");resp.setContentType("text/html");resp.getWriter().write("studentId = " + studentId + " classId = " + classId);}
}
如果key在query string中不存在,此時就返回值就是null
2.通過POST,借助form表單的方式
對于前端是form表單這樣格式的數據(也是鍵值對,和query string的格式一樣,只是這部分內容在body中),后端還是使用getParameter來獲取.
前端代碼:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><form action="postParameter" method="post"><input type="text" name="studentId"><input type="text" name="classId"><input type="submit" value="提交"></form>
</body>
</html>
后端代碼:
@WebServlet("/postParameter")
public class PostParameterServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String studentId = req.getParameter("studentId");String classId = req.getParameter("classId");resp.setContentType("text/html");resp.getWriter().write("studentId = " + studentId + " classId = " + classId);}
}
使用getParameter 既可以獲取到query string 中的鍵值對,也可以獲取到form表單構造的body中的鍵值對.
3.POST里的json
json是一種非常主流的數據格式,也是鍵值對結構.
我們可以把body按照這個格式來組織.前端可以通過ajax的方式來構造出這個內容,更簡單的方法使用postman直接構造.
后端代碼:
@WebServlet("/postParameter2")
public class PostParameter2Servlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//通過這個方法來處理 body 為 json 格式的數據//直接把 req 對象里 body 完整的讀取出來//getInputStream// 在流對象中讀多少字節取決于Content-Lengthint length = req.getContentLength();byte[] buffer = new byte[length];InputStream inputStream = req.getInputStream();inputStream.read(buffer);//把這個字節數組構造成 String 打印出來String body = new String(buffer,0,length,"utf8");System.out.println("body =" + body);resp.getWriter().write(body);}
}
然后在postman那塊點擊Send請求,服務器也相對應打印出了從body中讀出的數據.
總結:借助postman構造出post請求,body就是json數據,請求到達Tomcat,Tomcat解析成req對象,再通過req.getInputStream讀取body內容,把讀出的結果構造成響應往回寫,最后postman客戶端收到了對應的結果.
當前通過json傳遞數據,但是服務器這邊只是把整個body讀出來,沒有按照鍵值對的方式來處理,還不能根據key獲取value,為了解決這個問題,我們可以使用jackson第三方庫.
通過maven引入第三方庫:
將所選的內容粘貼到pom.xml
當中:
然后上面的后端代碼就需要改變:
class Student{public int studentId;public int classId;
}
@WebServlet("/postParameter2")
public class PostParameter2Servlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {Student student = objectMapper.readValue(req.getInputStream(),Student.class);System.out.println(student.studentId + ", " + student.classId);}
}