這兩天複習了下Request以及Response部分的內容。 主要內容 1. HTTP協議:響應消息 2. Request對象 3. Response對象 4. ServletContext對象 HTTP: 概念:Hyper Text Transfer Protocol 超文本傳輸協議 傳輸協議:定 ...
這兩天複習了下Request以及Response部分的內容。
主要內容
- HTTP協議:響應消息
- Request對象
- Response對象
- ServletContext對象
HTTP:
* 概念:Hyper Text Transfer Protocol 超文本傳輸協議
* 傳輸協議:定義了,客戶端和伺服器端通信時,發送數據的格式
* 特點:
1. 基於TCP/IP的高級協議
2. 預設埠號:80
3. 基於請求/響應模型的:一次請求對應一次響應
4. 無狀態的:每次請求之間相互獨立,不能交互數據
* 歷史版本:
* 1.0:每一次請求響應都會建立新的連接
* 1.1:復用連接
1. 請求消息:客戶端發送給伺服器端的數據
* 數據格式:
1. 請求行
請求方式 請求url 請求協議/版本
GET /login.html HTTP/1.1
* 請求方式:
* HTTP協議有7種請求方式,常用的有2種
* GET:
1. 請求參數在請求行中,在url後。
2. 請求的url長度有限制的
3. 不太安全
* POST:
1. 請求參數在請求體中
2. 請求的url長度沒有限制的
3. 相對安全
2. 請求頭:客戶端瀏覽器告訴伺服器一些信息
請求頭名稱: 請求頭值
* 常見的請求頭:
1. User-Agent:瀏覽器告訴伺服器,我訪問你使用的瀏覽器版本信息
* 可以在伺服器端獲取該頭的信息,解決瀏覽器的相容性問題
2. Referer:http://localhost/login.html
* 告訴伺服器,我(當前請求)從哪裡來?
* 作用:
1. 防盜鏈
2. 統計工作
3. 請求空行
空行,就是用於分割POST請求的請求頭,和請求體的。
4. 請求體(正文):
* 封裝POST請求消息的請求參數的
* 請求字元串格式:
POST /login.html HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost/login.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1
username=zhangsan
2. 響應消息:伺服器端發送給客戶端的數據
* 數據格式:
1. 響應行
1. 組成:協議/版本 響應狀態碼 狀態碼描述
2. 響應狀態碼:伺服器告訴客戶端瀏覽器本次請求和響應的一個狀態。
1. 狀態碼都是3位數字
2. 分類:
1. 1xx:伺服器接受客戶端消息,但沒有接受完成,等待一段時間後,發送1xx多狀態碼
2. 2xx:成功。代表:200
3. 3xx:重定向。代表:302(重定向),304(訪問緩存)
4. 4xx:客戶端錯誤。
* 代表:
* 404(請求路徑沒有對應的資源)
* 405:請求方式沒有對應的doXxx方法
5. 5xx:伺服器端錯誤。代表:500(伺服器內部出現異常)
2. 響應頭:
1. 格式:頭名稱: 值
2. 常見的響應頭:
1. Content-Type:伺服器告訴客戶端本次響應體數據格式以及編碼格式
2. Content-disposition:伺服器告訴客戶端以什麼格式打開響應體數據
* 值:
* in-line:預設值,在當前頁面內打開
* attachment;filename=xxx:以附件形式打開響應體。文件下載
3. 響應空行
4. 響應體:傳輸的數據
* 響應字元串格式
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 101
Date: Wed, 06 Jun 2018 07:08:42 GMT
<html>
<head>
<title>$Title$</title>
</head>
<body>
hello , response
</body>
</html>
Request:
1. request對象和response對象的原理
1. request和response對象是由伺服器創建的。我們來使用它們
2. request對象是來獲取請求消息,response對象是來設置響應消息
2. request對象繼承體繫結構:
ServletRequest -- 介面
| 繼承
HttpServletRequest -- 介面
| 實現
org.apache.catalina.connector.RequestFacade 類(tomcat)
3. request功能:
1. 獲取請求消息數據
1. 獲取請求行數據
* GET /day14/demo1?name=zhangsan HTTP/1.1
* 方法:
1. 獲取請求方式 :GET
* String getMethod()
2. (*)獲取虛擬目錄:/day14
* String getContextPath()
3. 獲取Servlet路徑: /demo1
* String getServletPath()
4. 獲取get方式請求參數:name=zhangsan
* String getQueryString()
5. (*)獲取請求URI:/day14/demo1
* String getRequestURI() : /day14/demo1
* StringBuffer getRequestURL() : http://localhost/day14/demo1
* URL:統一資源定位符 : http://localhost/day14/demo1 中華人民共和國
* URI:統一資源標識符 : /day14/demo1 共和國
6. 獲取協議及版本:HTTP/1.1
* String getProtocol()
7. 獲取客戶機的IP地址:
* String getRemoteAddr()
2. 獲取請求頭數據
* 方法:
* (*)String getHeader(String name):通過請求頭的名稱獲取請求頭的值
* Enumeration<String> getHeaderNames():獲取所有的請求頭名稱
3. 獲取請求體數據:
* 請求體:只有POST請求方式,才有請求體,在請求體中封裝了POST請求的請求參數
* 步驟:
1. 獲取流對象
* BufferedReader getReader():獲取字元輸入流,只能操作字元數據
* ServletInputStream getInputStream():獲取位元組輸入流,可以操作所有類型數據
* 在文件上傳知識點後講解
2. 再從流對象中拿數據
2. 其他功能:
1. 獲取請求參數通用方式:不論get還是post請求方式都可以使用下列方法來獲取請求參數
1. String getParameter(String name):根據參數名稱獲取參數值 username=zs&password=123
2. String[] getParameterValues(String name):根據參數名稱獲取參數值的數組 hobby=xx&hobby=game
3. Enumeration<String> getParameterNames():獲取所有請求的參數名稱
4. Map<String,String[]> getParameterMap():獲取所有參數的map集合
* 中文亂碼問題:
* get方式:tomcat 8 已經將get方式亂碼問題解決了
* post方式:會亂碼
* 解決:在獲取參數前,設置request的編碼request.setCharacterEncoding("utf-8");
2. 請求轉發:一種在伺服器內部的資源跳轉方式
1. 步驟:
1. 通過request對象獲取請求轉發器對象:RequestDispatcher getRequestDispatcher(String path)
2. 使用RequestDispatcher對象來進行轉發:forward(ServletRequest request, ServletResponse response)
如: request.getRequestDispatcher("/requestDemo9").forward(request,response);
2. 特點:
1. 瀏覽器地址欄路徑不發生變化
2. 只能轉發到當前伺服器內部資源中。
3. 轉發是一次請求
3. 共用數據:
* 域對象:一個有作用範圍的對象,可以在範圍內共用數據
* request域:代表一次請求的範圍,一般用於請求轉發的多個資源中共用數據
* 方法:
1. void setAttribute(String name,Object obj):存儲數據
2. Object getAttribude(String name):通過鍵獲取值
3. void removeAttribute(String name):通過鍵移除鍵值對
4. 獲取ServletContext:
* ServletContext getServletContext()
案例:用戶登錄
* 用戶登錄案例需求:
1.編寫login.html登錄頁面
username & password 兩個輸入框
2.使用Druid資料庫連接池技術,操作mysql,day14資料庫中user表
3.使用JdbcTemplate技術封裝JDBC
4.登錄成功跳轉到SuccessServlet展示:登錄成功!用戶名,歡迎您
5.登錄失敗跳轉到FailServlet展示:登錄失敗,用戶名或密碼錯誤
* 分析
* 開發步驟
1. 創建項目,導入html頁面,配置文件,jar包
2. 創建資料庫環境
CREATE DATABASE day14;
USE day14;
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32) UNIQUE NOT NULL,
PASSWORD VARCHAR(32) NOT NULL
);
3. 創建包com.ping.domain,創建類User
package com.ping.domain;
/**
* 用戶的實體類
*/
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
4. 創建包com.ping.util,編寫工具類JDBCUtils
package com.ping.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import javax.xml.crypto.Data;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* JDBC工具類 使用Durid連接池
*/
public class JDBCUtils {
private static DataSource ds ;
static {
try {
//1.載入配置文件
Properties pro = new Properties();
//使用ClassLoader載入配置文件,獲取位元組輸入流
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//2.初始化連接池對象
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 獲取連接池對象
*/
public static DataSource getDataSource(){
return ds;
}
/**
* 獲取連接Connection對象
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
}
5. 創建包com.ping.dao,創建類UserDao,提供login方法
package com.ping.dao;
import com.ping.domain.User;
import com.ping.util.JDBCUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* 操作資料庫中User表的類
*/
public class UserDao {
//聲明JDBCTemplate對象共用
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
/**
* 登錄方法
* @param loginUser 只有用戶名和密碼
* @return user包含用戶全部數據,沒有查詢到,返回null
*/
public User login(User loginUser){
try {
//1.編寫sql
String sql = "select * from user where username = ? and password = ?";
//2.調用query方法
User user = template.queryForObject(sql,
new BeanPropertyRowMapper<User>(User.class),
loginUser.getUsername(), loginUser.getPassword());
return user;
} catch (DataAccessException e) {
e.printStackTrace();//記錄日誌
return null;
}
}
}
6. 編寫com.ping.web.servlet.LoginServlet類
package com.ping.web.servlet;
import com.ping.dao.UserDao;
import com.ping.domain.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.設置編碼
req.setCharacterEncoding("utf-8");
//2.獲取請求參數
String username = req.getParameter("username");
String password = req.getParameter("password");
//3.封裝user對象
User loginUser = new User();
loginUser.setUsername(username);
loginUser.setPassword(password);
//4.調用UserDao的login方法
UserDao dao = new UserDao();
User user = dao.login(loginUser);
//5.判斷user
if(user == null){
//登錄失敗
req.getRequestDispatcher("/failServlet").forward(req,resp);
}else{
//登錄成功
//存儲數據
req.setAttribute("user",user);
//轉發
req.getRequestDispatcher("/successServlet").forward(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
7. 編寫FailServlet和SuccessServlet類
@WebServlet("/successServlet")
public class SuccessServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//獲取request域中共用的user對象
User user = (User) request.getAttribute("user");
if(user != null){
//給頁面寫一句話
//設置編碼
response.setContentType("text/html;charset=utf-8");
//輸出
response.getWriter().write("登錄成功!"+user.getUsername()+",歡迎您");
}
}
@WebServlet("/failServlet")
public class FailServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//給頁面寫一句話
//設置編碼
response.setContentType("text/html;charset=utf-8");
//輸出
response.getWriter().write("登錄失敗,用戶名或密碼錯誤");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
8. login.html中form表單的action路徑的寫法
* 虛擬目錄+Servlet的資源路徑
9. BeanUtils工具類,簡化數據封裝
* 用於封裝JavaBean的
1. JavaBean:標準的Java類
1. 要求:
1. 類必須被public修飾
2. 必須提供空參的構造器
3. 成員變數必須使用private修飾
4. 提供公共setter和getter方法
2. 功能:封裝數據
2. 概念:
成員變數:
屬性:setter和getter方法截取後的產物
例如:getUsername() --> Username--> username
3. 方法:
1. setProperty()
2. getProperty()
3. populate(Object obj , Map map):將map集合的鍵值對信息,封裝到對應的JavaBean對象中
Response對象
* 功能:設置響應消息
1. 設置響應行
1. 格式:HTTP/1.1 200 ok
2. 設置狀態碼:setStatus(int sc)
2. 設置響應頭
setHeader(String name, String value)
3. 設置響應體
* 使用步驟
1. 獲取輸出流
* 字元輸出流:PrintWriter getWriter()
* 位元組輸出流:ServletOutputStream getOutputStream()
2. 使用輸出流,將數據輸出到客戶端瀏覽器
* 案例:
1. 完成重定向
* 重定向:資源跳轉的方式
* 代碼實現:
//1. 設置狀態碼為302
response.setStatus(302);
//2.設置響應頭location
response.setHeader("location","/day15/responseDemo2");
//簡單的重定向方法
response.sendRedirect("/day15/responseDemo2");
* 重定向的特點:redirect
1. 地址欄發生變化
2. 重定向可以訪問其他站點(伺服器)的資源
3. 重定向是兩次請求,不能使用request對象來共用數據
* 轉發的特點:forward
1. 轉發地址欄路徑不變
2. 轉發只能訪問當前伺服器下的資源
3. 轉發是一次請求,可以使用request對象來共用數據
* forward 和 redirect 區別
* 路徑寫法:
1. 路徑分類
1. 相對路徑:通過相對路徑不可以確定唯一資源
* 如:./index.html
* 不以/開頭,以.開頭路徑
* 規則:找到當前資源和目標資源之間的相對位置關係
* ./ :當前目錄
* ../ :後退一級目錄
2. 絕對路徑:通過絕對路徑可以確定唯一資源
* 如:http://localhost/day15/responseDemo2 /day15/responseDemo2
* 以/開頭的路徑
* 規則:判斷定義的路徑是給誰用的?判斷請求將來從哪兒發出
* 給客戶端瀏覽器使用:需要加虛擬目錄(項目的訪問路徑)
* 建議虛擬目錄動態獲取:request.getContextPath()
* <a> , <form> 重定向...
* 給伺服器使用:不需要加虛擬目錄
* 轉發路徑
2. 伺服器輸出字元數據到瀏覽器
* 步驟:
1. 獲取字元輸出流
2. 輸出數據
* 註意:
* 亂碼問題:
1. PrintWriter pw = response.getWriter();獲取的流的預設編碼是ISO-8859-1
2. 設置該流的預設編碼
response.setCharacterEncoding("utf-8");
3. 告訴瀏覽器響應體使用的編碼
response.setHeader("content-type","text/html;charset=utf-8");
//簡單的形式設置編碼,是在獲取流之前設置(以後使用這種方式設置即可)
response.setContentType("text/html;charset=utf-8");
3. 伺服器輸出位元組數據到瀏覽器
* 步驟:
1. 獲取位元組輸出流
2. 輸出數據
4. 驗證碼
* 本質:圖片
* 目的:防止惡意表單註冊
代碼:
@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int width = 100;
int height = 50;
//1.創建一對象,在記憶體中圖片(驗證碼圖片對象)
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//2.美化圖片
//2.1 填充背景色
Graphics g = image.getGraphics();
g.setColor(Color.pink);
g.fillRect(0,0,width,height);
//2.2 畫邊框
g.setColor(Color.blue);
g.drawRect(0,0,width-1,height-1);
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Random ran = new Random();
for (int i = 1;i <= 4;i++){
int index = ran.nextInt(str.length());
//獲取字元
char ch = str.charAt(index);
//2.3 寫驗證碼
g.drawString(ch+"", (width / 5) * i,25);
}
//2.4 畫干擾線
g.setColor(Color.green);
for (int i = 0;i< 10;i++){
//隨機生成坐標點
int x1 = ran.nextInt(width);
int x2 = ran.nextInt(width);
int y1 = ran.nextInt(height);
int y2 = ran.nextInt(height);
g.drawLine(x1,y1,x2,y2);
}
//3.將圖片輸出到頁面展示
ImageIO.write(image, "jpg",response.getOutputStream());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
添加點擊圖片或超鏈接切換驗證碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
/*
分析:
點擊超鏈接或圖片,需要換一張
1.給超鏈接和圖片綁定單擊事件
2.重新設置圖片的src屬性值
*/
window.onload = function () {
//1.獲取圖片對象
var img = document.getElementById("checkCode");
//2.綁定單擊事件
img.onclick = function () {
//加時間戳,解決瀏覽器緩存問題
var date = new Date().getTime();
img.src = "/day15/checkCodeServlet?"+date;
}
a.onclick = function () {
var date = new Date().getTime();
img.src = "/day15/checkCodeServlet?"+date;
}
}
</script>
</head>
<body>
<img id="checkCode" src="/day15/checkCodeServlet" />
<a id="change" href="">看不清換一張?</a>
</body>
</html>
ServletContext對象:
1. 概念:代表整個web應用,可以和程式的容器(伺服器)來通信
2. 獲取:
1. 通過request對象獲取
request.getServletContext();
2. 通過HttpServlet獲取
this.getServletContext();
3. 功能:
1. 獲取MIME類型:
* MIME類型:在互聯網通信過程中定義的一種文件數據類型
* 格式: 大類型/小類型 text/html image/jpeg
* 獲取:String getMimeType(String file)
2. 域對象:共用數據
1. setAttribute(String name,Object value)
2. getAttribute(String name)
3. removeAttribute(String name)
* ServletContext對象範圍:所有用戶所有請求的數據
3. 獲取文件的真實(伺服器)路徑
1. 方法:String getRealPath(String path)
String b = context.getRealPath("/b.txt");//web目錄下資源訪問
System.out.println(b);
String c = context.getRealPath("/WEB-INF/c.txt");//WEB-INF目錄下的資源訪問
System.out.println(c);
String a = context.getRealPath("/WEB-INF/classes/a.txt");//src目錄下的資源訪問
System.out.println(a);
案例:
* 文件下載需求:
1. 頁面顯示超鏈接
2. 點擊超鏈接後彈出下載提示框
3. 完成圖片文件下載
* 分析:
1. 超鏈接指向的資源如果能夠被瀏覽器解析,則在瀏覽器中展示,如果不能解析,則彈出下載提示框。不滿足需求
2. 任何資源都必須彈出下載提示框
3. 使用響應頭設置資源的打開方式:
* content-disposition:attachment;filename=xxx
* 步驟:
1. 定義頁面,編輯超鏈接href屬性,指向Servlet,傳遞資源名稱filename
2. 定義Servlet
1. 獲取文件名稱
2. 使用位元組輸入流載入文件進記憶體
3. 指定response的響應頭: content-disposition:attachment;filename=xxx
4. 將數據寫出到response輸出流
* 問題:
* 中文文件問題
* 解決思路:
1. 獲取客戶端使用的瀏覽器版本信息
2. 根據不同的版本信息,設置filename的編碼方式不同
* 代碼:
Servlet:
@WebServlet(name = "downloadServlet")
public class DownloadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 獲取請求參數,文件名稱
String filename = request.getParameter("filename");
//2.使用位元組輸入流載入文件進記憶體
//2.1 找到文件伺服器路徑
ServletContext servletContext = this.getServletContext();
String realPath = servletContext.getRealPath("/img/"+filename);
//2.2 用位元組流關聯
FileInputStream fis = new FileInputStream(realPath);
//3.設置response的響應頭
//3.1 設置響應頭類型: content-type
String mimeType = servletContext.getMimeType(filename);
response.setHeader("content-type",mimeType);
//解決中文文件名問題
//1.獲取user-agent請求頭
String agent = request.getHeader("user-agent");
//2.使用工具類方法編碼文件名即可
filename = DownLoadUtils.getFileName(agent, filename);
//3.2 設置響應頭打開方式: content-disposition
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();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
下載工具類:
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;
}
}