1. HttpServletRequest
1.1 HttpServletRequest 介绍
- HttpServletRequest 对象代表客户端的请求
- 当客户端/浏览器通过 HTTP 协议访问服务器时,HTTP 请求头中的所有信息都封装在这
个对象中 - 通过这个对象的方法,可以获得客户端这些信息。
1.2 HttpServletRequest 类图
1.3 HttpServletRequest 常用方法
- getRequestURI() 获取请求的资源路径 http://localhost:8080/servlet/loginServlet
- getRequestURL() 获 取 请 求 的 统 一 资 源 定 位 符 ( 绝 对 路 径 )http://localhost:8080/servlet/loginServlet
- getRemoteHost() 获取客户端的 主机, getRemoteAddr()
- getHeader() 获取请求头
- getParameter() 获取请求的参数
- getParameterValues() 获取请求的参数(多个值的时候使用) , 比如 checkbox, 返回的数组
- getMethod() 获取请求的方式 GET 或 POST
- setAttribute(key, value); 设置域数据
- getAttribute(key); 获取域数据
getRequestDispatcher()
获取请求转发对象, 请求转发的核心对象
1.4 HttpServletRequest 应用实例
- 需求: 说明: 在一个表单提交数据给 Servlet , 然后在 Servlet 通过 HttpServletRequest对象获取相关数据
- 创建 register_request.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<h1>注册用户</h1>
<form action="http://localhost:8080/servlet/requestMethods"
method="post">
u: <input type="text" name="username"/><br><br>
p: <input type="password" name="pwd"/><br><br>
选择你喜欢的老师:
<input type="checkbox" name="hobby" value="hsp">韩顺平
<input type="checkbox" name="hobby" value="lh">老韩
<input type="checkbox" name="hobby" value="spls">顺平老师<br/><br/>
<input type="submit" value="注册用户"/>
</form>
</body>
</html>
public class HttpServletRequestMethods extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//这里我们使用request对象,获取表单提交的各种数据
System.out.println("HttpServletRequestMethods doPost() 被调用..");
/***********************************
* 获取和http请求头相关信息
***********************************/
System.out.println("请求的资源路径URI= " + request.getRequestURI());
//http://主机/uri
System.out.println("请求的统一资源定位符(绝对路径)URL= " + request.getRequestURL());
System.out.println("请求的客户端ip 地址= " + request.getRemoteAddr());//本地就是127.0.01
//思考题:如发现某个ip 在10s中,访问的次数超过 100次,就封ip
//实现思路: 1用一个集合concurrentHashmap[ip:访问次数] 2[线程/定时扫描] 3 做成处理
// 获取http请求头的信息,可以指定其他,比如 User-Agent , Host等待 老师就举一个例子
System.out.println("http请求头HOST= " + request.getHeader("Host"));
// 说明,如果我们希望得到请求的头的相关信息,可以使用request.getHeader("请求头字段")
System.out.println("该请求的发起地址是= " + request.getHeader("Referer"));
// 请获取访问网站的浏览器是什么?
String userAgent = request.getHeader("User-Agent");
System.out.println("User-Agent= " + userAgent);
// 取出FireFox, 取出最后
String[] s = userAgent.split(" ");
System.out.println("浏览器=" + s[s.length - 1].split("\\/")[0]);
//获取 Cookie
// JSESSIONID=8CBBD23BDE01BAE6705E03C5C8916BD1
String cookie = request.getHeader("Cookie");
String JSESSIONID = cookie.split("=")[1];
System.out.println("取出JSESSIONID= " + JSESSIONID);
//课堂练习: 要求同学们取出 Windows NT 10.0 和 Win64
// 主要是Get / Post
System.out.println("http请求方式~= " + request.getMethod());
/***********************************
* 获取和请求参数相关信息, 注意要求在返回数据前,获取参数
***********************************/
//解决接收参数的中文乱码问题, 老师提示,写在 getParameter前.
request.setCharacterEncoding("utf-8");
//1. 获取表单的数据[单个数据]
//username=tom&pwd=&hobby=hsp&hobby=spls
String username = request.getParameter("username");
String pwd = request.getParameter("pwd");
//2. 获取表单一组数据
String[] hobbies = request.getParameterValues("hobby");
System.out.println("username= " + username);
System.out.println("pwd= " + pwd);
//增强for循环的快捷键 iter->回车即可 , 能使用快捷键,就使用快捷键
for (String hobby : hobbies) {
System.out.println("hobby=" + hobby);
}
//推而广之, 如果是 单选 , 下拉框 等等. => 作业布置
//返回接收到的信息, 给浏览器回显
//本质就是在http响应头,加上 Content-Type: text/html;charset=utf-8
//说 text/html 表示返回的数据类型,浏览器会根据这个类型来解析数据
// text/plain 表示返回的数据,请浏览器使用文本方式解析
// application/x-tar 表示返回的是文件,浏览器就会以下载文件的方式处理
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("<h1>提交的用户名= " + username + "</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
修改 web.xml 添加配置
<servlet>
<servlet-name>HttpServletRequestMethods</servlet-name>
<servlet-class>com.hspedu.httpservletrequest.HttpServletRequestMethods</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HttpServletRequestMethods</servlet-name>
<url-pattern>/httpServletRequestMethods</url-pattern>
</servlet-mapping>
完成测试…
1.5 HttpServletRequest 注意事项和细节
说明
: 注意事项和细节,在 HttpServletRequestMethods.java 上修改演示即可
- 获 取 doPost 参 数 中 文 乱 码 解 决 方 案 , 注 意 setCharacterEncoding(“utf-8”) 要 写 在request.getParameter()前。
- 注意:如果通过 PrintWriter writer, 有返回数据给浏览器,建议将获取参数代码写在writer.print() 之前,否则可能获取不到参数值(doPost)
- 处理 http 响应数据中文乱码问题
- 再次理解 Http 协议响应 Content-Type 的含义, 比如 text/plain application/x-tar
1.6 课堂小练习
1、请通过 HttpServletRequest 对象获取到 , 是什么浏览器访问服务器
String userAgent = request.getHeader("User-Agent");
System.out.println("User-Agent= " + userAgent);
// 取出FireFox, 取出最后
String[] s = userAgent.split(" ");
System.out.println("浏览器=" + s[s.length - 1].split("\\/")[0]);
2、请想办法获取到 JSESSIONID 的值.
//获取 Cookie
// JSESSIONID=8CBBD23BDE01BAE6705E03C5C8916BD1
String cookie = request.getHeader("Cookie");
String JSESSIONID = cookie.split("=")[1];
System.out.println("取出JSESSIONID= " + JSESSIONID);
1.7 请求转发
1.7.1 为什么需要请求转发
- 目前我们学习的都是一次请求,对应一个 Servlet, 如图
- 但是在实际开发中,往往业务比较复杂,需要在一次请求中,使用到多个 Servlet 完成一个任务(Servlet 链, 流水作业) 如图:
1.7.2 请求转发说明
- 实现请求转发:请求转发指一个 web 资源收到客户端请求后,通知服务器去调用另外一个 web 资源进行处理
- HttpServletRequest 对象(也叫 Request 对象)提供了一个getRequestDispatcher 方法,该方法返回一个 RequestDispatcher 对象,调用这个对象的 forward 方法可以实现请求转发
- request 对象同时也是一个域对象,开发人员通过 request 对象在实现转发时,把数据通过 request 对象带给其它 web 资源处理
- setAttribute方法
- getAttribute方法
- removeAttribute方法
- getAttributeNames方法
1.7.3 实现请求转发
请求转发原理示意图
url是不会变化的
1.7.4 请求转发应用实例
● 需求说明/图解, 如果是 tom,提示为管理员,其它是普通用户
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="http://localhost:8080/servlet/checkServlet" method="post">
u: <input type="text" name="username"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
CheckServlet.java
public class CheckServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("CheckServlet 被调用..");
//根据用户名来确定该用户是什么身份
String username = request.getParameter("username");
//注意:如果是同一个request对象(请求转发),那么可以在不同的servlet中,是getParameter
//这里使用tom去点,而不是username去点,是因为username如果为空,会出现空指针异常
if ("tom".equals(username)) {
//分配
request.setAttribute("role", "管理员");
} else {
request.setAttribute("role", "普通用户");
}
//获取分发器
//解读 1. /manageServlet写的是 要转发的servlet的url
// 2. / 会被解析成 /servlet
// 3. forward(request, response) 表示把当前servlet的request对象和response对象,传递给下一个servlet使用
RequestDispatcher requestDispatcher =
request.getRequestDispatcher("/manageServlet");
requestDispatcher.forward(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
ManageServlet.java
public class ManageServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ManageServlet 被调用..");
String username = request.getParameter("username");
String role = (String) request.getAttribute("role");
//输出信息
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("用户名: " + username + "<br/>");
writer.print("角色 : " + role);
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
完成测试: 注意看浏览器的请求次数和数据通信情况.
1.7.5 请求转发注意事项和细节
- 浏览器地址不会变化(地址会保留在第 1 个 servlet 的 url)
- 在同一次 HTTP 请求中,进行多次转发,仍然是一次 HTTP 请求
- 在同一次 HTTP 请求中,进行多次转发,多个 Servlet 可以共享 request 域/对象的数据(因为始终是同一个 request 对象)
- 可以转发到 WEB-INF 目录下(后面做项目使用)
- 不能访问当前 WEB 工程外的资源
在前面的案例基础上演示一把
- 因为浏览器地址栏会停止在第一个 servlet ,如果你刷新页面,会再次发出请求(并且会带数据), 所以在支付页面情况下,不要使用请求转发,否则会造成重复支付[演示]
1.8 课后作业
1、下面是一个表单,前面我们使用过,完成如下功能
- 编写一个 RegisterServlet , 能够接收到提交的各种数据
- 并把接收到的数据返回给浏览器, 并显示(回显)
- 注意处理中文乱码问题
- 暂不处理文件提交
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>表单综合案例</title>
</head>
<body>
<form action="http://localhost:8080/servlet/registerServlet" method="post">
用户注册信息<br/>
用户名称: <input type="text" name="username"><br/>
用户密码: <input type="password" name="pwd1"><br/>
确认密码: <input type="password" name="pwd2"><br/>
选择你喜欢的运动项目:
<input type="checkbox" name="sport" value="篮球">篮球<br/>
<input type="checkbox" name="sport" value="足球" checked>足球<br/>
<input type="checkbox" name="sport" value="手球" checked>手球<br/>
请选择性别 :
<input type="radio" name="gender" value="male">男<br/>
<input type="radio" name="gender" value="female">女<br/>
请选择城市:
<select name="city">
<option>--选择--</option>
<option value="cd">成都</option>
<option value="bj">北京</option>
<option value="sh">上海</option>
</select><br/>
自我介绍:
<textarea name="info" rows="6" cols="20"></textarea><br/>
选择你的文件(头像)<input type="file" name="myfile"><br/>
<input type="submit" value="提交"/> <input type="reset" value="重置"/>
</form>
</body>
</html>
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("RegisterServlet 被调用...");
request.setCharacterEncoding("utf-8");
String username = request.getParameter("username");
String pwd1 = request.getParameter("pwd1");
String pwd2 = request.getParameter("pwd2");
//获取 checkbox
String[] sports = request.getParameterValues("sport");
//对 sports 处理
String sportsStr = "";
for (String sport : sports) {
sportsStr += sport + " ";
}
//获取 radio
String gender = request.getParameter("gender");
//获取 select
String city = request.getParameter("city");
//获取 textarea
String info = request.getParameter("info");
//返回给浏览器,回显
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("username= " + username + "<br/>");
writer.print("pwd1= " + pwd1 + "<br/>");
writer.print("pwd2= " + pwd2 + "<br/>");
writer.print("喜欢的运动= " + sportsStr + "<br/>");
writer.print("gender= " + gender + "<br/>");
writer.print("city= " + city + "<br/>");
writer.print("info= " + info + "<br/>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
web.xml
<servlet>
<servlet-name>RegisterServlet</servlet-name>
<servlet-class>com.hspedu.servlet.request.homework.RegisterServlet</servlet-class
>
</servlet>
<servlet-mapping>
<servlet-name>RegisterServlet</servlet-name>
<url-pattern>/registerServlet</url-pattern>
</servlet-mapping>
2、请编写一个 Servlet, 可以获取到浏览器所在电脑的操作系统版本和位数(32 还是 64), 显示在页面即可
public class ComputerServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//System.out.println("ComputerServlet ..");
//可以获取到浏览器所在电脑的操作系统版本和位数(32还是64), 显示在页面即可
//分析一把 http请求头
//User-Agent
// Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0
// 我们要 Windows NT 10.0 和 Win64
// 老师解读
// (1) Windows NT 10.0; Win64; x64; rv:97.0
// (2) Windows NT 10.0 和 Win64
// (3) 老师回顾使用正则表达式
String userAgent = request.getHeader("User-Agent");
//java基础 正则表达式
String regStr = "\\((.*)\\)";
Pattern compile = Pattern.compile(regStr);
Matcher matcher = compile.matcher(userAgent);
matcher.find();// 因为我们的userAgent只有一组 ()
String group = matcher.group(0);// (Windows NT 10.0; Win64; x64; rv:97.0)
String group1 = matcher.group(1);// Windows NT 10.0; Win64; x64; rv:97.0
String[] operInfos = group1.split(";");
System.out.println("操作系统=" + operInfos[0]);//Windows NT 10.0
System.out.println("操作系统位数=" + operInfos[1].trim());// Win64
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
解法2:使用字符串分割
public class Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String user_agent = request.getHeader("User-Agent");
String browser = user_agent.split(";")[0].split("\\(")[1];
String version = user_agent.split(";")[2];
System.out.println("browser:"+browser+",version"+version);
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write("操作系统:"+browser+"<br/>");
writer.write("位数:"+version+"<br/>");
writer.flush();
writer.close();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
}
2. HttpServletResponse
2.1 HttpServletResponse 介绍
- 每次 HTTP 请求,Tomcat 会创建一个 HttpServletResponse 对象传递给 Servlet 程序去使用。
- HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,如果需要设置返回给客户端的信息,通过 HttpServletResponse 对象来进行设置即可
2.2 HttpServletResponse 类图
2.3 向客户端返回数据方法
- 字节流 getOutputStream(); 常用于下载(处理二进制数据)
- 字符流 getWriter(); 常用于回传字符串
- (细节:)两个流同时只能使用一个。 使用了字节流,就不能再使用字符流,反之亦然,否则就会报错
2.4 向客户端返回数据应用实例
- 需求:浏览器请求 , 返回 hello, world
- 代 码 实 现 :
创 建ResponseServlet_.java
public class ResponseServlet_ extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {
/* 解读:
1. setContentType 会设置服务器和客户端都用 utf-8 字符集,还设置了响应头
2. setContentType 要在获取流对象(getWriter)之前调用才有效
*/
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println("<h1>hello, world~</h1>");
writer.println("<h1>hi, boss</h1>");
writer.flush();
writer.close();
}
}
- 在 web.xml 中完成配置.
- 完成测试
2.5 向客户端返回数据注意事项和细节
- 处理中文乱码问题-方案 1
- 处理中文乱码问题-方案 2
2.6 请求重定向
2.6.1 请求重定向介绍
- 请求重定向指:一个 web 资源收到客户端请求后,通知客户端去访问另外一个 web资源,这称之为请求重定向
- 请求重定向原理示意图
2.6.2 请求重定向应用实例
- 需 求 : 演 示 请 求 重 定 向 的 使 用 当 访 问 DownServlet 下 载 文 件 , 重 定 向 到DownServletNew 下载文件
- 创 建 DownServlet.java
public class DownServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// System.out.println("DownServlet 被调用");
// response.setContentType("application/x-tar;charset=utf-8");
// PrintWriter writer = response.getWriter();
// writer.print("hi");
// writer.flush();
// writer.close();
//完成了自己业务
//发出请求重定向-> DownServletNew
//听老师解读
//1. sendRedirect 本质就会 返回 302 状态码 Location: /servlet/downServletNew
//2. 因此 302和 /servlet/downServletNew 是浏览器解析,而不是服务器
//3. 浏览器在解析 /servlet/downServletNew => http://localhost:8080/servlet/downServletNew
response.sendRedirect("/servlet/downServletNew");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
- 创 建 DownServletNew.java
public class DownServletNew extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("DownServletNew 被调用...");
response.setContentType("application/x-tar;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("下载天龙八部..");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
-
在 web.xml 中完成 servlet 配置
-
完成测试
2.6.3 请求重定向注意事项和细节
- 最佳应用场景:网站迁移,比如原域名是 www.hsp.com 迁移到 www.hsp.cn ,但是百度抓取的还是原来网址.
- 浏览器地址会发生变化,本质是两次 http 请求.
- 不能共享 Request 域中的数据,本质是两次 http 请求,会生成两个 HttpServletRequest对象
- 不能重定向到 /WEB-INF 下的资源
- 可以重定向到 Web 工程以外的资源, 比如 到 www.baidu.com 【在前面的案例演示】
- 重定向有两种方式, 推荐使用第 1 种.
- 动态获取到 application context
String contextPath = getServletContext().getContextPath();
System.out.println("contextPath= " + contextPath);
response.sendRedirect(contextPath + "/downServletNew");
2.7 课后作业
1、看具体需求
- 编写一个 MyPayServlet , 能够接收到提交的数据
- 编写一个简单的支付页面 pay.html(如图)
- 如果支付金额大于 100, 则重定向到 payok.html, 否则重定向到原来的 pay.html
pay.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>支付页面</title>
</head>
<body>
<h1>支付页面</h1>
<!--注意:这里action="/servlet/myPayServlet"的第一/被浏览器解析从浏览器地址栏的主机-->
<form action="/servlet/myPayServlet" method="post">
用户编号: <input type="text" name="userid"><br/>
支付金额: <input type="text" name="money"><br/>
<input type="submit" value="点击支付">
</form>
</body>
</html>
payok.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>支付成功</title>
</head>
<body>
<h1>恭喜你,支付成功~</h1>
</body>
</html>
WebUtils.java
/**
* 编写一个转换string->int的方法,处理异常
*/
public class WebUtils {
public static int parseString(String str) {
int num = 0;
//try-catch : ctrl+alt+t
//如果不知道老师在说什么? 回去看到java基础 - 异常
try {
num = Integer.parseInt(str);
} catch (NumberFormatException e) {
//这个异常不会throw 给 tomcat
System.out.println("输入的str格式不正确...");
}
return num;
}
}
MyPayServlet.java
public class MyPayServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//System.out.println("MyPayServlet 被调用...");
//得到金额
String money = request.getParameter("money");
System.out.println("money= " + money.getClass());
//转成int
//处理一下这个异常
int iMoney = WebUtils.parseString(money);
ServletContext servletContext = getServletContext();
if(iMoney > 100) {
//重定向到payok.html
//servletContext.getContextPath() 就是 /servlet
response.sendRedirect(servletContext.getContextPath() + "/payok.html");
} else {
response.sendRedirect(servletContext.getContextPath() + "/pay.html");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}