JavaWeb之HttpServletRequest与HttpServletResponse详解及快递管理系统实践

HttpServletRequest与HttpServletResponse详解及快递管理系统实践

第一章 HttpServletRequest请求对象

1. HttpServletRequest概述

  1. 概述HttpServletRequest extends ServletRequest -> 请求对象(request对象)
  2. 请求方式
    • GET请求:请求参数在浏览器地址栏上,没有请求体
    • POST请求:请求参数在请求体中
  3. 请求参数形式key=value形式,多个参数之间用&链接
  4. 请求路径localhost:8080/web应用名称/资源?key=value&key=value
    • key:页面上的name属性值
    • value:页面上的value属性值

1.1 HttpServletRequest的组成

请求包含请求报文,请求报文由以下部分组成:

  1. 请求行
    • GET请求:请求方式 请求路径?请求参数 协议版本
    • POST请求:请求方式 请求地址 协议版本
  2. 请求头:对服务器解析请求的指导性信息
  3. 请求空行(可忽略)
  4. 请求体
    • GET请求没有请求体
    • POST请求有请求体,包含请求参数

1.2 HttpServletRequest的作用

  1. 获取请求报文中的数据(主要是请求体内容)
  2. 实现请求转发(重定向)
  3. 作为域对象存储数据(请求域)

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/>
    密&nbsp;&nbsp;&nbsp;码:<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 获取请求体中的请求参数(重点)
  1. 请求参数位置

    • GET:请求参数在地址栏上 -> localhost:8080/web应用名称/资源?key=value&key=value
    • POST:请求参数在请求体中,格式为key=value&key=value
  2. 参数说明

    • 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 乱码原因
  1. 编码和解码遵循的编码规则不一样
  2. 与tomcat的解码方式相关:
    • tomcat10采用UTF-8编码
    • tomcat获取请求参数主要是解析url中的参数
  3. 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/>
    密&nbsp;&nbsp;&nbsp;码:<input type="password" name="password"/><br/>
    <input type="submit" value="登录">
</form>
</body>
</html>

3. 请求转发

3.1 什么是请求转发
  1. 概述:通过项目下的一个资源将请求转发到另外一个该项目资源下的技术
  2. 特点
    • 整个过程只产生一个请求,浏览器地址栏不会变化
    • 是服务器内部行为,浏览器不知情
    • 在同一个请求中完成,只有一次请求
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 总结
  1. 请求转发是服务器内部行为,浏览器只发一次请求
  2. 只能转发到当前web应用下的资源
  3. 地址栏不会变化
  4. 转发涉及的资源属于同一个请求

4. HttpServletRequest域对象

  1. 域对象对比
    • 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 重定向和请求转发区别
  1. 重定向会由浏览器发起新的请求,请求转发不会
  2. 重定向可以访问任意互联网资源,请求转发只能访问本项目资源
  3. 重定向不能访问本项目WEB-INF内的资源,请求转发可以
  4. 重定向涉及的资源不在同一次请求中,不能使用请求域;请求转发涉及的资源在同一次请求中,可以使用请求域

第三章 MVC模式

1. 为什么需要MVC

直接在Servlet中通过write方法编写页面代码维护性差,需要将视图(页面)单独抽取为View层。但HTML无法展示动态数据,需要引入服务器端动态视图模板技术(如jsp或Thymeleaf)。

2. MVC概念

  1. 定义:web应用程序的开发模式

    • M:Model模型 -> 处理数据
    • V:View视图 -> 页面展示
    • C:Controller控制器 -> 协调调度
  2. 理念:在表现层开发中,将封装数据的模型、显示界面的视图、协调调度的控制器分开。

  3. 好处

    • 实现组件解耦
    • 便于单独维护
    • 方便前后端工程师协作

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.htmlindex.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>
相关推荐
怪兽20142 小时前
JRE、JDK、JVM 及 JIT 之间有什么不同?
java·面试
vker2 小时前
第 4 天:建造者模式(Builder Pattern)—— 创建型模式
java·后端·设计模式
我不是混子3 小时前
MySQL中如何查看数据库容量大小、表容量大小、索引容量大小?
后端·mysql
奶糖 肥晨3 小时前
模型驱动的 AI Agent架构:亚马逊云科技的Strands框架技术深度解析
人工智能·科技·架构
Coding_Doggy3 小时前
java面试day3 | 框架篇、Spring、SpringMVC、SpringBoot、MyBatis、注解、AOP、Bean
java·mysql·面试
remaindertime3 小时前
从“万能 ES”到专业 ClickHouse:一次埋点数据存储的选择
数据库·架构
椎名澄嵐3 小时前
★ Linux ★ 信号
linux·运维·服务器·开发语言·c++
双向333 小时前
【征文计划】深度剖析 Rokid SLAM 算法:从传感器融合到空间重建的完整技术链路
数据库
Li zlun3 小时前
Kubernetes 进阶实战:CRD、Gateway API 与优先级调度
java·kubernetes·gateway