16. Servlet入门 - request介绍以及使用
request概述
什么是request
在Servlet API中,定义了一个HttpServletRequest接口,它继承自ServletRequest接口,专门用来封装HTTP请求消息。由于HTTP请求消息分为请求行、请求头和请求体三部分,因此,在HttpServletRequest接口中定义了获取请求行、请求头和请求消息体的相关方法.
Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。
request作用
- 操作请求三部分(行,头,体)
- 请求转发
- 作为域对象存数据
获取客户端以及请求头信息(操作请求行和请求头)
上面我们已经知道了 request 的基本作用,下面我们来使用一下。
获取客户机信息(操作请求行)
请求方式 请求路径(URI) 协议版本
代码语言:javascript复制GET /requestDemo/register.htm?username=zs&password=123456 HTTP/1.1
- getMethod();获取请求方式
- getRemoteAddr() ;获取客户机的IP地址(知道是谁请求的)
- getContextPath(); 获得当前应用工程名(部署的路径);
- getRequestURI();获得请求地址,不带主机名
- getRequestURL();获得请求地址,带主机名
- getServerPort();获得服务端的端口
- getQueryString();获的请求参数(get请求的,URL的?后面的. eg:username=zs&password=123456)
1. 创建一个演示Servlet程序,如下:
image-20201228164510317
代码语言:javascript复制@WebServlet("/requestDemo")
public class RequestServletDemo 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("访问 RequestServletDemo ....");
}
}
启动 tomcat 测试服务如下:
image-20201228164610934
可以看到成功访问了,那么下面我们来逐个演示一下获取客户端信息的方法。
2. getMethod();获取请求方式
image-20201228165148365
代码语言:javascript复制//**getMethod()**;获取请求方式
String method = request.getMethod();
System.out.println("获取请求方式 method: " method);
3. getRemoteAddr() ;获取客户机的IP地址(知道是谁请求的)
image-20201228165349054
代码语言:javascript复制// **getRemoteAddr()** ;获取客户机的IP地址(知道是谁请求的)
String remoteAddr = request.getRemoteAddr();
System.out.println("获取客户机的IP地址: " remoteAddr);
4.getContextPath(); 获得当前应用工程名(部署的路径);
image-20201230234000886
代码语言:javascript复制// **getContextPath()**; 获得当前应用工程名(部署的路径);
String contextPath = request.getContextPath();
System.out.println("获得当前应用工程名(部署的路径): " contextPath);
5.getRequestURI();获得请求地址,不带主机名
image-20201230234255331
代码语言:javascript复制// **getRequestURI();获得请求地址,不带主机名**
String requestURI = request.getRequestURI();
System.out.println("获得请求地址,不带主机名: " requestURI);
6.getRequestURL();获得请求地址,带主机名
image-20201230234438317
代码语言:javascript复制// **getRequestURL();获得请求地址,带主机名**
StringBuffer requestURL = request.getRequestURL();
System.out.println("获得请求地址,带主机名: " requestURL);
7. getServerPort();获得服务端的端口
image-20201230234808764
代码语言:javascript复制// getServerPort();获得服务端的端口
int serverPort = request.getServerPort();
System.out.println("获得服务端的端口: " serverPort);
8. getQueryString();获的请求参数(get请求的,URL的?后面的. eg:username=zs&password=123456)
image-20201230235128191
代码语言:javascript复制// getQueryString();获的请求参数(get请求的,URL的?后面的. eg:username=zs&password=123456)
String queryString = request.getQueryString();
System.out.println("获的请求参数: " queryString);
获得请求头信息(操作请求头)
请求头: 浏览器告诉服务器自己的属性,配置的, 以key value存在, 可能一个key对应多个value
img
使用 getHeader(String name); 可以获取以下信息
- User-Agent: 浏览器信息
- Referer:来自哪个网站(防盗链)
image-20201231085654381
代码语言:javascript复制//根据请求头的name获取value
//目标:获取name为user-agent的请求头的信息
// User-Agent: 浏览器信息
String header = request.getHeader("user-agent");
System.out.println("获取的请求头agent为:" header);
// Referer:来自哪个网站(防盗链)
String referer = request.getHeader("referer");
System.out.println("来自哪个网站(防盗链) Referer:" referer);
小结
- 操作请求行(获得客户机信息)
- getMethod(); 获得请求方式
- getRemoteAddr(); 获得客户机的IP地址
- getContextPath(); 获得项目的部署的路径
- getRequestURI(); 获得URI(不带http,主机,端口)
- getRequestURL(); 获得URL(带http,主机,端口)
- 操作请求头
- User-Agent: 浏览器信息
- Referer:来自哪个网站(防盗链)
- getHeader(String name);
获取请求参数以及乱码处理(操作请求体)
上面我们已经理解了通过 request 获取客户端 以及 请求头 的信息,下面我们来看看如何获取请求参数,以及对应的中文乱码问题处理。
request 获得请求参数的方法
方法名 | 描述 |
---|---|
String getParameter(String name) | 获得指定参数名对应的值。如果没有则返回null,如果有多个获得第一个。 例如:username=jack |
String[] getParameterValues(String name) | 获得指定参数名对应的所有的值。此方法专业为复选框提供的。 例如:hobby=抽烟&hobby=喝酒&hobby=敲代码 |
Map<String,String[]> getParameterMap() | 获得所有的请求参数。key为参数名,value为key对应的所有的值。 |
下面我们来用代码快速示例演示一下。
1.获取指定参数名对应的值 getParameter(String name)
1.1 首先我们创建一个 Servlet,如下:
image-20210107213514678
经过测试,我们已经知道了 Servlet 能够正常访问了。
1.2 使用 getParameter(String name) 来获取一个 username 的参数
image-20210107213833663
代码语言:javascript复制// 使用 getParameter(String name) 来获取一个 username 的参数
String username = request.getParameter("username");
System.out.println("获取到的username参数: " username);
那么这里带来一个疑问,如果是 post 请求呢?是否也可以获取参数呢?下面我们可以使用 postman 使用 form 表单的方式,快速测试一下,如下:
image-20210107214127912
我们使用 post 请求的方式,可以看到 Servlet 打印信息如下:
image-20210107214226773
2. 获得指定参数名对应的所有的值 getParameterValues(String name)。
获得指定参数名对应的所有的值。此方法专业为复选框提供的。 例如:hobby=抽烟&hobby=喝酒&hobby=敲代码
2.1 测试使用 GET 的 query 请求方式
image-20210107220002203
代码语言:javascript复制// 获得指定参数名对应的所有的值 getParameterValues(String name)。
String[] hobbies = request.getParameterValues("hobby");
for (String hobby : hobbies) {
System.out.println("获取的hobby:" hobby);
}
访问:http://localhost:8080/demo6?username=libai&hobby=basketball&hobby=football
可以看到正常获取到所有 hobby 参数。
2.2 测试用 POST 的 请求方式
image-20210107220150452
获取到参数如下:
image-20210107220249999
可以看到两种请求方式都可以获取参数。
3.获得所有的请求参数 getParameterMap()。key为参数名,value为key对应的所有的值。
image-20210107221251645
代码语言:javascript复制// 获得所有的请求参数 getParameterMap()
System.out.println("获得所有的请求参数 getParameterMap(): ");
Map<String, String[]> parameterMap = request.getParameterMap();
// 遍历所有参数 map
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
// 遍历参数的value
for (String value : entry.getValue()) {
System.out.println(entry.getKey() ": " value);
}
}
中文乱码问题处理
上面我们设置请求参数的时候,都是采用英文,并没有使用中文内容,那么如果使用中文的话,会出现什么结果呢?
1.中文乱码问题
1.1 在 GET 请求参数中,设置中文内容
image-20210107222052303
访问 http://localhost:8080/demo6?username=肥子白&hobby=basketball&hobby=football&hobby=无情兴趣
1.2 在 POST 请求参数中,设置中文内容
image-20210107222405433
image-20210107222442030
可以看到当我们使用 POST 请求参数为中文的时候,服务端接收的内容为乱码,这是为什么么?
2.中文乱码的产生原因(客户端与服务端两者的编码格式不一致)
我们在输入一些中文数据提交给服务器的时候,服务器解析显示出来的一堆无意义的字符,就是乱码。那么这个乱码是如何出现的呢?如下图所示:
image-20191209100446323
基本的原因其实就是 客户端 与 服务端 两者的编码格式不一致,那么我们只要将两边都设置为 utf-8
编码格式即可。
而在上面的示例中,我们发现GET请求中文乱码不用处理,而 POST 请求需要处理,总结如下:
- get方式, 当 tomcat>=8.0, 乱码会由 tomcat 自动处理;而如果低于 8.0 ,也是需要自己设置一下编码格式。
- post方式, 目前的版本都需要自己设置编码格式。
所以,还是都自己设置一下编码格式就好了。
3.中文乱码的解决(设置服务端的解码格式为 utf-8)
image-20210107223051821
代码语言:javascript复制// 设置服务端为 utf-8 解码格式,解决中文乱码问题
request.setCharacterEncoding("UTF-8");
4.设置编码格式的快速输入模板
在 IDEA 中,如果每次都要去记住如何输入,也是一个比较麻烦的事情,那么我们可以设置一个快捷输入方式,如下:
image-20210107233153227
代码语言:javascript复制request.setCharacterEncoding("UTF-8");
image-20210107233232778
测试输入如下:
image-20210107233257945
获取请求参数使用 BeanUtils 封装好 POJO 对象中
现在我们已经可以使用request对象来获取请求参数,但是,如果参数过多,我们就需要将数据封装到对象。
以前封装数据的时候,实体类有多少个字段,我们就需要手动编码调用多少次setXXX方法,因此,我们需要BeanUtils来解决这个问题。
BeanUtils是Apache Commons组件的成员之一,主要用于简化JavaBean封装数据的操作。
使用步骤:
- 导入jar
- 使用BeanUtils.populate(user,map)
1.使用 Maven 导入 BeanUtils 的 jar 包依赖
1.1 访问 Maven 仓库,搜索 BeanUtils 的依赖
访问 https://mvnrepository.com 搜索 BeanUtils 如下:
image-20210107225030048
image-20210107225128037
image-20210107225235531
代码语言:javascript复制<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
1.2 在项目的 pom.xml 导入 BeanUtils 坐标依赖
image-20210107225418529
2.创建 POJO 包的 User 类
image-20210107225758825
代码语言:javascript复制package com.pojo;
import java.util.Arrays;
/**
* @author Aron.li
* @date 2021/1/7 22:56
*/
public class User {
private String username;
private String password;
private String gender;
private String[] hobby;
public User() {
}
public User(String username, String password, String gender, String[] hobby) {
this.username = username;
this.password = password;
this.gender = gender;
this.hobby = hobby;
}
@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;
}
}
3.使用 BeanUtils 将 与 User 类属性同名的 请求参数 进行自动批量赋值
image-20210107230727283
代码语言:javascript复制@WebServlet("/BeanUtilsDemo")
public class BeanUtilsDemo 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.setCharacterEncoding("UTF-8");
//1. 获取所有的请求参数
Map<String, String[]> parameterMap = request.getParameterMap();
//2. 将请求参数封装到User对象中
User user = new User();
//3. 使用BeanUtils框架自动将map中的数据封装到对象中
try {
BeanUtils.populate(user, parameterMap);
} catch (Exception e) {
e.printStackTrace();
}
// 4. 打印封装好的user对象
System.out.println(user);
}
}
4.使用 post 请求 Servlet,每个请求参数都与 User 类中的属性同名
image-20210107230847532
请求之后,Servlet 程序打印封装好的 user 对象如下:
image-20210107230927807
那么如果缺少 或者 请求的参数 与 User 类属性不同名称 呢?
image-20210107231023378
image-20210107231040429
可以发现,如果请求的参数不同命,那么 BeanUtils 就无法对应赋值了。
request 的请求转发
在上面我们基本清楚了 request 如何获取请求头、请求参数等功能了,那么下面继续介绍 request 的请求转发功能。
1. 请求转发的格式
代码语言:javascript复制request.getRequestDispatcher(url).forward(request, response);//转发
2.请求转发的作用
当我们需要将 request 对象同时交给两个或者多个 Servlet 程序处理的时候,那么就可以使用请求转发。
request 请求转发不会修改浏览器的路径,也不需要浏览器跳转至其他路径,由服务端执行跳转。
更加重要的是,request 请求转发还可以访问 WEB-INF 中受保护的资源。
3.设置 请求转发的 forward 快捷输入模板
我们可以看到输入一个请求转发要写好多内容,为了方便,我们可以自己设置一下快捷方式,如下:
File | Settings | Editor | Live Templates
image-20210107232036814
代码语言:javascript复制request.getRequestDispatcher(url).forward(request, response);
image-20210107232718248
image-20210107232752324
下面在代码输入 forward
尝试快捷输入,如下:
image-20210107232829032
image-20210107232846420
4.请求转发的效果是跳转: 跳转到其它页面,跳转到其它的servlet
4.1 首先创建一个 Servlet 程序 forwardDemo1
image-20210108075753836
代码语言:javascript复制@WebServlet("/ForwardDemo1")
public class ForwardDemo1 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 {
response.getWriter().print("this is ForwardDemo1....");
}
}
4.2 再创建一个 Servlet 程序 forwardDemo2
image-20210108075933510
代码语言:javascript复制@WebServlet("/ForwardDemo2")
public class ForwardDemo2 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 {
response.getWriter().print("this is ForwardDemo2....");
}
}
4.3 在 forwardDemo1 中设置请求转发至 forwardDemo2
image-20210108080421057
代码语言:javascript复制@WebServlet("/ForwardDemo1")
public class ForwardDemo1 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 {
response.getWriter().print("this is ForwardDemo1....");
System.out.println("访问到 ForwardDemo1...");
// 请求转发至 forwardDemo2
request.getRequestDispatcher("/ForwardDemo2").forward(request, response);
}
}
我们从执行的效果来看,请求的确到了 demo1 的 Servlet 程序中,并且浏览器又直接显示 demo2 返回的结果,没有进行二次请求。
5.请求转发的跳转,只能是跳转到本项目的资源,无法访问项目外的资源
5.1 创建一个项目的 index.html ,用于演示请求转发
image-20210108080827164
5.2 在forwardDemo2中,再次设置请求转发至 index.html
image-20210108081024136
代码语言:javascript复制// 请求转发项目中的 index.html
request.getRequestDispatcher("/index.html").forward(request, response);
5.3 接着上面的示例,首先请求 demo1,然后demo1 转发至 demo2,然后 demo2 转发至 index.html
image-20210108081159370
5.4 测试能否访问项目外的资源,例如:百度
image-20210108081337556
代码语言:javascript复制// 测试访问项目外的资源
request.getRequestDispatcher("https://www.baidu.com/").forward(request, response);
可以看到请求失败了,无法请求转发无法访问项目外的资源。
6. 请求转发到WEB-INF中的资源
6.1 将上面写的 index.html 移动到 WEB-INF 中
image-20210108081547303
一般来说,WEB-INF的资源是受保护的,在浏览器无法访问。但是使用请求转发的话,就可以访问到了。
6.2 使用请求转发,访问 WEB-INF 中的 index.html
image-20210108081813515
代码语言:javascript复制// 请求转发至 WEB-INF 中的 index.html
request.getRequestDispatcher("/WEB-INF/index.html").forward(request, response);
request 作为域对象存取值
在上面我们已经理解了 request 请求转发的功能,而伴随着 请求转发的 当然就是可以携带 request 对象的存储值了。
当然 Servlet 的域对象有四种,而 request 只是其中的一种。
ServletContext: 范围 整个应用(无论多少次请求,只要是这个应用里面的都是可以共享的)
request范围: 一次请求有效 (所以要结合请求转发使用)
域对象是一个容器,这种容器主要用于Servlet与Servlet/JSP之间的数据传输使用的。
- Object getAttribute(String name) ;
- void setAttribute(String name,Object object) ;
- void removeAttribute(String name) ;
下面编写两个Servlet,进行 request 传值看看。
1.接着在上面的 forwardDemo1 中存储一个 name 值在 request 域对象中
image-20210108084315439
代码语言:javascript复制@WebServlet("/ForwardDemo1")
public class ForwardDemo1 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 {
response.getWriter().print("this is ForwardDemo1....");
System.out.println("访问到 ForwardDemo1...");
// 目标: 在ForwardDemo2中获取ForwardDemo1中的变量username的值
// 要求只能是由ForwardDemo1跳转到ForwardDemo2的时候才能获取
String username = "周杰棍";
//将username存储到request域对象中
request.setAttribute("name",username);
// 请求转发至 forwardDemo2
request.getRequestDispatcher("/ForwardDemo2").forward(request, response);
}
}
2.在 forwardDemo2 中取 request 的 name 值
image-20210108084406991
代码语言:javascript复制@WebServlet("/ForwardDemo2")
public class ForwardDemo2 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 {
response.getWriter().print("this is ForwardDemo2....");
System.out.println("访问到 ForwardDemo2....");
// 获取request中的值
Object name = request.getAttribute("name");
System.out.println("从 ForwardDemo1 传递过来的 name: " name);
}
}
3.测试请求
image-20210108084500341
可以看到,通过上面的示例,成功将 demo1 存储的值 转发到 demo2 上了。
4.小结
- request 作为域对象存取数据
- Object getAttribute(String name) ; 取
- void setAttribute(String name,Object object) ; 存
- void removeAttribute(String name) ; 移除
- 范围: 一次请求有效(转发可以使用)
image-20191209110018952
Request部分的小结
- 获取请求行的信息
- 获取请求方式:getMethod()
- 获取请求的客户端的ip地址:getRemoteAddr()
- 获取项目部署的路径:getContextPath()
- 获取uri:统一资源标识符
- 获取url:统一资源定位符
- 获取服务器的端口号
- 获取请求参数的字符串:getQueryString()
- 获取请求头的信息:getHeader(name)
- 获取请求参数
- getParameter(name)
- getParameterValues(name)
- getParameterMap()
- 使用BeanUtils将map中的数据存储到JavaBean对象中
- map的key要和JavaBean的属性名保持一致,如果不一致那么该字段的值就无法存储
- BeanUtils中默认内置一些基本类型的转换器(如果map中的数据是string类型,JavaBean的属性还是int类型那么会自动转换)
- 使用request做请求转发: request.getRequestDispatcher("跳转路径").forward(request,response);
- request对象作为域对象向存取数据,它的作用范围是一次请求中,和请求转发一起使用