Request&Response
1. 请求对象
2.1 请求对象介绍
-
请求:获取资源。在 BS 架构中,就是客户端浏览器向服务器端发出询问,请求获取资源
-
请求对象:就是在项目当中用于发送请求的对象
-
我们常用的对象就是ServletRequest和HttpServletRequest,它们的区别就是是否和HTTP协议有关
2.2 常用方法介绍
2.3 请求对象的使用示例
2.3.1 请求对象常用方法1-获取各种路径
-
获取路径方法
- getQueryString,获取请求的数据,比如url问号后边的数据
- URI是以一种抽象的,高层次概念定义统一资源标识,而URL则是具体的资源标识的方式
- URI比URL范围大,URL是URI的一种
- url?后边的请求参数,也可以叫做查询字符串 (请求动作是查询,把请求参数作为查询关键字)
- 重点方法:getContextPath,getQueryString,getRequestURI
-
案例:新建项目request_demo,新建类com.itheima.servlet.ServletDemo01
-
这里的项目需要是java ee 8 ,我们要用注解注册Servlet
package 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("/servletDemo01")
public class ServletDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取虚拟目录名称 getContextPath()
String contextPath = req.getContextPath(); ***
///getServletContext().getContextPath();
System.out.println(contextPath);
//2.获取Servlet映射路径 getServletPath()
String servletPath = req.getServletPath();
System.out.println(servletPath);
//3.获取访问者ip getRemoteAddr()
String ip = req.getRemoteAddr();
System.out.println(ip);
//4.获取请求消息的数据 getQueryString() url?后边的字符串(查询字符串)
// xxxurl?keyword=abc (?后边的参数,很多时候都是作为查询条件,keyword=abc叫做查询字符串)
String queryString = req.getQueryString();
System.out.println(queryString);
//5.获取统一资源标识符 getRequestURI() /request/servletDemo01 共和国
String requestURI = req.getRequestURI();
System.out.println(requestURI);
//6.获取统一资源定位符 getRequestURL() http://localhost:8080/request/servletDemo01 中华人民共和国
StringBuffer requestURL = req.getRequestURL();
System.out.println(requestURL);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
- 访问
2.3.2 请求对象常用方法2-获取请求头信息
-
获取请求头
- 有的请求头是有多个值,所以可以通过getHeaders方法获取
- 比如:accept-encoding,他的值就有多个:gzip,deflate,br
-
案例:新建ServletDemo02
package 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;
/*
获取请求头信息的相关方法
*/
@WebServlet("/servletDemo02")
public class ServletDemo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.根据请求头名称获取一个值
String connection = req.getHeader("connection");
System.out.println(connection);
System.out.println("--------------");
//2.根据请求头名称获取多个值
Enumeration<String> values = req.getHeaders("accept-encoding");
while(values.hasMoreElements()) {
String value = values.nextElement();
System.out.println(value);
}
System.out.println("--------------");
//3.获取所有的请求头名称
Enumeration<String> names = req.getHeaderNames();
while(names.hasMoreElements()) {
String name = names.nextElement();
String value = req.getHeader(name);
System.out.println(name + "," + value);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
- 访问
2.3.3 请求对象常用方法3-获取请求参数(非常重要)
1)获取请求参数信息的方法 ***
- 获取请求头参数
-
如果请求参数只有一个值,通过getParameter获取,比如:text
-
如果请求参数有多个值,通过getParameterValues获取,比如:checkbox
-
重点掌握:getParameter,getParameterMap
-
案例:新建web/register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="/request/servletDemo03" 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">游戏 <br>
<button type="submit">注册</button>
</form>
</body>
</html>
- 新建ServletDemo03
package 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;
import java.util.Map;
/*
获取请求参数信息的相关方法
*/
@WebServlet("/servletDemo03")
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.根据名称获取数据 getParameter()
String username = req.getParameter("username");
System.out.println(username);
String password = req.getParameter("password");
System.out.println(password);
System.out.println("--------------------");
//2.根据名称获取所有数据 getParameterValues()
String[] hobbies = req.getParameterValues("hobby");
for(String hobby : hobbies) {
System.out.println(hobby);
}
System.out.println("--------------------");
//3.获取所有名称 getParameterNames()
Enumeration<String> names = req.getParameterNames();
while(names.hasMoreElements()) {
String name = names.nextElement();
System.out.println(name);
}
System.out.println("--------------------");
//4.获取所有参数的键值对 getParameterMap()
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.print(value + " ");
}
System.out.println();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
- 访问
2)获取参数手动封装对象
-
我们通过上面的方法可以获取到请求参数,但是如果参数过多,在进行传递时,方法的形参定义将会变得非常难看
-
此时我们应该用一个对象来描述这些参数,它就是实体类。这种类的定义,从基础阶段我们就开始使用了
-
在基础阶段,我们做过一个学生管理系统,用到了一个Student的类,它就是用于描述一个学生的实体类
-
我们现在要做的就是把表单中提交过来的数据填充到实体类中
-
案例:新建com.itheima.bean.Student
package bean; import java.util.Arrays; 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) + '}'; } }
-
新建ServletDemo04
package servlet; import com.itheima.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.io.IOException; /* 封装对象-手动方式 */ @WebServlet("/servletDemo04") public class ServletDemo04 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"); //2.封装学生对象 Student stu = new Student(username,password,hobbies); //3.输出对象 System.out.println(stu); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
修改register.html
<form action="/request/servletDemo04">
-
访问
3) 获取参数反射封装对象
-
问题:如果获取的参数有10个,那么就需要写十次
String xx= req.getParameter("xx");
,这样太麻烦 -
那我们就可以采用反射封装对象方式
-
此种封装的使用要求是,表单
<input>
标签的name属性取值,必须和实体类中定义的属性名称一致 -
案例:新建ServletDemo05
package servlet; import 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.beans.PropertyDescriptor; import java.io.IOException; import java.lang.reflect.Method; import java.util.Map; /* 封装对象-反射方式(使用反射+内省实现数据模型的封装) * 内省:是sun公司推出的一套简化反射操作的规范。把javabean中的属性都封装成一个属性描述器。 * 属性描述器中会有字段信息,get和set方法(取值或存值) */ @WebServlet("/servletDemo05") public class ServletDemo05 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.获取所有的数据 Map<String, String[]> map = req.getParameterMap(); //2.封装学生对象 Student stu = new Student(); //2.1遍历集合 for(String name : map.keySet()) {//username String[] value = map.get(name); try { //2.2获取Student对象的属性描述器 //构造函数的第一个参数决定是哪个属性的描述器。第二个参数是当前属性所属对象的字节码 PropertyDescriptor pd = new PropertyDescriptor(name,stu.getClass()); //2.3获取对应的setXxx方法 Method writeMethod = pd.getWriteMethod(); // setUsername() //2.4执行方法 if(value.length > 1) { writeMethod.invoke(stu,(Object)value);//如果是多个属性值(爱好),需要转换为object // stu.setUsername(value) }else { writeMethod.invoke(stu,value);//通过当前属性描述器的写入方法,将value写入stu对象的这个属性身上 } } catch (Exception e) { e.printStackTrace(); } } //3.输出对象 System.out.println(stu); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
修改register.html
<form action="/request/servletDemo05" >
-
访问
4) 获取请求参数工具类封装 ***
-
上述的操作还是都太麻烦,能不能直接调用现成的工具呢?
-
可以,使用apache的beanutils包
-
copy jar包
“beanutils的jar包”,copy到“项目/web/WEB-INF/libs”下
然后添加到项目引用库
-
案例:新建ServletDemo06
package servlet; import bean.Student; 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; /* 封装对象-工具类方式 */ @WebServlet("/servletDemo06") public class ServletDemo06 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.获取所有的数据 Map<String, String[]> map = req.getParameterMap(); //2.封装学生对象 Student stu = new Student(); try { BeanUtils.populate(stu,map);//将map中的数据,封装到stu对象中(就这么一句话就搞定了) } catch (Exception e) { e.printStackTrace(); } //3.输出对象 System.out.println(stu); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
修改register.html
<form action="/request/servletDemo06">
-
访问 : 通过register.html点击注册之后,跳转过去发现报错
-
报错信息说是,找不到BeanUtils
-
我们刚刚不是添加到项目引用库中了么?注意我们现在的项目是需要部署到tomcat的,项目中有了,但是部署到tomcat的war包中还没有,所以需要特殊处理一下:
-
File - Project Structure
-
-
访问
2.3.4 用流的形式读取请求信息 ***
- 除了上述方式可以获取请求信息,还可以通过流来获取
-
BufferedReader了解即可
-
ServletInputStream常用于获取图片,文件 ***
-
案例:新建ServletDemo07
package servlet; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; 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("/servletDemo07") public class ServletDemo07 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();//这个流是从req对象中获取的,不是自己创建的,所以不需要我们关闭 //字节流 (应用场景,请求参数中有二进制文件(图片)时) 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)); } //is.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
修改 register.html
<!--如果是字符流,post请求方式--> <form action="/request/servletDemo07" method="post">
-
访问
- 流方式获取到的中文,会进行编码,所以看到的姓名是编码后的
2.3.5请求正文中文编码问题
- GET 方式
- 没有乱码问题。在 Tomcat 8.5 版本后已经解决
- POST 方式
- 有乱码问题。可以通过 setCharacterEncoding() 方法来解决
1)POST方式请求 ***
-
案例:ServletDemo08
package 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("/servletDemo08") public class ServletDemo08 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username"); System.out.println(username); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
修改register.html
<form action="/request/servletDemo08" method="post" >
-
访问
-
解决乱码,修改
//设置编码格式 req.setCharacterEncoding("UTF-8");
2)GET方式请求 (了解)
GET方式请求的正文是在地址栏中,在Tomcat8.5版本及以后,Tomcat服务器已经帮我们解决了,所以不会有乱码问题了。
而如果我们使用的不是Tomcat服务器,或者Tomcat的版本是8.5以前,那么GET方式仍然会有乱码问题,解决方式如下:(以下代码了解即可,因为我们现在使用的是Tomcat9.xx版本)
/**
* 在Servlet的doGet方法中添加如下代码
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* GET方式:正文在地址栏
* username=%D5%C5%C8%FD
* %D5%C5%C8%FD是已经被编过一次码了
*
* 解决办法:
* 使用正确的码表对已经编过码的数据进行解码。
* 就是把取出的内容转成一个字节数组,但是要使用正确的码表。(ISO-8859-1)
* 再使用正确的码表进行编码
* 把字节数组再转成一个字符串,需要使用正确的码表,是看浏览器当时用的是什么码表
*/
String username = request.getParameter("username");
byte[] by = username.getBytes("ISO-8859-1");
username = new String(by,"GBK");
//输出到浏览器:注意响应的乱码问题已经解决了
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write(username);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
2.3.6 请求域 ***
- 请求到ServletA,实现的功能,需要用到ServletB,那这时候就可以在ServletA将请求转发到ServletB
- 那ServletB中可能会用到ServletA中的数据,那这时候就涉及到数据共享了
- 在这里如果使用应用域来共享数据,就有点浪费了,因为我们只是在这次请求中需要共享数据,不是整个应用
- 所以就用到了请求域
2.3.7 请求转发 ***
-
请求转发:客户端的一次请求到达后,发现需要借助其他 Servlet 来实现功能
-
特点:
-
浏览器地址栏不变
-
域对象中的数据不丢失
-
负责转发的 Servlet 转发前后的响应正文会丢失 (ServletA的响应正文会丢失,由ServletB响应客户端)
-
由转发的目的地来响应客户端
-
- 应用场景:如果一次请求中这个servlet无法单独完成任务,这时会将这个请求转发给另一个servlet
- 如果这两个servlet需要共享数据,我们一般使用请求域
- 请求转发,不会丢失请求域数据,所以共享数据,都使用请求域
- 请求转发API
-
dispatcher:[dɪˈspætʃə®].调度
-
forward:向前,(按新地址)转寄; 发送
-
getRequestDispatcher(‘转发的url’)
-
案例:新建ServletDemo09
package 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; /* 请求转发 */ @WebServlet("/servletDemo09") public class ServletDemo09 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置共享数据 req.setAttribute("encoding","gbk"); //获取请求调度对象 RequestDispatcher rd = req.getRequestDispatcher("/servletDemo10"); //实现转发功能 rd.forward(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
新建ServletDemo10
package 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("/servletDemo10") public class ServletDemo10 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("servletDemo10执行了..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
访问
2. 响应对象
2.1 响应对象介绍
2.1.1 关于响应
-
响应:回馈结果。在 BS 架构中,就是服务器给客户端浏览器反馈结果
-
响应对象:就是在项目中用于发送响应的对象
-
响应对象也是是Servlet规范中定义的,它包括了协议无关的和协议相关的
-
协议无关的对象标准是:ServletResponse接口
-
协议相关的对象标准是:HttpServletResponse接口
-
-
我们课程中涉及的响应对象都是和HTTP协议相关的。即使用的是HttpServletResponse接口的实现类
2.1.2 常见状态码 ***
-
405:请求方式不支持,例如:一般是后台只支持post请求,而发起的请求确实get请求,这时候就会提示405
-
状态码首位含义:
状态码 说明 1xx 消息 2xx 成功 3xx 重定向 4xx 客户端错误 5xx 服务器错误
2.2 常用方法介绍
- 在HttpServletResponse接口中提供了很多方法,接下来我们通过API文档,来了解一下这些方法
- 先了解即可,接下来我们会一步一步介绍
2.3 响应对象的使用示例
2.3.1 字节流响应消息&解决乱码
- 字节流响应API
-
字节流,常用于处理图片,文件
-
新建项目:response_demo,设置虚拟路径/response
-
新建类:servlet.ServletDemo01
package 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; /* 字节流响应消息及乱码的解决 */ @WebServlet("/servletDemo01") public class ServletDemo01 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { /* 项目中常用的编码格式是u8,而浏览器默认使用的编码是gbk。导致乱码! */ //1. 获取字节流输出对象 ServletOutputStream sos = resp.getOutputStream(); //2. 定义一个消息 String str = "你好"; //通过字节流对象输出 sos.write(str.getBytes());//getBytes不带参数,默认获取操作系统的编码集(我们安装的都是中文windows,所以系统编码集是gbk) //sos.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
访问
-
谷歌浏览器,没有乱码(这是因为谷歌浏览器的默认编码是gbk)
-
但是谷歌浏览器想查看编码,需要安装插件,所以我们再来看ie(发现默认是gb2312(gbk))
-
-
但其实,我们的代码都应该是utf-8编码格式,只不过是getBytes默认处理了,才没有出现乱码
- 代码格式是utf-8,但是到浏览器gbk上为啥不是乱码,这是因为getBytes方法默认处理了,getBytes默认与操作系统的编码格式一致,而操作系统编码是gbk,所以getBytes处理成gbk了
-
那如何出现乱码呢?修改代码:
sos.write(str.getBytes("UTF-8"));
-
访问
-
解决乱码
/* 项目中常用的编码格式是u8,而浏览器默认使用的编码是gbk。导致乱码! 解决方式一:修改浏览器的编码格式(不推荐,不能让用户做修改的动作) 解决方式二:通过输出流写出一个标签:<meta http-equiv='content-type' content='text/html;charset=UTF-8'> 解决方式三:response.setHeader("Content-Type","text/html;charset=UTF-8"); 指定响应头信息 解决方式四:response.setContentType("text/html;charset=UTF-8"); */ //sos.write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'>".getBytes()); //resp.setHeader("Content-Type","text/html;charset=UTF-8"); resp.setContentType("text/html;charset=UTF-8");//常用第四种,告知浏览器采用utf-8编码方式
-
访问
2.3.2 字符流响应消息&解决乱码 ***
- 字符流API
-
字符流,常用于处理文字
-
案例:新建ServletDemo02
package 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.io.PrintWriter; /* 字符流响应消息及乱码的解决 */ @WebServlet("/servletDemo02") public class ServletDemo02 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1. 准备一个消息 String str = "你好"; //2. 获取字符流对象 PrintWriter pw = resp.getWriter(); //3. 通过字符流输出 pw.write(str); //pw.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
访问
-
解决
//解决中文乱码 resp.setContentType("text/html;charset=UTF-8");
-
访问
-
解决乱码总结
-
如果是响应解决乱码问题,一般思路就是让浏览器与代码中的编码风格保持一致(utf-8)
resp.setContentType("text/html;charset=UTF-8");
-
如果请求中解决乱码,将gbk编码的中文信息转换为utf-8
//设置编码格式 (将浏览器默认编码gbk的中文信息,转换为utf-8的信息) req.setCharacterEncoding("UTF-8");
-
2.3.3 响应图片
-
创建字节输入流对象,关联读取的图片路径
-
通过响应对象获取字节输出流对象
-
循环读取和写出图片
-
案例:新建ServletDemo03
package 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; /* 响应图片到浏览器 */ @WebServlet("/servletDemo03") public class ServletDemo03 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1. 创建字节输入流对象,关联图片路径 String realPath = getServletContext().getRealPath("/img/yasuo.png"); System.out.println(realPath); BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath)); //注意:不能直接指定图片路径,需要同getRealPath获取,因为项目还要发布,我们需要获取到发布之后的图片路径 //BufferedInputStream bis = new BufferedInputStream(new FileInputStream(/img/yasuo.png)); //2. 获取字节输出流对象 ServletOutputStream sos = resp.getOutputStream(); //3. 循环读写 byte[] arr = new byte[1024]; int len; while((len = bis.read(arr)) != -1) { sos.write(arr,0,len); } bis.close(); sos.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
2.3.4 设置缓存时间
-
缓存:对于不经常变化的数据,我们可以设置合理缓存时间,以避免浏览器频繁请求服务器。以此来提高效率
-
缓存API
-
案例:新建ServletDemo04
package 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("/servletDemo04") public class ServletDemo04 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String news = "这是一条很火爆的新闻~~"; //设置缓存时间:1小时的缓存时间 //参数1:Expires : 失效的意思 //参数2:当前时间+1小时毫秒值(意思就是在1小时之后过期) resp.setDateHeader("Expires",(System.currentTimeMillis()+1*60*60*1000L)); //设置编码格式 resp.setContentType("text/html;charset=UTF-8"); //写出数据 resp.getWriter().write(news); System.out.println("aaa"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
第一次访问:发现执行了Servlet,并且打印了aaa
-
此时,这个Servlet已经被缓存下来了
-
第二次访问
- 控制台没有打印aaa
2.3.5 设置定时刷新
-
定时刷新:过了指定时间后,页面自动进行跳转
-
定时刷新API
- 案例:新建ServletDemo05
package 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("/servletDemo05") public class ServletDemo05 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String news = "您的用户名或密码错误,3秒后自动跳转到登录页面..."; //设置编码格式 resp.setContentType("text/html;charset=UTF-8"); //写出数据 resp.getWriter().write(news); //设置响应消息头定时刷新 //Refresh:刷新 //第二个参数第一部分:3,3设之后 //第二个参数第二部分:跳转到哪个路径 //resp.setHeader("Refresh","3;URL=/response/login.html"); resp.setHeader("Refresh","3;URL="+req.getContextPath()+"/login.html"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
将之前的login.html复制到这个项目:web/login.html
-
访问:3s之后自动跳转到login.html
2.3.6 请求重定向 ***
-
案例:新建ServletDemo06
package 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("/servletDemo06") public class ServletDemo06 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置请求域数据 req.setAttribute("username","zhangsan"); //设置重定向 resp.sendRedirect(req.getContextPath() + "/servletDemo07"); //resp.sendRedirect("/response/servletDemo07"); // resp.sendRedirect("https://www.baidu.com"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
新建ServletDemo07,获取共享数据
package 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("/servletDemo07") public class ServletDemo07 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servletDemo07执行了..."); Object username = req.getAttribute("username");//获取不到 System.out.println(username); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
访问:无法获取共享数据打印null
-
重定向与转发的区别
-
重定向:
- 两次请求
- 地址栏发生变化
- 不可以使用request域共享数据 (既然是两次请求,那肯定不能使用请求域中共享的数据)
- 可以重定向到其他服务器的url
-
转发:
- 一次请求
- 地址栏不发生变化
- 可以使用request域共享数据
- 只能转发到自己服务器内部的url
2.3.7 响应对象-文件下载
-
案例:新建ServletDemo08
package 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; /* 文件下载 */ @WebServlet("/servletDemo08") public class ServletDemo08 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.创建字节输入流,关联读取的文件 //获取文件的绝对路径 String realPath = getServletContext().getRealPath("/img/yasuo.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.setHeader("Content-Disposition","attachment;filename=hm.png"); //4.获取字节输出流对象 ServletOutputStream sos = resp.getOutputStream(); //5.循环读写文件 byte[] arr = new byte[1024]; int len; while((len = bis.read(arr)) != -1) { sos.write(arr,0,len); } //6.释放资源 bis.close(); //sos.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
访问
3 案例-学生管理系统
3.1 案例效果演示
3.2 资源准备
-
创建一个 web 项目 : reqresp_test,虚拟目录/stu
-
在 web 目录下创建一个 index.html,包含两个超链接标签(添加学生、查看学生)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>学生管理系统首页</title> </head> <body> <a href="/stu/addStudent.html">添加学生</a> <a href="/stu/listStudentServlet">查看学生</a> </body> </html>
-
在 web 目录下创建一个 addStudent.html,用于实现添加功能的表单页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加学生</title> </head> <body> <form action="/stu/addStudentServlet" method="get" autocomplete="off"> 学生姓名:<input type="text" name="username"> <br> 学生年龄:<input type="number" name="age"> <br> 学生成绩:<input type="number" name="score"> <br> <button type="submit">保存</button> </form> </body> </html>
-
在 src 下创建一个 bean.Student 类,用于封装学生信息
package bean; public class Student { private String username; private int age; private int score; public Student() { } public Student(String username, int age, int score) { this.username = username; this.age = age; this.score = score; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } }
3.3 添加学生功能
-
实现步骤
- 创建 AddStudentServlet 类。继承 HttpServlet
- 重写 doGet 和 doPost 方法
- 获取表单中的数据
- 将获取到的数据封装成 Student 对象
- 将 Student 对象中的数据保存到 d:\stu.txt 文件中
- 通过定时刷新功能完成对浏览器的响应
-
新建:AddStudentServlet
package servlet; import 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.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; /* 实现添加功能 */ @WebServlet("/addStudentServlet") public class AddStudentServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.获取表单中的数据 String username = req.getParameter("username"); String age = req.getParameter("age"); String score = req.getParameter("score"); //2.创建学生对象并赋值 Student stu = new Student(); stu.setUsername(username); stu.setAge(Integer.parseInt(age)); stu.setScore(Integer.parseInt(score)); //3.将学生对象的数据保存到d:\\stu.txt文件中 BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\stu.txt",true)); bw.write(stu.getUsername() + "," + stu.getAge() + "," + stu.getScore()); bw.newLine(); bw.close(); //4.通过定时刷新功能响应给浏览器 resp.setContentType("text/html;charset=UTF-8"); resp.getWriter().write("添加成功。2秒后自动跳转到首页..."); resp.setHeader("Refresh","2;URL=/stu/index.html"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
访问
3.4 查看学生功能
-
实现步骤
- 创建 ListStudentServlet 类。继承 HttpServlet
- 重写 doGet 和 doPost 方法
- 通过字符输入流读取 d:\stu.txt 文件中的数据
- 将读到的数据封装到 Student 对象中
- 将多个 Student 对象保存到集合中
- 遍历集合,将数据响应到浏览器
-
新建ListStudentServlet
package servlet; import 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.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; /* 实现查看功能 */ @WebServlet("/listStudentServlet") public class ListStudentServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.创建字符输入流对象,关联读取的文件 BufferedReader br = new BufferedReader(new FileReader("d:\\stu.txt")); //2.创建集合对象,用于保存Student对象 ArrayList<Student> list = new ArrayList<>(); //3.循环读取文件中的数据,将数据封装到Student对象中。再把多个学生对象添加到集合中 String line; while((line = br.readLine()) != null) { //张三,23,99 Student stu = new Student(); String[] arr = line.split(","); stu.setUsername(arr[0]); stu.setAge(Integer.parseInt(arr[1])); stu.setScore(Integer.parseInt(arr[2])); list.add(stu); } //4.遍历集合,将数据响应给浏览器 resp.setContentType("text/html;charset=UTF-8"); //获取输出流对象 PrintWriter pw = resp.getWriter(); for(Student s : list) { pw.write(s.getUsername() + "," + s.getAge() + "," + s.getScore()); pw.write("<br>"); } // 注意:其实这个代码写复杂了。文本中的数据,与最终输出到浏览器的数据一模一样 // 所以完全可以读出来的line,通过pw直接写即可,不需要封装List<student> } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
-
访问