Request&Response
Request
请求对象
请求,顾名思议,就是使用者希望从服务器端索取一些资源,向服务器发出询问。在B/S架构中,就是客户浏览器向服务器发出询问。在我们的JavaEE工程中,客户浏览器发出询问,要遵循HTTP协议所规定的。
请求对象,就是在JavaEE工程中,用于发送请求的对象。我们常用的对象就是ServletRequest和HttpServletRequest,它们的区别就是是否和HTTP协议有关。
获取各种路径的方法
实操演示,先创建一个项目,我这里起名request_demo,
创建一个继承HttpServlet 的类
package com.symc.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;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/12 19:24
* @Description:
*/
@WebServlet("/requestDemo1")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取虚拟目录的名称
String contextPath = req.getContextPath();
System.out.println(contextPath);
//获取servlet映射路径
String servletPath = req.getServletPath();
System.out.println(servletPath);
//获取访问者IP
String remoteAddr = req.getRemoteAddr();
System.out.println(remoteAddr);
//获取请求消息的数据
String queryString = req.getQueryString();
System.out.println(queryString);
//获取统一资源标识符
String requestURI = req.getRequestURI();
System.out.println(requestURI);
//获取统一资源定位符
StringBuffer requestURL = req.getRequestURL();
System.out.println(requestURL);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
配置
启动tomcat,浏览器访问,控制台显示如下
由输出可知,虚拟目录和映射路径是什么东西;
获取的访问者iP,这里是本机地址,采用的是IPv6;
而请求消息的数据,这里并没有,所以是空的;
对于URI和URL需要知道的是URI包含URL。
现在我们在地址栏加入请求内容
在观察控制台
这就是获取到的请求消息的数据。
获取请求头信息
还是使用刚才的例子,我们在网页按F12打开开发者工具
现在我们要在控制台获取请求头的这些信息。
package com.symc.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;
import java.util.Enumeration;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/12 19:24
* @Description: 获取请求头信息
*/
@WebServlet("/requestDemo2")
public class RequestDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//根据请求头名称获取一个值,比如这里获取的是Connection的信息,不区分大小写
String value = req.getHeader("connection");
System.out.println(value);
System.out.println("-----------------------------------");//分割线
//根据请求头名称获取多个值
Enumeration<String> values = req.getHeaders("Accept-Encoding");
while (values.hasMoreElements()) {
String val = values.nextElement();
System.out.print(val + " ");
}
System.out.println("-----------------------------------");//分割线
//获取所有请求头名称
Enumeration<String> names = req.getHeaderNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
String s = req.getHeader(name);
System.out.println(s);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
观察控制台
获取请求参数信息
首先写一个html,用于用户访问
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>注册页面</title>
</head>
<body>
<div>
<form action="/request/requestDemo3" method="get" autocomplete="off">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
爱好:<input type="checkbox" name="hobby" value="study">学习
<input type="checkbox" name="hobby" value="game">游戏
<input type="checkbox" name="hobby" value="sport">运动<br>
<button type="submit">注册</button><br>
</form>
</div>
</body>
</html>
创建一个类来测试上面的这些方法
package com.symc.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sound.midi.Soundbank;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/13 10:26
* @Description:
*/
@WebServlet("/requestDemo3")
public class RequestDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//根据名称获取数据
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username);
System.out.println(password);
System.out.println("---------------------------------------");
//根据名称获取所有数据
String[] hobbies = req.getParameterValues("hobby");
for (String hobby : hobbies) {
System.out.println(hobby);
}
System.out.println("---------------------------------------");
//获取所有名称
Enumeration<String> names = req.getParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
System.out.println(name);
}
System.out.println("---------------------------------------");
//获取所有参数的键值对
Map<String, String[]> map = req.getParameterMap();
for (String key : map.keySet()) {
String[] values = map.get(key);
System.out.print(key + ":");
for (String value : values) {
System.out.println(value);
}
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
开启服务器,访问并填写内容提交,观察控制台。
获取参数手动封装对象
首先说一下为什么要封装对象,刚才的案例属性比较少,通过打印到控制台还是比较容易的,但是当数据非常多的时候,输出起来就非常复杂。那么我们将每一次用户访问的数据封装到一个对象中,我们只需要通过获取对象的信息就能获取到这些数据。
首先创建一个实体类,并且其中的成员变量一定要与我们要获得的信息一致。
package com.symc.bean;
import java.util.Arrays;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/13 17:46
* @Description:
*/
public class User {
private String username;
private String password;
private String[] hobbies;
public User() {
}
public User(String username, String password, String[] hobbies) {
this.username = username;
this.password = password;
this.hobbies = hobbies;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", hobbies=" + Arrays.toString(hobbies) +
'}';
}
}
这样我们在写servlet时只需要将获取的参数放到对象中即可。
package com.symc.servlet;
import com.symc.bean.User;
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;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/13 17:42
* @Description:
*/
@WebServlet("/requestDemo4")
public class RequestDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbies = req.getParameterValues("hobby");
User user1 = new User(username, password, hobbies);
System.out.println(user1);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
别忘记html的提交位置也要改变
<form action="/request/requestDemo4" method="get" autocomplete="off">
访问,观察控制台
获取参数反射封装对象
刚才将输出部分封装起来,可是当数据项足够多的时候,我们还是得写入大量的req.getParameter()方法,有没有一劳永逸的方法呢?
package com.symc.servlet;
import com.symc.bean.User;
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.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/13 19:38
* @Description: 获取参数反射封装对象
*/
@WebServlet("/requestDemo5")
public class RequestDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取所有数据
Map<String, String[]> parameterMap = req.getParameterMap();
//封装对象
User user = new User();
//遍历集合
for (String name : parameterMap.keySet()) {
String[] value = parameterMap.get(name);
try {
//获取封装对象的属性描述器
PropertyDescriptor pd = new PropertyDescriptor(name, user.getClass());
//获取对应的setXxx方法
Method writeMethod = pd.getWriteMethod();
//执行方法
if (value.length>1) {
writeMethod.invoke(user, (Object) value);
}else {
writeMethod.invoke(user,value);
}
} catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
System.out.println(user);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
我做这个的时候遇到个异常:
java.beans.IntrospectionException: Method not found: isHobby
获取参数工具类封装对象
上面我们写起来还是很复杂,我们可以导入已经写好的jar包。
所以这里关键是如何导入jar包。
首先,将我们的本地的jar包复制下来,这两个jar包都是由apache提供的。
在web目录下创建一个lib目录,专门用来放置jar包,粘贴进去。
粘贴进来后还需要将他们添加到引用类库中
全选右键找到add as library,确定即可。
这个时候我们就可以使用了。
这里我已经把需要的jar包上传到网盘了。
百度网盘资源链接
链接:https://pan.baidu.com/s/1Qev4FerXAFGAPhfXepZSJA
提取码:xxxx
导入jar包后我们就要写代码了
package com.symc.servlet;
import com.symc.bean.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.util.Map;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/14 15:31
* @Description: 使用工具类封装对象
*/
@WebServlet("/requestDemo6")
public class RequestDemo6 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Map<String, String[]> parameterMap = req.getParameterMap();
User user = new User();
try {
BeanUtils.populate(user,parameterMap);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(user);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
可见,我们只需要将map集合获取,调用BeanUtils的populate类传入对象和map集合就可以了。
调整好html的提交位置,现在我们来启动服务器运行一下
结果。。。
网页提示找不到这个类,造成这个问题的原因是这个jar包虽然导入到类库中,但并没有导入到项目的war包中,解决方法如下:
在file——projectStructure中,最下面选项的problem,出现有一个问题,点击fix,选图中的第一个,然后问题就解决了。
通过流对象获取请求信息
@WebServlet("/requestDemo7")
public class RequestDemo7 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//字符流(必须是post请求方式)
BufferedReader br = req.getReader();
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
br.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
需要注意,通过字符流的这种方式只有post是有效的。
<form action="/request/requestDemo7" method="post" autocomplete="off">
由于编码的限制,我这里用一个英文名字,并且会发现post方式地址栏不显示请求信息。
控制台是这样的
这个是字节流
//字节流
ServletInputStream is = req.getInputStream();
byte[] bytes = new byte[1024];
int len;
while ((len = is.read(bytes)) !=-1){
System.out.println(new String(bytes,0,len));
}
中文乱码问题
Get方式没有乱码问题,已经在Tomcat8版本后解决了。
Post方式有乱码问题,可以通过setCharacterEncoding()方法来解决。
之前的例子都是用get方式来做的,或者用对象的方式,我这个9.0的tomcat没有发现乱码问题!
我们来看一下Post方式的乱码:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
System.out.println(username);
}
输出这样的:
我们只需要在方法里面添加上setCharacterEncoding方法就可以解决。
req.setCharacterEncoding("UTF-8");
String username = req.getParameter("username");
System.out.println(username);
这里的编码要与Html中的编码一致才可以,当然我们大多数情况下开发用的都是UTF-8。
<html lang="en">
<head>
<meta charset="UTF-8">
这样问题就解决了。
请求域、请求转发、请求包含
请求域也叫request域,可以在一次请求范围内进行共享数据。它一般用于请求转发的多个资源中共享数据。
请求对象操作共享数据方法。
请求转发:客户端的一次请求到达后,发现需要借助其他Servlet来实现功能。
当客户端访问到ServletA时,想要通过ServletA完成功能,但是发送给它时发现ServletA并不能满足想要的功能,同时有一个ServletB却可以实现这个功能,那么ServletA将请求发送给ServletB,有ServletB完成目标,这个过程就叫请求转发。
请求转发的特点:
浏览器地址栏不变,域对象中的数据不丢失,负责转发的Servlet转发前后的响应正文会丢失,由转发的目的地来响应客户端。
请求转发的API:
这里的形参列表是请求转发到的路径,获取到的就是请求调度对象RequestDispatcher,获取到这个对象后,通过forward方法来实现转发,
forword方法调用时还需要将请求和响应对象传递。
package com.symc.servlet;
import javax.servlet.RequestDispatcher;
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;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/16 17:31
* @Description: 请求转发实现
*/
@WebServlet("/requestDemo9")
public class RequestDemo9 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置共享数据
req.setAttribute("data","随意的测试数据");
//获取请求调度对象
RequestDispatcher rd = req.getRequestDispatcher("/requestDemo10");
rd.forward(req,resp);
//实现转发
}
}
package com.symc.servlet;
import javax.servlet.RequestDispatcher;
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;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/16 17:31
* @Description: 请求转发实现
*/
@WebServlet("/requestDemo9")
public class RequestDemo9 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置共享数据
req.setAttribute("data","随意的测试数据");
//获取请求调度对象
RequestDispatcher rd = req.getRequestDispatcher("/requestDemo10");
rd.forward(req,resp);
//实现转发
}
}
这两个类完成后我们直接访问requestDemo9,发现控制台的信息便是成功转发给了requestDemo10
请求包含:可以合并其他Servlet的功能一起响应给客户端。
请求包含和请求转发类似。客户端发送请求给ServletA,ServletA能够完成一部分,而剩下的部分A不能完成,这时发现ServletB能够完成剩下的部分,于是A就把B包含。
请求包含的特点:
浏览器地址栏不变,域对象的数据不丢失,被包含的Servlet响应头会丢失。
实现方法与请求转发类似:
代码实现方式与请求转发基本一致
requestDemo11
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("requestDemo11执行了。。");
//获取请求调度对象
RequestDispatcher rd = req.getRequestDispatcher("/requestDemo12");
//实现请求包含
rd.include(req,resp);
}
requestDemo12
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("requestDemo12执行了。。。");
}
Response
响应对象
响应:回馈结果。在BS架构中,就是服务器给客户端浏览器反馈结果。
对于这个回馈结果,要么是成功,要么是失败。
响应对象:就是项目中用于发送响应的对象。
响应对象有ServletResponse、HttpServletResponse
常见状态码
状态码 | 说明 |
---|---|
200 | 成功 |
302 | 重定向 |
304 | 请求资源未改变 |
400 | 请求错误 |
404 | 请求资源未找到 |
405 | 请求方式不支持 |
500 | 服务器错误 |
刚才提到响应的结果,要么成功,要么失败,这个结果浏览器会返回一些状态码,表明是否响应成功,又表明问题可以出现的原因。 | |
对于200,说明响应成功了; | |
对于3开头的3xx,说明访问成功,但是还需要进行进一步的细化操作,比如302,重定向,需要继续访问,再如304,请求资源未改变,使用的是缓存的资源,这个时候客户端应重新刷新; | |
对于4xx开头的,属于客户端访问出错,尤其经常出现的404,找不到文件,很可能是因为地址栏输入错误,而对于开发者,也可能是因为后端的某些脚本存放的路径错误; | |
然后500这样的,都属于服务器错误,比如,服务器宕机。对于开发者,那还可以从后端代码错误上找原因,浏览器页面会抛出异常。 |
字节流响应消息和解决乱码问题
我这里又创建了一个块。
package com.symc.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/17 16:06
* @Description: 字节流响应消息
*/
@WebServlet("/responseDemo1")
public class ResponseDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");//设置编码
//创建输出流对象
ServletOutputStream os = resp.getOutputStream();
//创建消息
String message = "你好!";
//响应给浏览器
//getBytes默认的编码是GBK,浏览器的编码也是GBK
//但是项目的编码需要统一为utf-8,于是需要解决乱码问题
os.write(message.getBytes("utf-8"));
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
字符流响应消息和解决乱码问题
写法与上面字节流差不多
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter pw = resp.getWriter();
String message = "你好!";
pw.write(message);
}
响应图片
之前都是返回给页面文字,那现在我想返回一张图片。
首先需要有一张图片,把图片粘贴到web目录下,我这里还新建了一个img目录;
同样是使用流的方式,不过这次使用的是效率更高的buffered,然后代码如下:
package com.symc.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/18 13:10
* @Description: 响应图片
*/
@WebServlet("/responseDemo3")
public class ResponseDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过文件的相对路径获取绝对路径
String realPath = getServletContext().getRealPath("/img/resp.png");
System.out.println(realPath);
//创建字节输入流对象,关联图片路径
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));
//通过响应对象获取字节输出流对象
ServletOutputStream os = resp.getOutputStream();
//循环读写
int len;
byte[] bytes = new byte[1024];
while ((len = bis.read(bytes)) != -1) {
os.write(bytes,0,len);
}
//os由服务器自动关闭
bis.close();
}
}
这个代码就是将图片文件转成流。
看一下访问的效果
这里有个问题值得说一下:
这里为什么要获取绝对路径,难道直接用相对路径不可以吗?
其实正常读取文件使用相对路径是可以的,但是我们现在是要发布到网页上,发布的路径是经过war包的路径的,所以必须使用绝对路径才可以,通过servlet的获取绝对路径的方法就可以实现。下面是控制台打印的绝对路径
这里有个response_demo_war_exploded的war包路径,使用相对路径是找不到文件的。
当然,我把使用相对路径的访问情况也发一下
设置缓存
缓存:对于不经常变化的数据,我们可以设置合理的缓存时间,以避免浏览器频繁请求服务器,依此来提高效率。
通过设置缓存,我们期望,当用户访问我们的资源时,只有第一次才会访问我们的服务器端,比如设置1小时的缓存时间,在客户访问服务器后的这一小时内,如果客户再次来请求的话,是不会来请求我们的服务器端,而是直接从缓存中查询数据。
而这个方法setDateHeader有两个参数,在代码中会解释。
package com.symc.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;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/18 15:31
* @Description: 设置缓存
*/
@WebServlet("/responseDemo4")
public class RequestDemo4 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置数据
String message = "这条消息设置了缓存,1小时内都可以查看";
//设置缓存
//这个方法中有两个参数,第一个要填写消息头,这里其实就是Expires,过期的意思,也就是说,
//给这些缓存设置失效的时间,而第二个参数就是具体的时间,单位是毫秒数,
// 这里使用的是获取当前时间再加上1小时,即设置1小时的缓存,也即在1小时后这个缓存会失效
resp.setDateHeader("Expires",
System.currentTimeMillis() + 1 * 60 * 60 * 1000);
//设置编码格式
resp.setContentType("text/html;charset=utf-8");
//写出数据
resp.getWriter().write(message);
//用来测试的
System.out.println("用户访问了服务器。。");
}
}
定时刷新
相信同学们在使用浏览器访问时,也常常遇到说“3秒后自动跳转到另一个页面”,当然这个页面是我们可以指定。
定时刷新就是,过了指定的时间,页面自动进行跳转。
package com.symc.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;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/18 16:14
* @Description: 定时刷新
*/
@WebServlet("/responseDemo5")
public class ResponseDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String message = "登录成功!3秒后自动返回登录页面";
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write(message);
//设置定时刷新
//定时刷新有两个参数,第一个消息头写“Refresh”,是刷新的意思,第二个参数中的3是代表
//3秒后刷新,URL是跳转的目标
resp.setHeader("Refresh","3;URL=/response/html/login.html");
}
}
这里面的login.html就写个简单的登录界面能用来测试就行。
<div>
<form action="/response/responseDemo5" method="get" autocomplete="off">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<button type="submit">登录</button><br>
</form>
</div>
请求重定向
请求重定向:客户端的一次请求到达后,发现需要借助其他Servlet来实现功能。
特点:浏览器地址栏会发生改变,两次请求、请求域对象中不能共享数据,可以重定向到其他的服务器。
重定向的实现原理:
1. 设置状态码为302:resp.setStatus(302);
2. 设置资源的响应路径(响应到哪里去,通过响应消息头location来指定)
resp.setHeader(“location”,“/response/responseDemo7”)
以上两个方法就是实现原理,不过已经有了封装好的方法直接使用。
响应对象重定向方法:
下面我们来创建两个类来感受一下重定向的特点:
@WebServlet("/responseDemo6")
public class ResponseDemo6 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("responseDemo6执行了");
//设置共享数据
req.setAttribute("username","张三");
//设置重定向
resp.sendRedirect(req.getContextPath()+"/responseDemo7");
}
}
@WebServlet("/responseDemo7")
public class ResponseDemo7 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("responseDemo7执行了");
//获取共享数据
Object username = req.getAttribute("username");
System.out.println(username);
}
}
当我们访问Demo6的时候,它立即跳转到Demo7
而控制台的情况如下
这说明这两个servlet都访问到了,但是Demo6中设置的共享数据Demo7中并不能访问到,证明不能共享数据。
我们打开开发者工具,找到网络的选项
发现Demo6的响应状态是302,重定向的位置就是Demo7
Demo7的状态是200,说明响应成功,所以是两次请求。
下载文件
1.创建字节输入流,关联读取的文件。
2.设置响应消息头支持的类型。
3.设置响应消息头以下载方式打开资源。
4.通过响应对象获取字节输出流对象。
5.循环读写。
6.释放资源。
package com.symc.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/18 20:30
* @Description: 下载文件
*/
@WebServlet("/responseDemo8")
public class ResponseDemo8 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.创建字节输入流,关联读取的文件。
String realPath = getServletContext().getRealPath("/img/resp.png");
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=resp.png 消息头参数 附件形式进行处理 指定下载文件的名称
resp.setHeader("Context-Disposition", "attachment;filename=resp.png");
// 4.通过响应对象获取字节输出流对象。
ServletOutputStream os = resp.getOutputStream();
// 5.循环读写。
byte[] b = new byte[1024];
int len;
while ((len = bis.read(b)) != -1) {
os.write(b);
}
// 6.释放资源。
bis.close();
}
}
我们访问后,可以发现就有下载文件的提示。
案例
先来看一下要做个什么样的页面:
首页是一个学生管理功能的页面,有添加和查看的功能
添加功能如下
项目目录是这样的:
这里的index.html是首页
addStudent.html是添加学生的功能页面
info.txt是用来存放学生信息的本地文件,当然可以使用mysql实现,我这里用个记事本代替一下。
接下来开始实现:
创建项目并配置,我这里虚拟目录为stu
首先,先创建一个主页
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>学生管理系统首页</title>
</head>
<body>
<a href="/stu/addStudent.html">添加学生</a>
<a href="/stu/listStudentServlet">查看学生</a>
</body>
</html>
然后打开web.xml文件,将它设置为默认主页
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
再写一个添加功能的页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>保存学生信息</title>
</head>
<body>
<div>
<form action="/stu/addStudentServlet" method="get" autocomplete="off">
学生姓名:<input type="text" name="name"><br>
学生班级:<input type="number" name="aClass"><br>
学生成绩:<input type="number" name="score"><br>
<button type="submit">保存</button>
</form>
</div>
</body>
</html>
接下来写后端功能,在bean目录下创建Student学生实体,并添加构造方法和get、set方法:
package com.symc.test.bean;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/21 20:47
* @Description:
*/
public class Student {
private String name;
private int aClass;
private double score;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAClass() {
return aClass;
}
public void setAClass(int aClass) {
this.aClass = aClass;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public Student() {
}
public Student(String name, int aClass, double score) {
this.name = name;
this.aClass = aClass;
this.score = score;
}
}
添加功能的实现:
package com.symc.test.servlet;
import com.symc.test.bean.Student;
import javafx.scene.text.FontPosture;
import sun.font.TrueTypeFont;
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.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/21 20:53
* @Description: 1.创建类AddStudentServlet,继承HttpServlet
* 2.重写doGet和doPost方法
* 3.获取表单中的数据。
* 4.将获取到的数据封装成Student对象
* 5.将Student的中数据保存到文件中
* 6.通过定时刷新功能完成对浏览器的响应。
*/
@WebServlet("/addStudentServlet")
public class AddStudentServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
String aClass = req.getParameter("aClass");
String score = req.getParameter("score");
Student stu = new Student(name, Integer.parseInt(aClass),
Double.parseDouble(score));
BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\Documents" +
"\\Documents\\ProgramProjects\\javaee\\reqresp_test\\file" +
"\\info.txt",true));
bw.write(stu.getName() + "," + stu.getAClass() + "," + stu.getScore());
bw.newLine();
bw.close();
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("添加成功!3秒后自动跳转到首页。。。");
resp.setHeader("Refresh", "3,URL=index.html");
}
}
查看功能的实现:
package com.symc.test.servlet;
import com.symc.test.bean.Student;
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.awt.dnd.DragGestureEvent;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
/**
* @Author: 凤文 沈阳医学院2019级医学信息工程 0213
* @CreateTime: 2021/10/22 8:32
* @Description:
* 1.创建一个ListStudentServlet类,继承HttpServlet
* 2.重写doGet和doPost方法
* 3.通过字符输入流读取文件中的数据。
* 4.将读取到的数据封装到Student对象中。
* 5.将多个Student对象保存到集合中。
* 6.遍历集合,响应到浏览器中
*/
@WebServlet("/listStudentServlet")
public class ListStudentServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BufferedReader br = new BufferedReader(new FileReader("D:\\Documents\\Documents" +
"\\ProgramProjects\\javaee\\reqresp_test\\file\\info.txt"));
String line;
ArrayList<Student> students = new ArrayList<>();
while ((line = br.readLine()) != null){
String[] arr = line.split(",");
Student stu = new Student();
stu.setName(arr[0]);
stu.setAClass(Integer.parseInt(arr[1]));
stu.setScore(Double.parseDouble(arr[2]));
students.add(stu);
}
resp.setContentType("text/html;charset=utf-8");
PrintWriter pw = resp.getWriter();
for (Student stu : students) {
pw.write(stu.getName()+","+stu.getAClass()+","+stu.getScore());
pw.write("<br>");
}
}
}
最后到浏览器测试。