一. Servlet體系結構
Servlet – 接口
GenericServlet – 抽象類
HttpServlet – 抽象類
- 由于Servlet接口中好多方法用不到, 是以GenericServlet對該接口的不常用方法做了預設空實作, 隻将service()方法作為抽象方法等子類去實作.
- HTTPServlet : 對http協定的一種封裝, 簡化操作 — 可以定義類繼承HTTPServlet類, 覆寫doGet/doPost方法
二. Request對象
2.1 擷取請求體資料
1.擷取請求方式 : getMethod()
2.擷取虛拟目錄 : getContextPath()
3.擷取Servlet路徑 : getServletPath()
4.擷取get方式請求參數 : getQueryString()
擷取post方式請求參數 : getReader()
5.擷取請求URI : getRequestURI() / getRequestURL()
6.擷取協定及版本 : getProtocol()
7.擷取用戶端的IP位址 : getRemoteAddr()
2.2 擷取請求頭資料
1.擷取所有請求頭名稱 : getHeaderName()
2.擷取指定的請求頭資料 : getHeader(String name)
2.3 擷取請求參數通用方式
1.String getParameter(String name) : 根據參數名稱擷取參數值
2.String[] getParameterValues(String name) : 根據參數名稱擷取參數值的數組
3.Enumeration getParameterNames() : 擷取所有請求的參數名稱
4.Map<String, String[]) getParameterMap() : 擷取所有參數的map集合
@WebServlet("/requestdemo6")
public class RequestDemo6 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// post 擷取請求參數
// 根據參數名稱擷取參數值
String username = request.getParameter("username");
System.out.println("post方式");
System.out.println(username);
// 根據參數名稱擷取參數值的數組
String[] hobbies = request.getParameterValues("hobby");
for (String hobby : hobbies) {
System.out.println(hobby);
}
// 擷取所有請求的參數名稱
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String key = parameterNames.nextElement();
String[] names = request.getParameterValues(key);
System.out.println(key);
for (String name : names) {
System.out.println(name);
}
System.out.println("--------------");
}
// 擷取所有參數的map集合
Map<String, String[]> parameterMap = request.getParameterMap();
// 周遊
Set<String> keySet = parameterMap.keySet();
for (String key : keySet) {
// 根據鍵, 擷取值
String[] values = parameterMap.get(key);
System.out.println(key);
for (String value : values) {
System.out.println(value);
}
System.out.println("--------------------");
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
2.4 中文亂碼問題
get方式 : tomcat8 已經将get方式亂碼問題解決了
post方式 : 會亂碼
解決方法 :
request.setCharacterEncoding("utf-8");
2.5 請求轉發
一種在伺服器内部的資源跳轉方式
方法 :
request.getRequestDispatcher("/Servlet路徑").forward(request, response);
特點 :
- 浏覽器位址欄路徑不發生變化
- 隻能轉發到目前伺服器内部資源中
- 轉發是一次請求
2.6. 共享資料
- 域對象 : 一個有作用範圍的對象, 可以再範圍内共享資料
- request域 : 代表一次請求的範圍, 一般用于請求轉發的多個資源中共享資料
- 方法 :
void setAttribute(String var1, Object var2);
Object getAttribute(String var1);
void removeAttribute(String var1);
- 擷取ServletContext
- ServletContext getServletContext();
2.7 注意事項
- login.html中form表單的action路徑的寫法
- 虛拟目錄 + Servlet的資源路徑
-
BeanUtils工具類, 簡化資料封裝
用于封裝JavaBean的, 導包時為 :
import org.apache.commons.beanutils.BeanUtils;
- 要求 :
- 類必須被public修飾
必須提供空參的構造器
- 成員變量必須使用private修飾
- 提供公共的setter和getter方法
- 功能 : 封裝資料
- 成員變量 :
- 方法 :
-
setProperty(Object bean, String name, Object value)
-
getProperty(Object bean, String name)
- populate(Object obj, Map map) : 将map集合的鍵值對資訊封裝到對應的JavaBean對象中
登入案例思路
三. Response對象
功能 : 設定響應消息
- 設定響應行
- 格式 : HTTP/1.1 200 ok
- 設定狀态碼 : setStatus(int sc)
-
設定響應頭
setHeader(String name, String value)
- 設定響應體
- 使用步驟 :
- 擷取輸出流
- 字元輸出流 : PrintWriter getWriter()
- 位元組輸出流 : ServletOutputStream getOutputStream()
- 使用輸出流, 将資料輸出到用戶端浏覽器
3.1 轉發和重定向的差別
重定向特點 : redirect
- 位址欄發生變化
- 重定向可以通路其他站點(伺服器)的資源(需要寫虛拟目錄)
- 重定向是兩次請求. 不能使用request對象來共享資料
轉發特點 : forward
- 轉發位址路徑不變
- 轉發隻能通路目前伺服器下的資源(不用寫虛拟目錄)
- 轉發是一次請求, 可以使用request對象來共享資料
3.2 路徑寫法
-
相對路徑
通過相對路徑不可以确定唯一資源
如 : ./index.html
不以 / 開頭, 以 . 開頭
規則 : 找到目前資源和目标資源之間的相對位置關系
- ./ : 目前目錄 (不寫代表./)
- …/ : 後退一級目錄
-
絕對路徑
通過絕對路徑可以确定唯一資源
如 : http://localhost/test/responServlet2 -> /test/responServlet2
以 / 開頭的路徑
規則 : 判斷定義的路徑給誰用?
- 給用戶端浏覽器使用 : 需要加虛拟目錄(項目的通路路徑)
- 虛拟目錄可能會發生改變, 是以需要動态擷取
// 動态擷取虛拟目錄
String contextPath = request.getContextPath();
// 簡單重定向
response.sendRedirect(contextPath + "/responServlet2");
虛拟目錄為
/
- 給伺服器使用 : 不需要加虛拟目錄
3.3 伺服器輸出字元資料到浏覽器
步驟 :
-
擷取字元輸出流
PrintWriter pw = response.getWriter();
-
輸出資料
pw.write("xxx");
注意 :
- 亂碼問題 :
-
擷取的流的預設編碼是ISO-8859-1PrintWriter pw = respon.getWriter();
- 設定該流的預設編碼
- 告訴浏覽器響應體使用的編碼
// 簡單形式, 設定編碼, 是在擷取流之前設定
response.setContentType("text/html;charset=utf-8");
3.4 伺服器輸出位元組資料到浏覽器
// 解決亂碼問題
response.setContentType("text/html;charset=utf-8");
// 擷取位元組輸出流
ServletOutputStream sos = response.getOutputStream();
// 輸出資料
sos.write("你好".getBytes("utf-8"));
四. ServletContext對象
概念 : 代表整個web應用, 可以和程式的容器(伺服器)來通信
4.1 擷取方式
-
通過request對象擷取
request.getServletContext();
-
通過HttpServlet擷取
this.getServletContext();
4.2 功能
4.2.1 擷取MIME類型
- MIME類型 : 在網際網路通信過程中定義的一種檔案資料類型
- 格式 : 大類型/小類型 text/html image/jpeg
- 擷取 :
String mimeType = servletContext.getMimeType(fileName);
4.2.2 域對象 : 共享資料
-
// 設定資料servletContext1.setAttribute("msg", "servletContextTransmit");
-
// 擷取資料Object msg = servletContext1.getAttribute("msg");
-
// 删除資料removeAttribute(String name)
- ServletContext對象範圍 : 所有使用者所有請求的資料
4.2.3 擷取檔案的真實(伺服器)路徑
存放檔案的位置 :
- src目錄下 :
String realPath1 = context.getRealPath("/WEB-INF/clases/a.txt");
- WEB-INF下 :
String realPath2 = context.getRealPath("/WEB-INF/c.txt");
- web目錄下 :
String realPath = context.getRealPath("b.txt");
原理 :
圖檔中的路徑中打開後,繼續打開 conf -> Catalina -> localhost, 裡面有ROOT.xml檔案
該檔案實際上就是Tomcat設定的虛拟目錄的名字, 該項目設定虛拟目錄為
/
, 是以該檔案名稱為ROOT
打開該檔案後, 顯示如下内容
<?xml version="1.0"?>
<Context docBase="C:\Document\Daily_Java\out\artifacts\JavaWebLearn_war_exploded" path=""/>
該路徑則為項目的部署位置
這三張圖進行對比可以發現,
檔案中的檔案顯示為 -> web目錄中的檔案
項目中其他檔案夾 -> 存放在WEB-INF檔案夾内
WEB-INF檔案中的内容 -> 與其他檔案夾存放在一起, 都在WEB-INF檔案夾中
是以, 在擷取真實路徑時, 需要看檔案到底存放在什麼位置, 若是在web目錄下,則直接
"/xx.xx"
即可, 否則, 需要添加它的額外路徑, 如
/WEB-INF/xx.xx
或者
/WEB-INF/classes/xx.xx
案例: 下載下傳圖檔
檔案下載下傳需求 :
- 頁面顯示超連結
- 點選超連結後彈出下載下傳提示框
- 完成圖檔檔案下載下傳
分析 :
- 超連結指向的資源如果能夠被浏覽器解析, 浏覽器中展示, 如果不能解析, 則彈出下載下傳提示框, 不滿足需求
- 任何資源都必須彈出下載下傳提示框
- 使用響應頭設定資源的打開方式 :
- content-disposition: attachment;filename=xxx
- 定義頁面, 編輯超連結href屬性, 指向servlet, 傳遞資源名稱filename
- 定義servlet
- 擷取檔案名稱
- 使用位元組輸入流加載檔案進記憶體
- 指定response的響應頭 : content-disposition:attachment;filename=xxx
- 将資料寫出到response輸出流
- 中文檔案問題
- 解決 :
- 擷取用戶端使用的浏覽器版本資訊
- 根據不同的版本資訊, 設定filename的編碼方式不同
import sun.misc.BASE64Encoder;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class DownLoadUtils {
public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
if (agent.contains("MSIE")) {
// IE浏覽器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏覽器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏覽器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}
@WebServlet("/downLoadServlet")
public class DownLoadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 擷取請求參數, 檔案名稱
String filename = request.getParameter("filename");
// 2. 使用位元組輸入流加載檔案進記憶體
// 2.1 找到檔案伺服器路徑
ServletContext context = this.getServletContext();
String realPath = context.getRealPath("/img/" + filename);
// 2.2 用位元組流關聯
FileInputStream fis = new FileInputStream(realPath);
// 3. 設定response的響應頭
// 3.1 設定響應頭類型 : content-type
// 擷取檔案的mime類型
String mimeType = context.getMimeType(filename);
response.setHeader("content-type", mimeType);
// 3.2 設定響應頭打開方式 : content-disposition
// 解決中文檔案名問題
// 1. 擷取user-agent請求頭
String agent = request.getHeader("user-agent");
// 2. 使用工具類方法編碼檔案名即可
filename = DownLoadUtils.getFileName(agent, filename);
response.setHeader("content-disposition", "attachment;filename=" + filename);
// 4. 将輸入流的資料寫出到輸出流
ServletOutputStream sos = response.getOutputStream();
byte[] buff = new byte[1024 * 8];
int len = 0;
while ((len = fis.read(buff)) != -1) {
sos.write(buff, 0, len);
}
fis.close();
}
<body>
<!-- href的路徑為: 虛拟路徑 + 圖檔路徑-->
<a href="/img/風景.jpg">圖檔1</a>
<hr>
<!--定義頁面, 編輯超連結href屬性, 指向servlet, 傳遞資源名稱filename-->
<a href="/downLoadServlet?filename=風景.jpg">圖檔</a>
</body>