HttpServletRequest与HttpServletResponse详解及快递管理系统实践
第一章 HttpServletRequest请求对象
1. HttpServletRequest概述
- 概述 :
HttpServletRequest extends ServletRequest
-> 请求对象(request对象) - 请求方式 :
- GET请求:请求参数在浏览器地址栏上,没有请求体
- POST请求:请求参数在请求体中
- 请求参数形式 :
key=value
形式,多个参数之间用&
链接 - 请求路径 :
localhost:8080/web应用名称/资源?key=value&key=value
- key:页面上的name属性值
- value:页面上的value属性值
1.1 HttpServletRequest的组成
请求包含请求报文,请求报文由以下部分组成:
- 请求行 :
- GET请求:
请求方式 请求路径?请求参数 协议版本
- POST请求:
请求方式 请求地址 协议版本
- GET请求:
- 请求头:对服务器解析请求的指导性信息
- 请求空行(可忽略)
- 请求体 :
- GET请求没有请求体
- POST请求有请求体,包含请求参数
1.2 HttpServletRequest的作用
- 获取请求报文中的数据(主要是请求体内容)
- 实现请求转发(重定向)
- 作为域对象存储数据(请求域)
2. HttpServletRequest获取HTTP请求内容的方法
2.1 获取请求行方法
方法名 | 说明 |
---|---|
getMethod() | 获取请求方式(常用) |
getContextPath() | 获取当前应用上下文路径(在application context中设置的名字)(常用,四个方法中的重点) |
getRequestURI() | 获取请求地址,不带主机名 |
getRequestURL() | 获取请求地址,带主机名 |
java
@WebServlet("/request1")
public class Request1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取请求方式
String method = request.getMethod();
System.out.println(method);
System.out.println("======================");
// 获取web应用名称
String contextPath = request.getContextPath();
System.out.println(contextPath);
System.out.println("=======================");
// 获取请求地址,不带主机名
String requestURI = request.getRequestURI();
System.out.println(requestURI);
System.out.println("=======================");
// 获取请求地址,带主机名
StringBuffer requestURL = request.getRequestURL();
System.out.println(requestURL);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
测试页面:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页1</title>
</head>
<body>
<form action="/day05_request_response/request1" method="post">
用户名:<input type="text" name="username"/><br/>
密 码:<input type="password" name="password"/><br/>
<input type="submit" value="登录">
</form>
</body>
</html>
2.2 获取请求头信息方法
方法名 | 说明 |
---|---|
getHeader(String name) | 根据请求的key获取对应的value |
java
@WebServlet("/Servlet2")
public class Servlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String host = req.getHeader("Host");
System.out.println(host);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
常见请求头说明:
- Accept:浏览器能够处理的内容类型
- Accept-Charset:浏览器能够显示的字符集
- Accept-Encoding:浏览器能够处理的压缩编码
- Accept-Language:浏览器当前设置的语言
- Connection:浏览器与服务器之间连接的类型
- Cookie:当前页面设置的任何Cookie
- Host:发出请求的页面所在的域
- Referer:发出请求的页面的URL
- User-Agent:浏览器的用户代理字符串
2.3 获取请求体中的请求参数(重点)
-
请求参数位置:
- GET:请求参数在地址栏上 ->
localhost:8080/web应用名称/资源?key=value&key=value
- POST:请求参数在请求体中,格式为
key=value&key=value
- GET:请求参数在地址栏上 ->
-
参数说明:
- key:页面上的name属性值
- value:页面上的value属性值
- 实际需要获取的是value值,通过key获取
方法名 | 返回值类型 | 方法描述 |
---|---|---|
request.getParameter("请求参数的name属性名") | String | 根据提交参数的name获取对应的value |
request.getParameterValues("请求参数的name属性名") | String[] | 根据提交参数的name获取多个value |
request.getParameterMap() | Map<String,String[]> | 获取提交请求的所有参数,以键值对的方式存到map中 |
示例页面:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>获取提交参数</title>
</head>
<body>
<a href="/day05_request_response/request3?username=tom&password=111">访问request3</a><br/>
<hr color="red" size="2">
<form action="/day05_request_response/request3" method="get">
用户名<input type="text" name="username"/><br/>
密码<input type="text" name="password"/><br/>
昵称<input type="text" name="nickname"/><br/>
邮箱<input type="text" name="email"/><br/>
<!--复选框-->
兴趣爱好<input type="checkbox" name="hobby" value="basketball"/>篮球
<input type="checkbox" name="hobby" value="football"/>足球
<input type="checkbox" name="hobby" value="yumaoball"/>羽毛球
<input type="checkbox" name="hobby" value="pingpangball"/>乒乓球<br/>
<button type="submit">提交</button>
</form>
</body>
</html>
处理代码:
java
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
@WebServlet("/request3")
public class request3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println("username:"+username+"password:"+password);
System.out.println("=======================");
String username1 = req.getParameter("username");
String password1 = req.getParameter("password");
String nickname = req.getParameter("nickname");
String email = req.getParameter("email");
System.out.println(username1+"..."+password1+"..."+nickname+"..."+email);
System.out.println("==============");
// 获取多个参数值
String[] hobbies = req.getParameterValues("hobby");
System.out.println("hobbies:"+ Arrays.toString(hobbies));
System.out.println("===============");
// 获取所有参数的Map集合
Map<String, String[]> parameterMap = req.getParameterMap();
Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();
for (Map.Entry<String, String[]> entry : entries) {
System.out.println(entry.getKey()+":"+Arrays.toString(entry.getValue()));
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
2.4 解决获取请求参数乱码
2.4.1 乱码原因
- 编码和解码遵循的编码规则不一样
- 与tomcat的解码方式相关:
- tomcat10采用UTF-8编码
- tomcat获取请求参数主要是解析url中的参数
- tomcat8及以上版本优化:
- GET请求:参数在url上,直接用UTF-8解析
- POST请求:参数在请求体中,tomcat默认编码可能导致中文乱码
2.4.2 解决方法(主要针对post请求)
方法名 | 说明 |
---|---|
request.setCharacterEncoding("UTF-8") | 将获取到的请求参数设置为utf-8格式 |
java
@WebServlet("/request4")
public class Request4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 处理请求中文乱码
request.setCharacterEncoding("utf-8");
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println(username);
System.out.println(password);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
测试页面:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页request4</title>
</head>
<body>
<form action="/day05_request_response/request4" method="post">
用户名:<input type="text" name="username"/><br/>
密 码:<input type="password" name="password"/><br/>
<input type="submit" value="登录">
</form>
</body>
</html>
3. 请求转发
3.1 什么是请求转发
- 概述:通过项目下的一个资源将请求转发到另外一个该项目资源下的技术
- 特点 :
- 整个过程只产生一个请求,浏览器地址栏不会变化
- 是服务器内部行为,浏览器不知情
- 在同一个请求中完成,只有一次请求
3.2 请求转发方法
方法名 | 说明 |
---|---|
RequestDispacher getRequestDispacher(String path) | 获取请求转发器对象 参数:path指目标资源路径 1. 转发到servlet:写servlet对应的url-pattern 2. 转发到页面:写页面路径 |
forward(request对象,response对象) | 实现请求转发,是RequestDispacher对象的方法 |
3.3 请求转发演示
java
// 浏览器访问request4,转发到request5或页面
@WebServlet("/request4")
public class request4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 请求转发到request5
// req.getRequestDispatcher("/request5").forward(req,resp);
// 请求转发到WEB-INF下的index4.html
req.getRequestDispatcher("/WEB-INF/index.html").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
目标Servlet:
java
@WebServlet("/request5")
public class request5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("I am LiuDaDan");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
3.4 总结
- 请求转发是服务器内部行为,浏览器只发一次请求
- 只能转发到当前web应用下的资源
- 地址栏不会变化
- 转发涉及的资源属于同一个请求
4. HttpServletRequest域对象
- 域对象对比 :
- ServletContext:代表整个web应用程序,作用范围最大,全应用共享
- HttpServletRequest域(请求域):存储的数据只能在当前请求中共享,新请求无法获取
4.1 HttpServletRequest域对象方法
方法名 | 说明 |
---|---|
setAttribute(String key,Object o) | 存数据 |
getAttribute(String key) | 根据key获取value |
removeAttribute(String key) | 根据key删除value |
4.2 演示代码
java
@WebServlet("/request7")
public class request7 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("username","liudadan");
// 必须请求转发,因为HttpServletRequest域只在同一请求中有效
req.getRequestDispatcher("/request8").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
java
// 在request8中获取request7存储的数据
@WebServlet("/request8")
public class request8 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Object username = req.getAttribute("username");
System.out.println("username:"+username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
第二章 HttpServletResponse响应对象
1. HttpServletResponse响应对象介绍
1.1 概念
HttpServletResponse extends ServletResponse
1.2 作用
给浏览器设置响应信息,包括:
- 响应行
- 响应头
- 响应体
2. HttpServletResponse使用
2.1 设置响应状态码(了解)
常见状态码:
- 200:响应成功
- 304:缓存
- 302:重定向
- 404:找不到资源
- 500:服务器内部错误
方法名 | 说明 |
---|---|
response.setStatus(状态码) | 设置响应行中的响应状态码 |
java
@WebServlet("/response1")
public class Response1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// response.setStatus(404);
System.out.println(1/0); // tomcat会默认给出报错信息
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
响应状态码通常由tomcat自动设置,无需手动干预
2.2 设置响应头(了解)
响应头数据格式:k:v
方法名 | 说明 |
---|---|
setHeader(String key,String value) | 设置响应头的key和value |
java
@WebServlet("/response2")
public class Response2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("myHeader","taoge"); // 设置响应头
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
2.3 设置响应体(重要)
2.3.1 使用字符输出流响应
方法名 | 说明 |
---|---|
PrintWriter getWriter() | 使用字符流作出响应,需注意中文乱码 |
java
@WebServlet("/response3")
public class Response3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>Title</title>\n" +
"</head>\n" +
"<body>\n" +
" hello,i am taoge,i am 16 years old\n" +
"</body>\n" +
"</html>");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
2.3.2 解决响应中文乱码
方法名 | 说明 |
---|---|
response.setContentType("text/html;charset=utf-8") | 设置响应头,解决中文乱码 |
java
@WebServlet("/response4")
public class Response4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("你好,我是一个大帅哥!");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
2.4 设置浏览器重定向
概述:从web项目资源跳转到另一个资源,浏览器会发起两次请求,是一个新的请求。
方法名 | 说明 |
---|---|
response.sendRedirect("重定向的资源路径") | 请求重定向到另外一个资源中去 |
示例代码:
java
// 访问response5,重定向到response6
@WebServlet("/response5")
public class Response5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 重定向到response6,需添加上下文地址
response.sendRedirect(request.getContextPath()+"/response6");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
@WebServlet("/response6")
public class Response6 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("你好,我是一个大帅哥!");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
2.5 重定向和请求转发区别
- 重定向会由浏览器发起新的请求,请求转发不会
- 重定向可以访问任意互联网资源,请求转发只能访问本项目资源
- 重定向不能访问本项目WEB-INF内的资源,请求转发可以
- 重定向涉及的资源不在同一次请求中,不能使用请求域;请求转发涉及的资源在同一次请求中,可以使用请求域
第三章 MVC模式
1. 为什么需要MVC
直接在Servlet中通过write
方法编写页面代码维护性差,需要将视图(页面)单独抽取为View层。但HTML无法展示动态数据,需要引入服务器端动态视图模板技术(如jsp或Thymeleaf)。
2. MVC概念
-
定义:web应用程序的开发模式
- M:Model模型 -> 处理数据
- V:View视图 -> 页面展示
- C:Controller控制器 -> 协调调度
-
理念:在表现层开发中,将封装数据的模型、显示界面的视图、协调调度的控制器分开。
-
好处:
- 实现组件解耦
- 便于单独维护
- 方便前后端工程师协作
3. MVC和三层架构之间关系
MVC属于表现层的设计模式,与三层架构(表现层、业务逻辑层、数据访问层)相辅相成,共同构成完整的应用程序架构。
第四章 快递管理系统第二期
1. 准备工作
1.1 数据库准备
导入资料中的deliver.sql
到数据库
1.2 项目搭建(delivery-system)
1.2.1 创建Javaweb工程(delivery-system)
1.2.2 导入页面
将资料中的页面导入到项目中
1.2.3 修改项目登录页面
xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<!-- 设置登录为欢迎页面 -->
<welcome-file-list>
<welcome-file>login.html</welcome-file>
</welcome-file-list>
</web-app>
改完web.xml后若仍跳转index.html,需清除浏览器缓存(ctrl+shift+delete)
1.2.4 导入数据库相关环境及创建类
导入数据库连接相关的工具类和配置文件
1.2.5 导入MD5加密工具类
java
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public final class MD5Utils {
public static String encrypt(String strSrc) {
try {
char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f' };
byte[] bytes = strSrc.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(bytes);
bytes = md.digest();
int j = bytes.length;
char[] chars = new char[j * 2];
int k = 0;
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
chars[k++] = hexChars[b >>> 4 & 0xf];
chars[k++] = hexChars[b & 0xf];
}
return new String(chars);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("MD5加密出错!!!");
}
}
}
1.2.6 导入pojo类
java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class SysUser implements Serializable {
private Integer id;
private String username;
private String password;
private String nickname;
}
2. 登录业务代码
2.1 MVC架构模式实现
2.1.1 部署项目
按照标准流程部署web项目
2.1.2 修改login页面表单参数
调整表单的action和参数名,确保与后端接收一致
2.1.3 Controller层
java
import java.io.*;
import com.atguigu.pojo.SysUser;
import com.atguigu.service.UserService;
import com.atguigu.service.impl.UserServiceImpl;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.WebServlet;
import java.io.IOException;
import java.sql.SQLException;
@WebServlet("/user/login")
public class UserLoginController extends HttpServlet {
private UserService userService = new UserServiceImpl();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
// 设置请求参数编码格式
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
// 获取请求参数
String username = request.getParameter("username");
String password = request.getParameter("password");
// 调用service层方法
SysUser sysUser = userService.login(username,password);
if (sysUser!=null){
request.getRequestDispatcher("/index.html").forward(request,response);
}else{
request.getRequestDispatcher("/login.html").forward(request,response);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
2.1.4 Service层
接口:
java
public interface UserService {
SysUser login(String username, String password) throws SQLException;
}
实现类:
java
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
@Override
public SysUser login(String username, String password) throws SQLException {
// 密码加密
password = MD5Utils.encrypt(password);
// 调用dao层
SysUser sysUser = userDao.login(username,password);
return sysUser;
}
}
2.1.5 Dao层
接口:
java
public interface UserDao {
SysUser login(String username, String password) throws SQLException;
}
实现类:
java
public class UserDaoImpl implements UserDao {
private QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
@Override
public SysUser login(String username, String password) throws SQLException {
String sql = "select id,username,password,nickname from sys_user where username = ? and password = ?";
SysUser sysUser = qr.query(sql, new BeanHandler<SysUser>(SysUser.class), username, password);
return sysUser;
}
}
2.2 解决转发页面样式丢失问题
修改login.html
和index.html
中的资源路径,添加web应用名称:
html
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>快递管理系统</title>
<meta name="Copyright" content="Douco Design." />
<link href="/delivery/css/public.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="/delivery/js/jquery.min.js"></script>
<script type="text/javascript" src="/delivery/js/global.js"></script>
</head>