Request
一 Request概述
-
用户通过浏览器访问服务器时,Tomcat将HTTP请求中所有的信息都封装在Request对象中
-
作用:开发人员可以通过request对象方法,来获取浏览器发送的所有信息.
Request体系结构
ServletRequest
|
HttpServletRequest
|
org.apache.catalina.connector.RequestFacade(由tomcat厂商提供实现类)
二 Request获取Http请求信息
2.1 获取请求行信息
* 例如:
GET /day09_request/requestDemo1 HTTP/1.1
请求方式 请求地址 协议/版本
* 相关API: (查阅文档: 是javaee文档)
1. 获取请求方式 GET【掌握】
String getMethod()
2. 获取项目虚拟路径(项目名)/day09_request【掌握】
String getContextPath()
3. 获取URI /day09_request/requestDemo1
统一资源标识符
(范围广,只要能唯一标识资源的: 本地路径,远程地址
url 是 uri 的子集
)
String getRequestURI()
4. 获取URL http://localhost:8080/day09_request/requestDemo1
统一资源定位符(确定某一个地址:俗称网址)
StringBuffer getRequestURL()
5. 获取协议和版本号 HTTP/1.1
String getProtocol()
6. 获取客户端ip
String getRemoteAddr()
@WebServlet("/requestDemo01")
public class RequestDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(request.getMethod());//请求方式
System.out.println(request.getContextPath());//项目虚拟路径
System.out.println(request.getRequestURI()); //获取请求地址
System.out.println(request.getRequestURL()); // 获取请求地址全称
System.out.println(request.getProtocol());//获取协议
//0:0:0:0:0:0:0:1 -> 本地回环地址的ipv6写法(localhost)
// 127.0.0.1 -> ipv4写法
System.out.println(request.getRemoteAddr()); //获取客户端ip
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
2.2 获取请求头信息
* 例如:
Host: localhost:8080(主机: 指的就是tomcat访问地址)
name(请求头属性名,固定不变的) : value(对应的属性值,动态改变)
常见请求头:
user-agent (用户的系统版本和浏览器)
referer(上一次地址)
如果在浏览器中看不到某个头,就表示这个头所对应的值是null
* 相关API:
1. 获取知道请求头名称对应的值,注:名称不区分大小写
String getHeader(String name)
2. 获取所有请求头的名称
Enumeration<String> getHeaderNames()
注:是Iterator迭代器前身(枚举)
/*
* * 相关API:
1. 获取知道请求头名称对应的值,注:名称不区分大小写
String getHeader(String name)
2. 获取所有请求头的名称
Enumeration<String> getHeaderNames()
注:是Iterator迭代器前身(枚举 enum)
* */
@WebServlet("/requestDemo02")
public class RequestDemo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
String host = request.getHeader("host");
System.out.println("主机地址:" + host); // localhost:8080(tomcat的访问地址)
String agent = request.getHeader("user-agent");
System.out.println("用户系统和浏览器:" + agent);
String referer = request.getHeader("referer");
//注意: 如果直接在浏览器中输入地址访问,就意味着没有上一次访问地址,返回null
//演示不是null的情况: 通过A地址访问本地址,A地址就是本地址的上一次访问地址
System.out.println("上一次访问地址:" + referer);
System.out.println("---------------------------------");
/*List<String> list = new ArrayList<>();
Iterator<String> it = list.iterator();
while(it.hasNext()){ // 判断有没更多的元素
String element = it.next(); // 获取下一个元素
System.out.println(element);
}*/
//获取所有请求头的名称
Enumeration<String> en = request.getHeaderNames();
while(en.hasMoreElements()){// 判断有没更多的元素
String name = en.nextElement();// 获取下一个元素 (请求头的名称)
String value = request.getHeader(name); // 请求头名称对应的值
System.out.println(name + ":" + value);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
2.3案例:视频防盗链
//案例:防盗链
@WebServlet("/requestDemo03")
public class RequestDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//播放喜羊羊的地址 http://localhost:8080/day09-request/requestDemo03
//1.获取上一次访问地址
String referer = req.getHeader("referer");
System.out.println(referer);
//2.判断是否来源于本站
//如果浏览器直接访问,referer是null
if(referer != null && referer.startsWith("http://localhost:8080")){
//是本站
// System.out.println("正常播放喜羊羊");
resp.setContentType("text/html;charset=utf-8");//防止响应体乱码
resp.getWriter().print("正常播放喜羊羊");//明天学习,网页上输出
}else{
//不是本站
// System.out.println("");
resp.setContentType("text/html;charset=utf-8");//防止响应体乱码
resp.getWriter().print("不好意思,无法查看,请到官方网站");//明天学习,网页上输出
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
2.3 获取请求参数(体)【重点…】
-
不论get还是post请求方式,都可以使用下列方法来获取请求参数
-
get请求参数放在 请求行 url中(get没有请求体)
-
post请求参数放在 请求体
-
在web课程里面,get和post处理的业务逻辑都是一样的…
* 参数
username=jack&password=123&hobby=drink&hobby=perm
格式: name1=value1&name2=value2... (name是可以重复)
* API
1. 获取指定参数名的值 username=jack
String getParameter(String name)
2. 获取指定参数名的值数组 hobby=drink&hobby=perm
String[] getParameterValues(String name)
3. 获取所有参数名和对应值数组,参数名 name(key),值数组 value,封装map集合
Map<String,String[]> getParameterMap()
name , value
* 中文乱码【重点】
get:在tomcat8及以上版本,内部URL编码(UTF-8)
post:编码解码不一致,造成乱码现象
客户端(浏览器)编码:UTF-8
服务器默认 解码:ISO-8859-1 拉丁文
指定解码:void setCharacterEncoding(String env)
注:这哥们必须在方法内,行首
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>get请求</h1>
<form action="/day09-request/requestDemo04" method="get">
用户 <input type="text" name="username" > <br>
密码 <input type="text" name="password" > <br>
性别
<input type="radio" name="gender" value="male"> 男
<input type="radio" name="gender" value="female"> 女 <br>
爱好
<input type="checkbox" name="hobby" value="smoke"> 抽烟
<input type="checkbox" name="hobby" value="drink"> 喝酒
<input type="checkbox" name="hobby" value="perm"> 烫头 <br>
<input type="submit">
</form>
<h1>post请求</h1>
<form action="/day09-request/requestDemo04" method="post">
用户 <input type="text" name="username" > <br>
密码 <input type="text" name="password" > <br>
性别
<input type="radio" name="gender" value="male"> 男
<input type="radio" name="gender" value="female"> 女 <br>
爱好
<input type="checkbox" name="hobby" value="smoke"> 抽烟
<input type="checkbox" name="hobby" value="drink"> 喝酒
<input type="checkbox" name="hobby" value="perm"> 烫头 <br>
<input type="submit">
</form>
</body>
</html>
/*
* * 参数
username=jack&password=123&hobby=drink&hobby=perm
格式: name1=value1&name2=value2... (name是可以重复)
* API
1. 获取指定参数名的值 username=jack
String getParameter(String name)
2. 获取指定参数名的值数组 hobby=drink&hobby=perm (爱好)
String[] getParameterValues(String name)
3. 获取所有参数名和对应值数组,参数名 name(key),值数组 value,封装map集合
Map<String,String[]> getParameterMap()
name , value
# post请求参数中文乱码问题
1. get请求乱码问题, tomcat8以及以上解决了
参数放在地址栏中(url), url编码(js的方法 encodeURI decodeURL)
2. post乱码, 参数放在请求体(不一定是中文, 可能提交文件)
浏览器提交参数: utf-8编码
tomcat默认: 使用的是 iso-8859-1解码(拉丁文,256个,欧码)
解决: 我们自己得手动指定 解码 的字符集 utf-8
//在post请求方式获取参数之前,指定解码字符集
request.setCharacterEncoding("utf-8");
* */
@WebServlet("/requestDemo04")
public class RequestDemo04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username"); // 一个name对应一个value
String password = req.getParameter("password");
String gender = req.getParameter("gender");
String[] hobbies = req.getParameterValues("hobby"); // name对应多个value情况
System.out.println(username + "," + password + "," + gender + "," + Arrays.toString(hobbies));
System.out.println("--------------------");
// 获取所有的参数
// 其中key是name, value是name对应的值
Map<String, String[]> parameterMap = req.getParameterMap();
Set<String> keySet = parameterMap.keySet();
for (String key : keySet) {
String[] value = parameterMap.get(key);
System.out.println(key + "=" + Arrays.toString(value));
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8"); //在post请求方式获取参数之前,指定解码字符集
//post请求, 交给doGet方法执行
this.doGet(req,resp);
}
}
2.4 BeanUtils
- 这哥们apache组织,提供一套工具类,简化参数的封装…
- 简单来说:将前端提交的数据,直接封装到你想要的JavaBean中
要用BeanUtils 表单项的name属性值 和 javaBean的属性名一致即可
① 导入jar包
② 使用工具类封装数据
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/day09-request/requestDemo05" method="get">
用户 <input type="text" name="username" > <br>
密码 <input type="text" name="password" > <br>
性别
<input type="radio" name="gender" value="male"> 男
<input type="radio" name="gender" value="female"> 女 <br>
爱好
<input type="checkbox" name="hobby" value="smoke"> 抽烟
<input type="checkbox" name="hobby" value="drink"> 喝酒
<input type="checkbox" name="hobby" value="perm"> 烫头 <br>
<input type="submit">
</form>
</body>
</html>
/*
* 需求: 把所有的请求参数都封装到javaBean
* 思考: 如果参数有好多, 手动封装是不是很麻烦
*
* 解决: BeanUtils 工具包 jar (apache)
* 1. 导包: web/WEB-INF下创建lib目录, jar包放里面(两个都要)
* 2. 使用api
* BeanUtils.populate(Object bean, Map param);
* a. bean: 一个空的javaBean
* b. param : 参数集合
* c. 效果: 将param中的所有参数填充到bean对象中
* */
@WebServlet("/requestDemo05")
public class RequestDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/* String username = req.getParameter("username"); // 一个name对应一个value
String password = req.getParameter("password");
String gender = req.getParameter("gender");
String[] hobbies = req.getParameterValues("hobby"); // name对应多个value情况
User user = new User();
user.setUsername(username);
user.setPassword(password);
user.setGender(gender);
user.setHobby(hobbies);
System.out.println(user);*/
//获取所有的请求参数
Map<String, String[]> parameterMap = req.getParameterMap();
User user = new User(); //空的
try {
//将parameterMap中的所有参数填充到user对象中
/*
* populate原理: 反射
* 0. parameterMap 长这样:
* username=[zs]
password=[123]
gender= [male]
hobby= [smoke, drink]
1. 遍历 parameterMap
一次遍历为例:
key = username
value = zs
2. 反射找方法 (伪代码: 内省)
//根据方法名和形参列表找到该类的某个方法
Method method = class对象.getMethod(方法名,形参列表Class对象)
// 调用此方法
method.invoke(调用方法的对象,参数)
//方法名根据map的key推导的
Method method = user.getClass().getMethod("setUsername",String.class);
//直接用map的value
method.invoke(user,value)
重点: 方法名根据map的key推导的!!!
而map的key 是由表单项的name属性值决定的
如果要用BeanUtils, 表单项的name属性值
必须和 javaBean的 setXXX的方法名(不包括set)一致!!!
软性规范: 属性名 和 setXXX 是一致的
-> 结论: 要用BeanUtils 表单项的name属性值 和 javaBean的属性名一致即可
* */
BeanUtils.populate(user, parameterMap);
System.out.println(user);
System.out.println("-----");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
this.doGet(req,resp);
}
}
javaBean其实就是一个标准类
package com.itheima03.param;
import java.util.Arrays;
/*
* javaBean : java标准类
* 1. public 空参
* 2. private 属性
* 3. public get set方法
* (三个必须规则,可以多不可少)
* */
public class User {
private String username;
private String password;
private String gender;
private String[] hobby;
//一个类不声明任何构造,默认有一个public空参(隐式声明)
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", gender='" + gender + '\'' +
", hobby=" + Arrays.toString(hobby) +
'}';
}
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;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
}
三 Request其他功能
3.1 请求转发
- 一种在服务器内部的资源跳转方式
* API
1. 通过request对象,获得转发器对象(邮递员)
//path是要跳转的资源路径
RequestDispatcher getRequestDispatcher(String path)
2. 通过转发器对象,实现转发功能
// 要记得携带请求和响应
void forward(ServletRequest request, ServletResponse response)
* 请求转发特点
1. 浏览器:发了一次请求
2. 地址栏:没有发生改变
3. 只能转发到服务器内部资源....
* 链式编程
request.getRequestDispatcher("/bServlet").forward(request,response)
就好像餐厅点菜,只能在这个餐厅点
3.2 域对象(共享数据)
-
域对象:一个有作用范围的对象,可以在范围内共享数据
-
web中有四大域对象 : jspContext, request , session, servletContext
-
共同点: 设置,获取和移除数据的api完全相同, 作用范围不一样
-
request域:代表一次请求的范围,一般用于一次请求中转发的多个资源中共享数据
服务员,厨师之间的交互
* API
1. 设置数据
void setAttribute(String name, Object o)
2. 获取数据
Object getAttribute(String name)
3. 删除数据
void removeAttribute(String name)
* 生命周期 : 一次请求到一次响应之间
1. 何时创建?
用户发送请求时,创建request
2. 何时销毁
服务器返回响应时,销毁request
3. 作用范围?
一次请求,可以包含多次转发
@WebServlet("/aServlet")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("接收到客户请求,客户要吃巨无霸汉堡");
System.out.println("单子转发给厨师");
//域对象 设置数据
req.setAttribute("extra","客户不吃香菜");
//请求转发
//1. 先获取转发器对象(邮递员,并告知要去哪里)
// RequestDispatcher dispatcher = req.getRequestDispatcher("/bServlet");
//2. 转发器对象,携带请求和响应转发
// dispatcher.forward(req,resp);
//简化(链式)
req.getRequestDispatcher("/bServlet").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
@WebServlet("/bServlet")
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("厨师拿到单子,开始做汉堡");
//域对象获取数据
String extra = (String) req.getAttribute("extra");
System.out.println("读取备注:" + extra);
resp.setContentType("text/html;charset=utf-8"); //解码中文乱码
resp.getWriter().print("汉堡好了,请吃~~");//网页上输出
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
四 案例:用户登录
需求
实现用户的登录功能
登录成功跳转到SuccessServlet展示:登录成功!xxx,欢迎您
登录失败跳转到FailServlet展示:登录失败,用户名或密码错误
代码实现
① 创建web项目
②编写index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--
post请求方式参数大小无限制,相对安全
1. 上传文件
2. 提交表单
-> 问题: 请求参数中文乱码
-->
<h1>登录页面</h1>
<form action="/day09-login/loginServlet" method="post">
用户<input type="text" name="username"> <br>
密码<input type="password" name="password"> <br> <br>
<input type="submit" value="登录">
</form>
</body>
</html>
User实体类
// 用户对象
public class User {
private String username;// 用户名
private String password;// 密码
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{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
LoginServlet
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//post请求中文乱码
request.setCharacterEncoding("utf-8");
//1. 获取请求参数
String username = request.getParameter("username");
String password = request.getParameter("password");
//2. 业务处理(判断用户名和密码是否存在)
//要登录,必先注册,注册的数据一般放在数据库中(还没学,先用伪数据)
// 伪数据, 只有一个用户已注册: 用户名jack, 密码123
if("jack".equalsIgnoreCase(username) && "123".equalsIgnoreCase(password)){
//a. 账号密码正确 -> 请求转发SuccessServlet
User user = new User();
user.setUsername(username);
user.setPassword(password);
request.setAttribute("user",user);//域对象设置数据
request.getRequestDispatcher("/successServlet").forward(request,response);
}else{
//b. 账号密码不正确 -> 请求转发FaliedServlet
request.getRequestDispatcher("/failedServlet").forward(request,response);
}
}
}
SuccessServlet
@WebServlet("/successServlet")
public class SuccessServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("登录成功");
//注意, 如果没有设置,获取是null
User user = (User) request.getAttribute("user");//域对象获取数据
response.setContentType("text/html;charset=utf-8");
response.getWriter().print("恭喜"+user.getUsername()+",登录成功");
}
}
FailServlet
@WebServlet("/failedServlet")
public class FailedServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("登录失败");
response.setContentType("text/html;charset=utf-8");
response.getWriter().print("用户名不存在或密码错误");
}
}