第二章 request&请求数据获取
1、什么是HttpServletRequest
HttpServletRequest接口的对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求中的所有信息都封装在HttpServletRequest接口的对象中,开发人员通过这个对象的方法,可以获得客户端信息。
2、HttpServletRequest的作用
通过HttpServletRequest接口的对象进行的常用操作:
1. 获取请求行信息:请求方式,url,ip地址和HTTP版本。
2. 获取请求头信息:浏览器类型,ip地址等。
3. 获取请求参数或者请求体中的数据:url后面拼接的参数或者请求体中提交的参数;
2.1 获取请求行信息
请求行主要包括:**请求方式** ,**url** 和**协议/版本** 。HttpServletRequest接口的对象提供了以下方法来获取这些信息:
方法 | 说明 |
---|---|
String getMethod() | 获取请求方式的类型 字母是大写的 |
StringBuffer getRequestURL() | 获取客户端发出请求完整URL |
String getRemoteAddr() | 获取IP地址 |
String getProtocol() | 获取当前协议的名称和版本 |
说明:对于上述方法StringBuffer getRequestURL() 的返回值是StringBuffer ,它也表示字符串缓冲区,用法和StringBuilder一样,只是StringBuffer 是多线程安全的,效率低。StringBuilder是多线程不安全的,效率高。
【案例】获取请求行信息
【java代码】
@WebServlet("/rowServlet")
public class RowServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//进行请求行API的学习
//获取请求行的请求方式
String method = request.getMethod();
System.out.println("method = " + method);
//获取请求行的请求URL
StringBuffer requestURL = request.getRequestURL();
System.out.println("requestURL = " + requestURL);
//获取请求行的请求IP
String remoteAddr = request.getRemoteAddr();
System.out.println("remoteAddr = " + remoteAddr);
//获取请求行的协议版本号
String protocol = request.getProtocol();
System.out.println("protocol = " + protocol);
}
}
【结果】
启动服务器,访问Servlet得到结果如下:
method = GET
requestURL = http://localhost:8080/rowServlet
remoteAddr = 0:0:0:0:0:0:0:1
protocol = HTTP/1.1
【注意事项】
-
如果浏览器地址栏中的地址为:http://localhost:8080/rowServlet ,获取的ip地址为:0:0:0:0:0:0:0:1。
0:0:0:0:0:0:0:1是ipv6的表现形式,对应ipv4来说相当于127.0.0.1,也就是本机。导致上述的原因是客户端和服务器在同一台电脑上。如果不想出现ipv6版本的ip地址格式,在访问网址的时候可以书写127.0.0.1。
或者在访问servlet的时候,输入具体的ip地址,假设我的电脑ip地址是:192.168.23.10
访问servlet的时候这样输入:http://192.168.23.10:8080/rowServlet
那么这里获取的ip地址:remoteAddr = 192.168.23.10
通过上述案例,我们可以发现,如果开发中服务器需要获取浏览器发送的行信息,使用对应的api 可以轻松获取!
2.2 获取请求头信息
浏览器的请求头信息是由很多:关键字:值
即key:value 形式的数据组成的。HttpServletRequest对象给我们提供了两个方法用来获取请求的头信息。
方法 | 作用 |
---|---|
String getHeader(String name) | 根据请求头的key关键字获取请求头信息 |
Enumeration getHeaderNames() | 返回此请求包含的所有头信息的key,属于对枚举类型进行遍历 |
说明: 枚举属于一种特殊的数据类型,类似于集合,对他遍历也是类似于集合遍历方式。Enumeration类似于Iterator的迭代器。只是Iterator来迭代1.2之后的集合。而Enumeration用来迭代1.2之前集合和枚举类型的。
【说明】请求头的key关键字如下表所示:
请求头key | 请求头value |
---|---|
referer | 浏览器通知服务器,当前请求来自何处,如果是直接访问,则不会有这个头。 |
If-modified-Since | 浏览器通知服务器,本地缓存的最后变更时间。与另一个响应头组合控制浏览器页面的缓存。 |
cookie | 与会话有关技术,用于存放浏览器缓存的cookie信息。 |
user‐agent | 浏览器通知服务器,客户端浏览器与操作系统相关信息 |
connection | 保持连接状态。Keep-Alive 连接中,close 已关闭 |
host | 请求的服务器主机名 |
content-length | 请求体的长度 |
content-type | 如果是POST请求,会有这个头,默认值为application/x-www-form-urlencoded,表示请求体内容使用url编码 |
accept | 浏览器可支持的MIME类型。文件类型的一种描述方式。 |
mime格式 | 浏览器请求数据的类型,例如: text/html ,html文件 text/css,css文件 text/javascript,js文件 image/*,所有图片文件 |
accept-encoding | 浏览器通知服务器,浏览器支持的数据压缩格式。如:GZIP压缩 |
accept-language | 浏览器通知服务器,浏览器支持的语言。各国语言(国际化i18n) |
.... | ..... |
【代码演示】:编写一个Servlet类来演示获取请求头信息:user-agent
@WebServlet("/headerServlet")
public class HeaderServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//请求头学习
//获取所有请求key信息
Enumeration<String> headerNames = request.getHeaderNames();
//取出每个key
while (headerNames.hasMoreElements()){
System.out.println(headerNames.nextElement());
}
//获取user-agent 对应value值
String header = request.getHeader("user-agent");
System.out.println("header = " + header);
}
}
【运行结果】
host
connection
upgrade-insecure-requests
user-agent
accept
accept-encoding
accept-language
cookie
header = Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
2.3 获取请求体(请求数据)【重要】
学习完了对请求行和请求头的内容,最后一部分就是请求体了,在请求体中,包含的是用户通过浏览器发送的请求参数,因此,我们主要学习的就是获取请求参数的方法。
首先,我们来观察一下浏览器中的请求数据是怎么提交的:
- get请求:get请求提交的数据是拼接在url后面的,如下图所示:
2.post请求:post请求的数据是在请求体中发送到后台的:
【请求体】 username: lisi password: 1234
通过对比,发现数据传递的格式都是:name=value
;
(1)获取请求参数的方法
方法名 | 描述 |
---|---|
String getParameter(String name) | getParameter获得指定参数名对应的值。如果没有返回null,如果值有多个获得第一个。 例如:username=jack。注意:参数name是form表单中的name属性值。 |
String[] getParameterValues(name) | getParameterValues[] 获取请求数据key相同的多个数据.一般获取的是复选框。例如爱好选项。 |
Map<String,String[]> request.getParameterMap(); | 获得表单中所有的数据,key表示input标签name的属性值:,value是一个数组,表示input输入框的值 |
(2)案例:获取请求参数练习
1、编写用户表单user.html,提供表单字段:username、password、hobby、education,以post方式提交。
<form action="/requestServlet" method="post">
用户名:<input type="text" name="username"/><br/>
密码: <input type="text" name="password"/><br/>
爱好: <input type="checkbox" name="hobby" value="basketball"/> 篮球
<input type="checkbox" name="hobby" value="football"/> 足球
<input type="checkbox" name="hobby" value="film"/> 电影 <br/>
学历:<select name="education">
<option value="gz">高中</option>
<option value="dz">大专</option>
<option value="bk">本科</option>
</select><br>
<input type="submit" value="post提交"/>
</form>
2、编写Servlet接受用户表单发送的数据.
@WebServlet("/requestServlet")
public class RequestServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//非常重要API 请求参数/请求体API
//获取用户名
String username = request.getParameter("username");
System.out.println("username = " + username);
String password = request.getParameter("password");
System.out.println("password = " + password);
//请求爱好
String[] hobbies = request.getParameterValues("hobby");
String s = Arrays.toString(hobbies);
System.out.println("s = " + s);
//获取学历
String education = request.getParameter("education");
System.out.println("education = " + education);
//获取表单所有信息
Map<String, String[]> parameterMap = request.getParameterMap();
Set<String> strings1 = parameterMap.keySet();
System.out.println("strings1 = " + strings1);
//获取用户输入所有值
Collection<String[]> values = parameterMap.values();
for (String[] strings : values) {
System.out.println(strings.length);
System.out.println("strings = " + Arrays.toString(strings));
}
}
}
说明:
对于 Map<String, String[]> parameterMap = request.getParameterMap(); 的返回值是一个Map集合,用来获取用户输入的所有值。
Map集合的key:表单的name属性值。
Map集合的value:以String类型的数组形式存储表单提交的每一项的值。
【运行结果】
(3) BeanUtils
BeanUtils 是 Apache commons组件的成员之一,主要用于简化JavaBean封装数据的操作。它可以给JavaBean封装一个字符串数据,也可以将一个表单提交的所有数据封装到JavaBean中。
要用BeanUtils 表单项的name属性值 和 javaBean的属性名一致即可
① 导入jar包
② 使用工具类封装数据
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/request/ParamServlet" 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>
@WebServlet("/ParamServlet")
public class ParamServlet2 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 {
request.setCharacterEncoding("utf-8");
String username = request.getParameter("username"); // 一个name对应一个value
String password = request.getParameter("password");
String gender = request.getParameter("gender");
String[] hobbies = request.getParameterValues("hobby"); // 一个name对应多个value
//封装到数据到对象中(JavaBean)
/*User user = new User();
user.setUsername(username);
user.setPassword(password);
user.setGender(gender);
user.setHobby(hobbies);
System.out.println(user);*/
/*
* 问题: 只有4个属性,封装比较简单, 如果有40个,还那么些很冗余
*
* 解决: BeanUtils (apache出品工具包)
* 大大简化封装javaBean数据的代码
*
* 使用: 1. BeanUtils.jar 放入 web-inf/lib/xx.jar
* (两个jar包)
* 2. 编写代码
* BeanUtils.populate(bean,map)
* 将map中数据的映射到bean中
*
* 方法底层原理: 反射(内省)
* BeanUtils.populate(user,map);
* 1. 遍历这个map
* key value
* 举例: username 李四
*
* 2. 方法反射
* Class clazz = user.getClass(); // User.class 对象
* Method 指定的方法 = clazz.getMethod(方法名,方法参数列表对应Class对象);
* method.invoke(调用此方法的对象,实参);
*
* // setUsername 根据map中遍历出来的key 推导来的
* Method method = clazz.getMethod("setUsername",String.class); //获取User类里的setUsername方法对象
* //李四 就是 map中遍历出来的key对应的value
* method.invoke(user,"李四") // user.setUserName("李四")
* 总结:
* javaBean 的 setXXX 的 xxx 跟Map的key一致 !!!
* 1. map中的key跟表单项的name属性值 一致
* 2. setXXX 习惯于 本类的 属性名一致
*
* 最终结论: 表单的name 跟 javaBean的属性名一致即可
*
* */
User user = new User(); //空的对象
Map<String, String[]> map = request.getParameterMap();//获取所有请求参数
try {
BeanUtils.populate(user,map);
System.out.println(user);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
javaBean
/*
* JavaBean: java标准类 (三个必须规范)
* 1. public 空参构造
* 2. private 属性
* 2. 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) {
System.out.println("setUsername被调用了");
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作用域
1、request的生命周期
request对象生命周期如下图所示:
【一次请求和响应的完整流程】
1、浏览器向servlet发送请求
2、tomcat收到请求后,创建Request和Response两个对象,并将请求数据封装到request对象中,然后传递给Servlet
3、Servlet接收到请求后,调用doget或者dopost方法。处理浏览器的请求信息,然后通过Response返回tomcat服务器信息
4、tomcat接收到返回的信息,返回给浏览器。
5、浏览器接收到返回消息后,tomcat销毁Request和Response两个对象,同时销毁这两个对象所获得的信息。
- 创建:浏览器给服务器发送请求后,tomcat创建request对象封装请求数据;
- 销毁:服务器给浏览器响应信息结束后销毁;
特点:浏览器每次给服务器发送请求,服务器都会为这次请求创建一个新的request对象。
2 、request域对象(重要)
问:什么是域对象?
在web阶段需要对象来存储数据,获取数据和移除数据,就可以使用域对象来实现。域对象是一个容器,这种容器主要用于servlet与servlet之间的数据传输使用的 .
今天讲解的request域对象,就可以在一次请求中的多个servlet之间进行数据共享.
request域对象,一个存储数据的区域对象.是把request当成一个容器来存储数据,request域存储数据主要是用于在两个servlet之间传递数据。request作为域对象,常用的方法如下:
方法 | 说明 |
---|---|
void setAttribute(String name, Object o) | 往request域中设置值 |
Object getAttribute(String name) | 从request域中根据name取值,这里的name和setAttribute()方法中的第一个参数name一致 |
void removeAttribute(String name) | 从request域中移除值,这里的name和setAttribute()方法中的第一个参数name一致 |
以上三个方法都是操作request中域对象中的数据,与请求参数无关。
【案例】request域对象练习
需求:
1. 创建一个ScopeServlet类继承HttpServlet类;
2. 往request域中设置值;
3. 从request域中取值;
4. 将request域中的值移除;
@WebServlet("/scopeServlet")
public class ScopeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用request域对象,进行存储值,删除值,获取值操作
Student student = new Student("xiaozhou","1234");
//向request存储值
request.setAttribute("msg","柳岩");
request.setAttribute("stu",student);
//可以获取值
String msg = (String) request.getAttribute("msg");
System.out.println("msg = " + msg);
//移除request中值
request.removeAttribute("msg");
Object msg1 = request.getAttribute("msg");
if (null== msg1){
System.out.println("没有获取到值");
}
Student student1 = (Student) request.getAttribute("stu");
System.out.println("student1 = " + student1);
}
}
实体类代码
package cn.itha.value05;
public class Student {
private String name;
private String password;
public Student(String name, String password) {
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + ''' +
", password='" + password + ''' +
'}';
}
}
在浏览器输入地址:http://localhost:8080/scopeServlet
效果图:
【注意】getParameter()方法和getAttribute()方法的区别
- getParameter()方法获取的是浏览器提交的数据(是表单提交的数据);
- getAttribute()方法获取的是request域中的数据(即获取的都是通过request.setAttribute()设置的值);
3、 请求转发(重要)
我们上面学习了域对象的一种request,主要是负责向request域对象中存储数据,然后以key作为键将数据取出。这种使用存储和获取数据的方式在开发中最为常见的就是请求转发。
请求转发:可以从某个servlet到某个html等静态资源,还可以去其他的servlet中。我们之前使用到的请求都是从页面发出,然后请求到Servlet。其实,在Servlet中。请求也可以从一个Servlet发起,然后请求到另一个Servlet或静态页面。这项技术叫做请求转发。
请求转发的描述可以参考如下图:
请求转发需要借助以下两个方法实现:
方法 | 说明 |
---|---|
RequestDispatcher getRequestDispatcher(String path) | 获取请求转发器(该方法需要使用request对象调用)。参数:path指定指向资源的路径名的 String 。如果该路径名是相对的,那么它必须是相对于当前 servlet 的。 |
void forward(ServletRequest request, ServletResponse response) | 将请求转发到另一个资源(Servlet)上。参数:request表示客户端对 servlet 发出的请求的 ServletRequest 对象 response 表示 servlet 向客户端返回的响应的 ServletResponse 对象。 |
请求转发练习
要求:
- 从一个Servlet转发到一个静态页面;
- 从一个Servlet转发到另一个Servlet;
【练习一】从一个Servlet转发到dispatcher.html
@WebServlet("/oneServlet")
public class OneServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("oneServlet...");
//转发到静态资源
request.getRequestDispatcher("/dispatcher.html").forward(request,response);
}
}
dispatcher.html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>转发到html静态资源</h1>
</body>
</html>
效果图:
回车
注意:从OneServlet转发到dispatcher.html页面之后,浏览器地址栏并没有发生变化。
【练习二】从一个TwoServlet转发到ThrServlet;
【TwoServlet】
@WebServlet("/twoServlet")
public class TwoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用转发 去ThrServelt
//System.out.println("twoServlet...");
//引入request域对象,向request存储值
request.setAttribute("flag","help me");
request.getRequestDispatcher("/thrServlet").forward(request,response);
}
}
【ThrServlet】
@WebServlet("/thrServlet")
public class ThrServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//System.out.println("thrServlet...");
//获取request域对象中值
String flag = (String) request.getAttribute("flag");
System.out.println("flag = " + flag);
}
}
效果图:
回车后,TwoServlet会转发到ThrServlet,我们可以从ThrServlet中获取域对象中的数据,查看控制台:
注意:从TwoServlet转发到ThrServlet之后,浏览器地址栏并没有发生变化。
【小结】转发
- 转发是服务器内部的跳转行为;
- 从一个Servlet转发到另一个资源(静态或动态),能够实现跳转,但是浏览器地址栏地址没有发生改变。因为对浏览器来说本质上就只有一次请求;
- 请求转发的时候,共享request域中的数据;
- 无论转发多少次,都是只有一次请求,同时会有一次响应;