day06 Servlet组件(下)
1. Request概述
1.1 Request的概念
在Servlet API中,定义了一个HttpServletRequest接口,它继承自ServletRequest接口,专门用来封装HTTP请求消息。由于HTTP请求消息分为请求行、请求头和请求体三部分,因此,在HttpServletRequest接口中定义了获取请求行、请求头和请求消息体的相关方法.
用我们自己的话来理解: Request就是服务器中的一个对象,该对象中封装了HTTP请求的请求行、请求头和请求体的内容
1.2 Request的组成
- 请求行: 包含请求方式、请求的url地址、所使用的HTTP协议的版本
- 请求头: 一个个的键值对,每一个键值对都表示一种含义,用于客户端传递相关的信息给服务器
- 请求体: POST请求有请求体,里面携带的是POST请求的参数,而GET请求没有请求体
1.3 Request的作用
-
获取HTTP请求三部分内容(行,头,体)
-
进行请求转发跳转
-
作为请求域对象进行存取数据
2 Request获取HTTP请求的内容
2.1 获取请求头的API
- getMethod();获取请求方式
- getContextPath(); 获得当前应用上下文路径
- getRequestURI();获得请求地址,不带主机名
- getRequestURL();获得请求地址,带主机名
public class ServletDemo01 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用request对象获取请求行的信息:
//1. 获取请求的方式(可能会用)
String method = request.getMethod();
//System.out.println("请求方式为:" + method);;
//2. 获取请求的url: 统一资源定位符 http://localhost:8080/requestDemo/demo01
String url = request.getRequestURL().toString();
//System.out.println("此次请求的url是:" + url);
//3. 获取请求的uri: 统一资源标识符,在url的基础上省略了服务器路径"http://loaclhost:8080"
String uri = request.getRequestURI();
System.out.println(uri);
}
}
2.2 获取请求头的API
- getHeader(String name), 根据请求头的name获取请求头的值
public class ServletDemo02 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//根据请求头的name获取value
//目标:获取name为user-agent的请求头的信息
//user-agent请求头中包含的是客户端浏览器信息
String header = request.getHeader("user-agent");
System.out.println("获取的请求头agent为:" + header);
}
}
2.3 获取请求参数
2.3.1 请求参数的概念
请求参数是客户端携带给服务器的由键值对组成的数据,例如"username=aobama&password=123456"这种类型的数据
2.3.2 客户端携带请求参数的形式
-
URL地址后面附着的请求参数,例如
http://localhost:8080/app/hellServlet?username=汤姆
-
表单携带请求参数
-
Ajax请求携带请求参数(后续会学习)
2.3.3 获取请求参数的API
方法名 | 返回值类型 | 方法描述 |
---|---|---|
request.getParameterMap() | Map<String, String[]> | 获取当前请求的所有参数,以键值对的方式存储到Map中 |
request.getParameter(“请求参数的名字”) | String | 根据一个参数名获取一个参数值 |
request.getParameterValues(“请求参数的名字”) | String [] | 根据一个参数名获取多个参数值 |
request.getParameterNames() | Enumeration | 获取当前请求的所有参数的参数名 |
2.3.4 实例代码
HTML代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>发送请求携带请求参数</title>
</head>
<body>
<a href="/webday0601/demo02?username=aobama&password=123456">访问ServletDemo02</a><br/>
<form action="/webday0601/demo02" 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代码
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
/**
*
* 日期2021-06-08 09:09
* HttpServletRequest的作用三:
* 获取请求参数的信息:1. url地址后边通过"?"拼接的内容 2. 表单使用POST请求在请求体中提交请求参数
* 方式:
* 1. getParameter(name):根据一个参数名获取一个参数值
* 2. getParameterValues(name):根据一个参数名获取多个参数值
* 3. getParameterMap():获取这次请求中的所有请求参数,并且封装到Map中
*/
public class ServletDemo02 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. getParameter(name):根据一个参数名获取一个参数值
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("获取到的请求参数username=" + username + ",password=" + password);
//2. getParameterValues(name):根据一个参数名获取多个参数值
String[] hobbies = request.getParameterValues("hobby");
System.out.println(Arrays.toString(hobbies));
//3. 获取所有的请求参数,并且将那些请求参数封装到一个Map集合中,那么请求参数的参数名就是map的key,请求参数的参数值就是map的value
//该Map的key是String类型就对应请求参数的参数名,该Map的value是String[]类型就对应请求参数的参数值
Map<String, String[]> parameterMap = request.getParameterMap();
//遍历打印Map中的数据
parameterMap.forEach((k,v) -> {
System.out.println(k + ":" + Arrays.toString(v));
} );
}
}
2.4 解决获取请求参数乱码
2.4.1 为什么会发生请求参数乱码
因为客户端发送给请求参数给服务器的时候需要进行编码,将字符串编码成二进制才能够在网络中传输,而服务器在接收到二进制之后需要进行解码才能够获取真正的请求参数;在这个过程中如果保证客户端编码使用的字符集和服务器解码使用的字符集相同的话,基本上(只要采用正确的够用的字符集)就不会发生乱码了;而发生乱码的原因是因为使用了错误的字符集,或者是客户端与服务器端所采用的字符集不一致。
2.4.2 怎么解决请求参数乱码
我们当前使用的Tomcat的版本是Tomcat8以上,所以我们不需要考虑GET方式乱码的问题,因为Tomcat8及以上版本已经在配置中解决了GET请求乱码的问题。我们只需要解决POST请求乱码问题
解决POST请求的参数乱码只需要在获取请求参数前调用request.setCharacterEncoding("UTF-8")
就行了
2.5 请求转发
2.5.1 什么是请求转发
请求转发是从一个资源跳转到另一个资源,在这个过程中客户端不会发起新的请求
2.5.2 请求转发的入门案例
① 案例目标
从ServletDemo03使用请求转发的方式跳转到ServletDemo04
② 请求转发的API
request.getRequestDispatcher("路径").forward(request,response);
③ 案例代码
ServletDemo03的代码
public class ServletDemo03 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ServletDemo03执行了...")
//请求转发跳转到ServletDemo04
request.getRequestDispatcher("/demo04").forward(request, response);
}
}
ServletDemo04的代码
public class ServletDemo04 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ServletDemo04执行了...")
}
}
2.5.3 请求转发的特征
- 请求转发的跳转是由服务器发起的,在这个过程中浏览器只会发起一次请求
- 请求转发只能跳转到本项目的资源,但是可以跳转到WEB-INF中的资源
- 请求转发不会改变地址栏的地址
2.5 请求域对象
2.5.1 请求域范围
我们之前学过全局域的范围,全局域是整个项目范围的所有动态资源都能够共享的一个范围;而请求域的范围只是在一次请求中的动态资源能够共享的一个范围
2.5.2 请求域对象的API
- 往请求域中存入数据:
request.setAttribute(key,value)
- 从请求域中取出数据:
request.getAttribute(key)
2.5.3 请求域对象案例
① 案例目标
在ServletDemo03中往请求域中存入"username"作为key,"aobama"作为值的键值对;然后在ServletDemo04中从请求域中根据"username"取出对应的值
② 使用请求域的前提
请求域对象一定要和请求转发一起使用,因为请求域的范围是一次请求范围内,所以要在两个动态资源中使用请求域必须要进行请求转发跳转
③ 案例代码
ServletDemo03的代码
public class ServletDemo03 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("ServletDemo03执行了...")
String username = "aobama";
//将username存储到request域对象中
request.setAttribute("username",username);
//请求转发跳转到ServletDemo04
request.getRequestDispatcher("/demo04").forward(request, response);
}
}
ServletDemo04的代码
public class ServletDemo04 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = (String)request.getAttribute("username");
System.out.println("在ServletDemo04中获取username:"+username)
}
}
2.6 JavaBean
2.6.1 什么是JavaBean
JavaBean是使用Java语言编写的可重用组件,在我们的项目中JavaBean主要用于存储内存中的数据,以及提供方法便于使用者获取数据
2.6.2 JavaBean的编写要求
- 类必须是公有的
- 必须有无参构造函数(非常重要)
- 属性私有,使用private修饰
- 针对所有的私有属性,提供对应的set和get方法
- 建议重写toString()方法,便于打印对象
- 基本类型简写使用包装类型
2.6.3 JavaBean的示例
package com.atguigu.pojo;
public class User {
private String username;
private String password;
private Integer age;
public User() {
}
public User(String username, String password, Integer age) {
this.username = username;
this.password = password;
this.age = age;
}
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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}
2.7 使用BeanUtils将Map中的数据封装到JavaBean对象中
2.7.1 使用JavaBean存储数据和使用Map存储数据的优缺点对比
① 使用Map存储数据的优缺点
优点:
- 灵活性强于javabean,易扩展,耦合度低。
- 写起来简单,代码量少。
缺点:
- javabean在数据输入编译期就会对一些数据类型进行校验,如果出错会直接提示。而map的数据类型则需要到代码运行阶段,才会进行处理判断。
- map的参数名称如果写错,也是需要到代码运行阶段,才能判断出是不是字段写错,不利于调试等。相对而言javabean会在编译期间发现错误
- map的参数值如果多传、乱传,也是需要到代码运行阶段,才能判断出是不是字段写错,不利于调试等。相对而言javabean会在编译期间发现错误
- 仅仅看方法签名,你不清楚Map中所拥有的参数个数、类型、每个参数代表的含义。 后期人员去维护,例如需要加一个参数等,如果项目层次较多,就需要把每一层的代码都了解清楚才能知道传递了哪些参数
② 使用JavaBean存储数据的优缺点
优点:
- 面向对象的良好诠释、
- 数据结构清晰,便于团队开发 & 后期维护
- 代码足够健壮,可以排除掉编译期错误
- Map存储数据的缺点都是JavaBean存储数据的优点
缺点:
代码量增多,需要花时间去封装JavaBean类
2.7.2 我们存储数据时候的选择
通常情况下,我们会选择使用JavaBean来存储内存中的数据,除非是非常简单的数据没有必要多编写一个JavaBean类的时候才会选择使用Map进行存储
2.7.3 BeanUtils的使用
① 作用
将Map中的数据填充到JavaBean对象中
② API方法介绍
BeanUtils.populate(map对象,JavaBean.class);
③ 使用步骤
- 导入对应的jar包
- 调用BeanUtils类的populate方法,传入对应的参数就可以了
代码
package com.atguigu.servlet;
import com.atguigu.pojo.User;
import org.apache.commons.beanutils.BeanUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
String age = req.getParameter("age");
int inAge = Integer.parseInt(age);
User user = new User(username, password, inAge);
System.out.println("user = " + user); // user = User{username='alex', password='123', age=19}
resp.getWriter().println("Register get Success");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
// 1.将所有数据封装到map中
Map<String, String[]> parameterMap = req.getParameterMap();
// 2.创建对象
User user = new User();
System.out.println("封装前 = " + user); // 封装前 = User{username='null', password='null', age=null}
// 3.使用BeanUtils完成 将map数据赋值给对象的属性
try {
BeanUtils.populate(user, parameterMap);
System.out.println("封装后 = " + user); // 封装后 = User{username='王安石', password='123456', age=19}
} catch (Exception e) {
e.printStackTrace();
}
resp.getWriter().println("Register Post Success");
}
}
3. Response的概述
3.1 Response的概念
在Servlet API中,定义了一个HttpServletResponse接口(doGet,doPost方法的参数),它继承自ServletResponse接口,专门用来封装HTTP响应消息。由于HTTP响应消息分为响应行、响应头、响应体三部分,因此,在HttpServletResponse接口中定义了向客户端发送响应状态码、响应头、响应体的方法
用我们自己的话说: Response就是服务器端一个对象,它里面可以封装要响应给客户端的响应行、头、体的信息
3.2 Response的组成部分
- 响应行: 包含响应状态码、状态码描述信息、HTTP协议的版本
- 响应头: 一个个的键值对,每一个键值对都包含了具有各自含义的发送给客户端的信息
- 响应体: 用于展示在客户端的文本、图片,或者供客户端下载或播放的内容
3.3 Response的作用
- 设置响应行的信息,主要是设置响应状态码
- 设置响应头的信息
- 设置响应体的信息
4.Response向客户端响应数据
4.1 使用Response向客户端输出字符串
4.1.1 输出字符串的API
//1. 获取字符输出流
PrintWriter writer = response.getWriter();
//2. 输出内容
writer.write("hello world");
4.1.2 响应数据乱码问题
由于服务器端在输出内容的时候进行编码使用的字符集和客户端进行解码的时候使用的字符集不一致,所以会发生响应数据乱码问题。
我们解决响应数据乱码问题只需要在获取字符输出流之前,执行如下代码就可以了:
response.setContentType("text/html;charset=UTF-8");
4.2 使用response向客户端响应一个文件
4.2.1 使用字节输出流的API
ServletOutputStream os = response.getOutputStream();
4.2.2 案例目标
在ServletDemo01中使用response向浏览器输出一张图片
4.2.3 案例代码
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("---------------- AServlet doGet() ----------------------");
// 解决请求乱码
req.setCharacterEncoding("utf-8");
// 解决响应乱码
resp.setContentType("text/html;charset=utf-8");
// 设置状态码
resp.setStatus(404);
resp.getWriter().println("AServlet Success: 你好帅啊 .....");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
package com.atguigu.servlet;
import org.apache.commons.io.IOUtils;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
public class ImgServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("----------ImgServlet doGet()------------");
ServletContext servletContext = getServletContext();
String realPath = servletContext.getRealPath("/img/g5.png");
FileInputStream fis = new FileInputStream(realPath);
ServletOutputStream os = resp.getOutputStream();
// 使用IoUtils的方法复制图片
IOUtils.copy(fis, os);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
4.3 重定向
4.3.1 什么是重定向
重定向是由项目中的一个资源跳转到另一个资源,在这个过程中客户端会发起新的请求
4.3.2 重定向的入门案例
① 案例目标
从EServlet重定向跳转到FServlet和外部资源
② 重定向的API
response.sendRedirect("路径");
③ 案例代码
package com.atguigu.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/e")
public class EServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("EServlet 执行了...." + req.getContextPath());
// 重定向
// resp.sendRedirect("f"); // 相对路径
// resp.sendRedirect(req.getContextPath() + "/f");
// resp.sendRedirect("http://www.baidu.com"); // 跳转到百度
resp.sendRedirect(req.getContextPath() + "/WEB-INF/b.html"); // 不能访问WEB-INF下的资源
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
4.3.3 重定向的特征
- 重定向的跳转是由浏览器发起的,在这个过程中浏览器会发起两次请求
- 重定向跳转可以跳转到任意服务器的资源,但是无法访问WEB-INF中的资源
- 重定向跳转浏览器的地址栏中的地址会变成跳转到的路径
4.4 重定向和请求转发的对比
- 重定向会由浏览器发起新的请求,而请求转发不会发起新的请求
- 重定向可以访问任意互联网资源,而请求转发只能访问本项目资源
- 重定向不能访问本项目的WEB-INF内的资源,而请求转发可以访问本项目的WEB-INF内的资源
- 发起重定向的资源和跳转到的目标资源没在同一次请求中,所以重定向不能在请求域中使用;而发起请求转发的资源和跳转到的目标资源在同一次请求中,所以请求转发可以在请求域中使用