请求对象
请求:获取资源。在BS架构中,就是客户端浏览器向服务器端发出询问。
请求对象:就是在项目当中用于发送请求的对象。
- ServletRequest和HttpServletRequest
获取各种路径
统一资源定位符:就是浏览器里面的网址栏中输入的完整地址 /demo/req
统一资源标识符:对应的虚拟目录名称以及Servlet访问的资源名称都获取了 http://localhost/demo/req
URI比URL的范围更加大一些,URI相当于共和国,URL相当于中华人民共和国
@WebServlet("/req")
public class reqServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取虚拟目录名称getContextPath ()
String contextPath = req.getContextPath();
System.out.println(contextPath);// /demo
//2.获取Servlet映射路径getServletPath()
String servletPath = req.getServletPath();
System.out.println(servletPath);// /req
//3.获取访问者ip getRemoteAddr()
String remoteAddr = req.getRemoteAddr();
System.out.println(remoteAddr);// 0:0:0:0:0:0:0:1
//4.获取请求消息的数据getQueryString()
String queryString = req.getQueryString();
System.out.println(queryString);// null 因为在路径上面没有填写然后的访问数据
//如果浏览器上面的地址变为:https://localhost/demo/req?username=ad&password=987
//则输出内容为:https://localhost/demo/req?username=ad&password=987
//5.获取统一资源标识符getRequestURI()
String requestURI = req.getRequestURI();
System.out.println(requestURI);// /demo/req
//6.获取`统一资源定位符getRequestURL()
StringBuffer requestURL = req.getRequestURL();
System.out.println(requestURL);// http://localhost/demo/req
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
/*
/demo
/req
0:0:0:0:0:0:0:1
username=ad&password=987
/demo/req
http://localhost/demo/req
*/
获取请求头信息
@WebServlet("/req")
public class reqServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//根据请求头名称获取一个值
String collection = req.getHeader("collection");
System.out.println(collection); // null
System.out.println("--------------------");
//根据请求头名称获取多个值
Enumeration<String> headers = req.getHeaders("accept-encoding");
while (headers.hasMoreElements()){
String s = headers.nextElement();
System.out.println(s);
}// gzip, deflate, br
System.out.println("--------------------");
//获取所有的请求头名称
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()){
String name = headerNames.nextElement();
String value = req.getHeader(name);
System.out.println(name + ","+value);
}
/*
*/
System.out.println("--------------------");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
/*
null
--------------------
gzip, deflate, br
--------------------
host,localhost
connection,keep-alive
cache-control,max-age=0
sec-ch-ua,"Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92"
sec-ch-ua-mobile,?0
upgrade-insecure-requests,1
user-agent,Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36
accept,text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
sec-fetch-site,none
sec-fetch-mode,navigate
sec-fetch-user,?1
sec-fetch-dest,document
accept-encoding,gzip, deflate, br
accept-language,zh-CN,zh;q=0.9,en;q=0.8
cookie,Idea-aafc89f8=00150c41-43b3-4e53-9346-76c7ba6f7eab; JSESSIONID=761F8802476C5A228C27B8476AAA9C76
--------------------
*/
获取请求参数信息
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="/demo/req" method="get" autocomplete="off">
姓名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
爱好:<input type="checkbox" name="hobby" value="game">游戏
<input type="checkbox" name="hobby" value="study">读书<br>
<button type="submit">注册</button>
</form>
</body>
</html>
@WebServlet("/req")
public class reqServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 根据名称获取数据getParameter()
String username = req.getParameter("username");
System.out.println(username);// whi
String password = req.getParameter("password");
System.out.println(password);// asddas
System.out.println("-------------");
//2.根据名称获取所有数据getParameterValues()
String[] parameterValues = req.getParameterValues("hobby");
for (String i : parameterValues){
System.out.println(i);
}
//game
//study
System.out.println("------------------------------");
//3.获取所有名称getParameterNames()
Enumeration<String> parameterNames = req.getParameterNames();
while (parameterNames.hasMoreElements()){
String s = parameterNames.nextElement();
System.out.println(s);
}
/*
username
password
hobby
*/
System.out.println("--------------");
//4.获取所有参数的键值对getParameterMap()
Map<String, String[]> parameterMap = req.getParameterMap();
for (String key:parameterMap.keySet()){
String[] values = parameterMap.get(key);
System.out.print(key+":");
for (String value : values){
System.out.println(value);
}
}
/*
username:whi
password:asddas
hobby:game
study
*/
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
/*
whi
asddas
-------------
game
study
------------------------------
username
password
hobby
--------------
username:whi
password:asddas
hobby:game
study
*/
获取请求参数并封装对象
有三种方式:
- 1.手动封装方式。
- 2.反射封装方式。
- 3.工具类封装方式。
手动封装
发表调用对象
-
学生类
public class Student { //成员变量的名称要和获取数据里面的参数名称要保持一致 private String username; private String password; private String[] hobby; public Student() { } public Student(String username, String password, String[] hobby) { this.username = username; this.password = password; this.hobby = 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[] getHobby() { return hobby; } public void setHobby(String[] hobby) { this.hobby = hobby; } @Override public String toString() { return "Student{" + "username='" + username + '\'' + ", password='" + password + '\'' + ", hobby=" + Arrays.toString(hobby) + '}'; } }
-
html页面修改
<form action="/demo/reqduix" method="get" autocomplete="off"> ......... </form>
-
Servlet
@WebServlet("/reqduix") public class duixServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1. 获取所有的数据 String username = req.getParameter("username"); String password = req.getParameter("password"); String[] hobbies = req.getParameterValues("hobby"); //封装学生对象 Student stu = new Student(username, password, hobbies); //输出对象 System.out.println(stu);//Student{username='原因', password='7981132', hobby=[game, study]} } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } //Student{username='原因', password='7981132', hobby=[game, study]}
反射封装
比较繁琐
@WebServlet("/reqduix")
public class duixServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 获取所有的数据
Map<String, String[]> map = req.getParameterMap();
//封装学生对象
Student stu = new Student();
//遍历集合
for(String name:map.keySet()){
String[] value = map.get(name);
try {
//获取Student对象的属性描述器:实际上是根据name,
// 也就是根据名称来拿到学生对象中指定的成员变量的get和set方法
//比如:在这里name遍历拿到的是username,
// 那么就能拿到学生对象username的get和set方法,
// 这两个方法就封装到了这个对象的属性描述器中pd
PropertyDescriptor pd = new PropertyDescriptor(name, stu.getClass());
//获取到对应的setXXX方法
Method writeMethod = pd.getWriteMethod();
//执行对应的方法,需要在这里进行条件判断,判断这个值是一个还是多个
if (value.length > 1){
writeMethod.invoke(stu,(Object)value);
}else {
writeMethod.invoke(stu,value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//输出对象
System.out.println(stu);//Student{username='ahl', password='asgh', hobby=[game, study]}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
工具类封装
这个方法需要导入下面这两个jar包
除此之外,还需要将这个包添加进服务器里面,:操作步骤
@WebServlet("/reqduix")
public class duixServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 获取所有的数据
Map<String, String[]> map = req.getParameterMap();
//封装学生对象
Student stu = new Student();
try {
BeanUtils.populate(stu,map);
} catch (Exception e) {
e.printStackTrace();
}
//输出对象
System.out.println(stu);//Student{username='加多少', password='sasa', hobby=[game, study]}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
流对象获取请求信息
只支持post方式
@WebServlet("/reqduix")
public class duixServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//字符流(请求方式必须是post方式)
BufferedReader br = req.getReader();
String li;
while ((li = br.readLine()) != null){
System.out.println(li);
}
//username=czddf&password=sadsadsa&hobby=game&hobby=study
//因为这是调用Servlet的方法,所以不需要关闭流,
// 因为在Servlet被销毁过后,流也会自动关闭
//字节流
ServletInputStream is = req.getInputStream();
byte[] arr = new byte[1024];
int len;
while ((len = is.read(arr)) != -1){
System.out.println(new String(arr,0,len));
}
//username=czddf&password=sadsadsa&hobby=game&hobby=study
//同理,也不需要手动关闭流,释放资源
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
中文乱码问题
-
GET方式
- 没有乱码问题。在Tomcat 8版本后已经解决!
-
POST方式
-
有乱码问题。可以通过setCharacterEncoding(方法来解决!
req.setCharacterEncoding("Utf-8");
-
请求域
- 请求域(request域) :可以在一次请求范围内进行共享数据。
- 一般用于请求转发的多个资源中共享数据
- 请求对象操作共享数据方法
请求转发
-
请求转发:客户端的一次请求到达后,发现需要借助其他Servlet来实现功能。
- 客户端浏览器发送一个请求到ServletA中,想借助ServletA去实现一些功能,但是ServletA完成不了这个功能,这时候它发现ServletB是可以实现这些功能的,所以ServletA就这次请求转发个ServletB
-
特点:
- 浏览器地址栏不变:(还是ServletA的地址)
- 域对象中的数据不丢失:(可以在ServletA中去设置一些共享数据,ServletB就能获取这些数据)
- 负责转发的Servlet转发前后的响应正文会丢失:(因为最终是由ServletB去响应给客户端的,所以ServletA的正文会丢失)
- 由转发的目的地来响应客户端
-
实现方法
-
代码实现
转发的Servlet
@WebServlet("/qqzf1") public class qqzfDemo1 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置共享数据 req.setAttribute("encoding","gbk"); //获取请求调度对象 RequestDispatcher rd = req.getRequestDispatcher("/qqzf2"); //实现转发功能 rd.forward(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
真正执行的Servlet
@WebServlet("/qqzf2") public class qqzfDemo2 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取共享数据 Object encoding = req.getAttribute("encoding"); System.out.println(encoding); System.out.println("qqzf2执行了"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } /* gbk qqzf2执行了 */
请求包含
-
请求包含:可以合并其他Servlet中的功能一起响应给客户端。
- 客户端浏览器发送一个请求,找到了ServletA实现一些功能,但是ServletA只能完成一部分,另一部分实现不了,替换ServletA发现ServletB可以把另外一部分的功能实现,所以这个时候ServletA就把ServletB包含了一下。能够在ServletA中合并其他Servlet当中的功能,一起响应给客户端
-
特点:
- 浏览器地址栏不变(还是ServletA的地址)
- 域对象中的数据不丢失(可以在ServletA中去设置一些共享数据,ServletB就能获取这些数据)
- 被包含的Servlet响应头会丢失(ServletB的消失)
-
实现方法
-
代码测试
@WebServlet("/qqzf1") public class qqzfDemo1 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置共享数据 req.setAttribute("encoding","gbk"); //获取请求调度对象 RequestDispatcher rd = req.getRequestDispatcher("/qqzf2"); //实现转发功能 rd.include(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
@WebServlet("/qqzf2") public class qqzfDemo2 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取共享数据 Object encoding = req.getAttribute("encoding"); System.out.println(encoding); System.out.println("qqzf2执行了"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } } /* gbk qqzf2执行了 */
响应对象
响应:回馈结果。在BS架构中,就是服务器给客户端浏览器反馈结果。
- 响应对象:就是在项目中用于发送响应的对象。
- ServletResponse和HttpServletResponse,这两个就是响应对象,其中HttpServletResponse是继承ServletResponse的,HttpServletResponse是基于HTTP的
服务器端想要响应消息个客户端的话,都是通过流对象来进行实现的,分为字节流和字符流
里面的通过resp获取的流对象,都不用去关闭流对象,因为Servlet会自己去处理这些
常见状态码
4开头一般代表的是客户端的错误,5开头一般代表的是服务器端错误
字节流响应消息
所有的响应内容都是来自于HTTPServletResponse这个对象的
-
获取字节流
ServletOutPutStream继承OutputStream,所以ServOutputStream的功能和使用的方式都和之前学习的字节输出流是一样的
@WebServlet("/xiangy1")
public class xiangyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取字节输出流对象
ServletOutputStream os = resp.getOutputStream();
//定义一个消息
String str = "你好";
//通过字节流对对象输出
os.write(str.getBytes());
//这个会在浏览器上面输出“你好”,
// 但是当字符串内容变成三个汉字的时候,就会乱码,这个问题我在java基础里面说过了
// 为什么不乱码:
// getBytes()方法在将字符串转成字节数组的时候,方法里面是可以传递参数的,如果不去传递参数的话
//那么他会根据当前系统平台(操作系统)的编码格式来进行转换,所以为什么看不见乱码问题,
// 就是在转出字节数组的时候元也是按照浏览器的编码格式进行转换的,但是后面的项目都是UTF-8,
// 所以我们需要在getBytes()方法里面传递一个参数:getBytes("UTF-8")
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
-
解决乱码问题
@WebServlet("/xiangy1") public class xiangyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //添加下面的一句代码就行了 resp.setContentType("text/html;UTF-8"); ....... } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
字符流响应消息
text/html的意思是可以识别html元素
- 代码实现
@WebServlet("/xiangy1")
public class xiangyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置编码格式
resp.setContentType("text/html;charset=UTF-8");
//获取字符输出流对象
PrintWriter pw = resp.getWriter();
//准备一个消息
String str = "a阿萨德第三方";
//通过字符流输出
pw.write(str);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
案例
响应图片
@WebServlet("/xiangy1")
public class xiangyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.创建字节输入流对象,关联图片路径:所有的Web资源都是以Web-INF作为根目录的
// BufferedInputStream bis = new BufferedInputStream(new FileInputStream("/img/img.jpg"));//如果这样写路径会报错,找不到该文件
//根据文件的相对路径获取绝对路径
//首先获取到应用上下文对象,然后在调用getRealPath方法,这样就能获取项目发布之后的绝对路径
String realPath = getServletContext().getRealPath("/WEB-INF/img/img.jpg");
System.out.println(realPath);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));
//2.通过响应对象获取字节输出流对象
ServletOutputStream os = resp.getOutputStream();
//3.循环读写
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1){
//写出这个字节数组,从0索引开始,读到几个就写几个
os.write(bytes,0,len);
}
//释放资源
bis.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
文件下载
- 1.创建字节输入流,关联读取的文件。
- 2.设置响应消息头支持的类型。
- 3.设置响应消息头以下载方式打开资源。
- 4.通过响应对象获取字节输出流对象。
- 5.循环读写。
- 6.释放资源。
@WebServlet("/xiangy1")
public class xiangyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.创建字节输入流对象,关联读取的文件
String realPath = getServletContext().getRealPath("/WEB-INF/img.img.jpg");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));
//2.设置响应头支持的类型
//第一个参数:消息头名称,第二个参数:消息的具体的参数
/*
Content-Type:消息头名称 支持的类型
application/octet-stream:消息头参数 应用的类型为字节流
*/
resp.setHeader("Content-Type","application/octet-stream");
//3.设置响应头以下载方式打开附件
/*
Content-Disposition:消息头的名称 处理的形式
attachment;filename=img.jpg 消息头参数 ,附近形式进行处理 filename:指定下载文件的名称
*/
resp.setHeader("Content-Disposition","attachment;filename=img.jpg");
//4.获取字节输出流对象
ServletOutputStream os = resp.getOutputStream();
//5.循环读写
byte[] arr = new byte[1024];
int len;
while ((len=bis.read(arr)) != -1){
os.write(arr,0,len);
}
//这个是重要输入地址,然后回车就会自动下载,不需要确认
//6释放咨源
bis.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
设置缓存
缓存:对于不经常变化的数据,我们可以设置合理缓存时间,以避免浏览器频繁请求服务器。以此来提高效率!
@WebServlet("/xiangy1")
public class xiangyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String n = "吴签他说会自己进牢房";
//设置缓存,1h的缓存时间:在这一个小时之内,
// 如果再次请求的话,是不会再次请求服务器,而是直接从缓存当中查询数据
resp.setDateHeader("Expires",System.currentTimeMillis()+1*60*60*100);
//设置编码格式
resp.setContentType("text/html;charset=UTF-8");
//写出数据
resp.getWriter().write(n);
System.out.println("访问第一次");//这句话只会在第一次访问的时候输出,其他基础访问并不会出现该内容,是需要在地址栏敲回车,如果使用刷新的按钮还是请求服务器
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
定时刷新
定时刷新:过了指定时间后,页面自动进行跳转
@WebServlet("/xiangy1")
public class xiangyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String str = "3s后自动跳转的注册页面。。。";
//设置编码格式
resp.setContentType("text/html;charset=UTF-8");
//写出数据
resp.getWriter().write(str);
//定时刷新
/*
第一个参数:消息头的名称
第二个参数:字符串类型;分为两个部分:
第一个部分:过了几秒钟过后
第二个部分:过了这么多秒过后要跳转的路径,需要将虚拟目录写在前面
*/
resp.setHeader("Refresh","3;URL=/demo/register.html");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
请求重定向
- 请求重定向:客户端的一次请求到达后,发现需要借助其他Servlet来实现功能。
- 特点
- 1.浏览器地址栏会发生改变,
- 2.两次请求请求域对象中不能共享数据
- 3.可以重定向到其他服务器。
- 重定向是两次请求
- 特点
@WebServlet("/xiangy1")
public class xiangyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("xiangy1执行了");
///设置重定向
//这个是浏览器的输入地址,就是我想访问reqduix,在浏览器地址栏做输入的内容
resp.sendRedirect(req.getContextPath()+"/reqduix");
resp.sendRedirect("https:.//www.baidu.com");//访问这个Servlet的话,重定向到百度的页面去
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
如何选择重定向还是请求转发
查看跳转资源的时候需不需要共享数据,如果需要的话使用的就是请求转发,不需要就可以使用重定向