目录
🐲 2. HttpServletRequest HTTP请求
🦄 2.1 打印请求信息(创建 ShowRequest 类)
🦄 2.2 获取 GET 请求中的参数(创建 GetParameter 类)
🦄 2.3 获取 POST 请求中的参数(创建 PostParameter 类)
2.3.1 请求的 body 是 x-www-form-urlencoded form表单
🐲 3. HttpServletResponse HTTP响应
🦄 3.1 设置状态码(创建 StatusServlet 类)
🦄 3.2 自动刷新(创建 AutoRefreshServlet 类)
🦄 3.3 重定向(创建 RedirectServlet 类)
| 方法名称 | 调用时机 |
|---|---|
| init | 在 HttpServlet 实例化之后被调用一次 |
| destroy | 在 HttpServlet实例不再使用的时候调用一次 |
| service | 收到HTTP请求的时候调用 |
| doGet | 收到GET请求的时候调用(由 service 方法调用) |
| doPost | 收到Post请求的时候调用(由 service 方法调用) |
| doPut / doDelete / doOptions / ... | 收到其他请求的时候调用(由 service 方法调用) |
需要注意开发常用的是 下面这几个 (doGet / doPost / doPut / .....)
而面试考的是上面这三个(init / destroy / service)
(1) init 创建出 HttpServlet 实例会调用一次
init 方法的作用,就是用来初始化 ,也就是首次被访问到的时候会被实例化
(2) destroy 在 HttpServlet 实例不再使用的时候调用一次,
这个不一定真的能调用到
a) 杀进程,比如cmd直接点 x 或是通过任务管理器结束任务 或是idea关闭运行状态
此时destroy无法被调用到(相当于直接拔电源)
b) 通过8005端口,用来控制tomcat的,通过这个端口给tomcat发送一个关闭操作,这个时候tomcat就可以正常关闭,就可以调用到destroy(相当于正常关闭电脑)
(3) service 收到HTTP请求的时候调用
tomcat 收到请求,实际上是先调用 service
在 service 中再去根据方法,调用不同的 doxxx
但在实际开发中,很少会重写 service 就是直接重写 doxxx 就可以了
面试题:谈谈 Servlet 的生命周期
先理解什么叫生命周期:就是什么阶段,干什么事
可以这样回答,在 Servlet中有三个主要的方法
init 在 Servlet 被实例化时,调用一次,用来初始化
destroy 在 Servlet 被销毁之前,调用一次
service 每次收到请求调用一次
| 方法 | 描述 |
|---|---|
| String getProtocol() | 返回请求协议的名称和版本,例如 HTTP/1.1 |
| String getMethod() | 返回请求的HTTP方法的名称,例如 GET/POST/PUT |
| String getRequestURI() | 从协议名称直到HTTP请求的第一行的查询字符串中,返回该请求的URL的一部分 |
| String getContextPath() | 返回指示请求上下文的请求URI部分 URL两层 context path servlet path 这个方法就是返回 context path部分的 |
| String getQueryString() | 返回包含在路径后的请求 URL 中的查询字符串 ?studentId=10&classId=1 |
| Enumeration getParameterNames() | 返回一个 String 对象的枚举,包含在该请求中包含的参数的名称 |
| String getParameter(String name) | 以字符串形式返回请求参数的值,或者如果参数不存在则返回null |
| String[] getParameterValues(String name) | 返回一个字符串对象的数组,包含所有给定的请求参数的值(返回多个value),如果参数不存在则返回null ?score=1&score=2&score=3 使用这个方法返回数组[1,2,3] |
| Enumeration getHeaderNames() | 返回一个枚举,包含在该请求中包含的所有的头名(key) |
| String getHeader(String name) | 以字符串形式返回指定的请求头的值(value) |
| String getCharacterEncoding() | 返回请求主体中使用的字符编码的名称 |
| String getContentType() | 返回请求主体的 MIME 类型,如果不知道类型则返回null |
| int getContentLength() | 以字节为单位返回请求主体的长度,并提供输入流,或者如果长度未知则返回-1 |
| InputStream getInputStream() | 用于读取请求的 body 内容,返回一个 InputStream 对象 |
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.util.Enumeration;
-
- @WebServlet("/showRequest")
- public class ShowRequestServlet extends HelloServlet{
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- StringBuilder sb = new StringBuilder();
- //查看协议的名称和版本
- sb.append(req.getProtocol());
- sb.append("\n");
- //查看HTTP方法的名称
- sb.append(req.getMethod());
- sb.append("\n");
- //从协议名称直到HTTP请求的第一行的查询字符串中,返回该请求的URL的一部分
- sb.append(req.getRequestURI());
- sb.append("\n");
- //返回ContextPath部分的路径
- sb.append(req.getContextPath());
- sb.append("\n");
- //返回请求URL查询的字符串
- sb.append(req.getQueryString());
- sb.append("\n");
-
- //把请求的 header 也拼进来
- Enumeration
headerNames = req.getHeaderNames(); - while(headerNames.hasMoreElements()) {
- String name = headerNames.nextElement();
- String value = req.getHeader(name);
- sb.append(name + ": " + value);
- sb.append("\n");
- }
-
- resp.getWriter().write(sb.toString());
-
- }
- }

如果不写context type,此时浏览器就不知道这里的body应该是啥格式,就会导致不一定是你想要的那种格式
所以要注意要写上context type让浏览器知道你想要什么格式,
格式有很多 text/plain text/html text/css application/js application.json image/png
我这里写一个 text/html (纯文本的格式),浏览器就知道了(而下面写的\n就会失效了)


写什么context type 就要对应什么格式

完整代码:
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.util.Enumeration;
-
- @WebServlet("/showRequest")
- public class ShowRequestServlet extends HelloServlet{
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //resp 是响应对象 setContextType,给响应的 ContextType设置了值:html
- //声明响应 body 是 html结构的数据
- resp.setContentType("text/html");
-
- StringBuilder sb = new StringBuilder();
- //查看协议的名称和版本
- sb.append(req.getProtocol());
- sb.append("
"); - //查看HTTP方法的名称
- sb.append(req.getMethod());
- sb.append("
"); - //返回请求的URL的一部分
- sb.append(req.getRequestURI());
- sb.append("
"); - //返回ContextPath部分的路径
- sb.append(req.getContextPath());
- sb.append("
"); - //返回请求URL查询的字符串
- sb.append(req.getQueryString());
- sb.append("
"); -
- //把请求的 header 也拼进来
- Enumeration
headerNames = req.getHeaderNames(); - while(headerNames.hasMoreElements()) {
- String name = headerNames.nextElement();
- String value = req.getHeader(name);
- sb.append(name + ": " + value);
- sb.append("
"); - }
-
- resp.getWriter().write(sb.toString());
-
- }
- }
使用 api 获取到请求中的重要参数(query string 中的值 , 以及 body 中的值)
比如: https://v.bitedu.vip/personInf/student?studentId=666&classId=888
GET请求中的参数通过query string传递给服务器,
如果此时浏览器通过 query string给服务器传递了两个参数,
studentId 和 classId 值分别是 666 和 二班,
在服务器端就可以通过 getParameter来获取到参数的值
- 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("/getParameter")
- public class GetParameterServlet extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //获取 query string 中的键值对
- // 浏览器的请求比如: ?studentId=666&classId=二班
- String studentId = req.getParameter("studentId");
- String classId = req.getParameter("classId");
- System.out.println(studentId);
- System.out.println(classId);
-
- resp.getWriter().write(studentId + ", " + classId);
- }
- }
浏览器没有正确识别出中文,不是没有urlencode的原因(服务器这里的打印结果是正确的,说明urlencode没有,也不是肯定会出错的)
而是返回响应的页面,浏览器没有正确识别
为了能够让浏览器能够正确识别,需要显示在响应中加上 context type

重新运行,

特殊情况
getParameter 获取键值对时
如果键不存在,得到的就是null
如果键存在,值不存在,得到的就是 " "


完整代码
- 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("/getParameter")
- public class GetParameterServlet extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //获取 query string 中的键值对
- // 浏览器的请求比如: ?studentId=666&classId=二班
- String studentId = req.getParameter("studentId");
- String classId = req.getParameter("classId");
- System.out.println(studentId);
- System.out.println(classId);
-
- resp.setContentType("text/html; charset=utf8");
- resp.getWriter().write(studentId + ", " + classId);
- }
- }
如果请求是 post,获取到 body 中的参数
也是可以通过 getParameter获取参数的值
- 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("/getParameter")
- public class GetParameterServlet extends HttpServlet {
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- // 通过 body 获取,发送 post 请求
- // 请求的 body 比如: studentId=666&classId=二班
- String studentId = req.getParameter("studentId");
- String classId = req.getParameter("classId");
- System.out.println(studentId);
- System.out.println(classId);
-
- resp.setContentType("text/html; charset=utf8");
- resp.getWriter().write(studentId + ", " + classId);
- }
- }
需要注意的doPost中写的代码和前面doGet中写的代码是一样的,
但是发送请求的方式是不一样的,之前doGet发送请求是直接在浏览器上面输入的
而doPost不能这样直接发送请求,
a) 可以使用postman来给body中写内容发送请求
如果对于postman还不了解,大家可以看我这一篇博客里面有很好的讲解
b) 还可以写一个html通过form发送请求
- 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>Documenttitle>
- head>
- <body>
-
- <form action="getParameter" method="post">
- <input type="text" name="studentId">
- <input type="text" name="classId">
- <input type="submit" value="提交">
- form>
-
-
- body>
- html>
运行程序,用fiddler抓包可以看到
当前发现响应的页面乱码了,但是我们代码中不是设置了吗,怎么还会乱码
需要注意的是,我们设置的是响应 ,并且服务器收到的也是一个乱码,
说明服务器针对请求的解析就已经乱了(也就是servlet不知道你传过来的是哪种编码方式),所以需要显示的告诉servlet按照哪种方式编码,来理解请求的body
所以我们就需要给请求这里也加上对于编码方式的声明
运行程序可以看到
最终代码
- 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("/getParameter")
- public class GetParameterServlet extends HttpServlet {
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- // 通过 body 获取,发送 post 请求
- // 请求的 body 比如: studentId=666&classId=二班
- req.setCharacterEncoding("utf8");
- String studentId = req.getParameter("studentId");
- String classId = req.getParameter("classId");
- System.out.println(studentId);
- System.out.println(classId);
-
- //响应这里设置字符集有两种写法,但是还是建议使用 setContentType 完整写法
- //设置的字符集只是一小部分,还需要设置格式
- resp.setContentType("text/html; charset=utf8");
- resp.getWriter().write(studentId + ", " + classId);
- }
- }
{
studentId: 666,
classId: "二班"
}
要想获取json中的数据,就要先读取body中的内容,可以使用getInputStream进一步的再来读取流对象
这个随便选
复制这个

-
com.fasterxml.jackson.core -
jackson-databind -
2.13.4.1
jackson 提供"一个类两个方法"
一个类 : ObjectMapper
一个方法: readValue,把json格式的数据转成 java 的对象
另一个方法: writeValueAsString,把 java 对象转成 json 格式的字符串
完整代码
- import com.fasterxml.jackson.databind.ObjectMapper;
-
- 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.ArrayList;
-
- class Student {
- //1.这个类的属性必须是public或者带有public的getter/setter
- // 否则jackson无法访问到这个对象的属性
- //2.这个类必须要有无参版本的构造方法
- public int studentId;
- public String classId;
- }
-
- @WebServlet("/jsonServlet")
- public class JsonServlet extends HttpServlet {
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- // 请求的 body 格式为
- // { studentId: 666, classId: "二班" }
- ObjectMapper objectMapper = new ObjectMapper();
- //readValue,第一个参数可以是字符串也可以是输入流
- //第二个参数是一个类对象
- Student s = objectMapper.readValue(req.getInputStream(),Student.class);
- System.out.println(s.studentId);
- System.out.println(s.classId);
-
- // resp.setContentType("text/html; charset=utf8");
- // resp.getWriter().write(s.studentId + ", " + s.classId);
- resp.setContentType("application/json; charset=utf8");
- resp.getWriter().write(objectMapper.writeValueAsString(s));
- }
- }
使用postman来给body发送数据

如果是更复杂的json,那就是 "套娃"
- import com.fasterxml.jackson.databind.ObjectMapper;
-
- 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.ArrayList;
-
- class Score {
- public ArrayList
scores; - }
- class Student {
- //1.这个类的属性必须是public或者带有public的getter/setter
- // 否则jackson无法访问到这个对象的属性
- //2.这个类必须要有无参版本的构造方法
- public int studentId;
- public String classId;
- public Score score;
- }
- @WebServlet("/jsonServlet")
- public class JsonServlet extends HttpServlet {
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- // 请求的 body 格式为
- // { studentId: 666, classId: "二班" }
- ObjectMapper objectMapper = new ObjectMapper();
- //readValue,第一个参数可以是字符串也可以是输入流
- //第二个参数是一个类对象
- Student s = objectMapper.readValue(req.getInputStream(),Student.class);
- System.out.println(s.studentId);
- System.out.println(s.classId);
-
- // resp.setContentType("text/html; charset=utf8");
- // resp.getWriter().write(s.studentId + ", " + s.classId);
- resp.setContentType("application/json; charset=utf8");
- resp.getWriter().write(objectMapper.writeValueAsString(s));
- }
- }
| 方法 | 描述 |
|---|---|
| void setStaus(int sc) | 为该响应设置状态码 |
| void setHeader(String name, String value) | 设置一个带有给定的名称和值的header,如果name已经存在则覆盖旧的值 |
| void addHeader(String name,String value) | 添加一个带有给定的名称和值的header,如果name已经存在,不覆盖旧的值,并列添加新的键值对(新旧同时存在) |
| void setContentType(String type) | 设置被发送到客户端的响应的内容类型 |
| void setCharacterEncoding(String charset) | 设置被发送到客户端的响应的字符编码(MIME 字符集) 例如,utf8 |
| void sendRedirect(String location) | 使用指定的重定向位置URL,发送临时重定向响应到客户端 |
| PrintWriter getWriter() | 用于往 body 中写入文本格式数据 (HTTP响应式啥样的数据,显示到浏览器上的) |
| OutputStream getOutputStream() | 用于往 body 中写入二进制格式数据 |
- 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("/statusServlet")
- public class StatusServlet extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- //约定, 浏览器 query string 传个参数过来 type
- //如果type=1,就返回200;type=2,就返回404;type=3返回一个500
- String type = req.getParameter("type");
- if(type.equals("1")) {
- resp.setStatus(200);
- }else if(type.equals("2")) {
- resp.setStatus(404);
- }else if(type.equals("3")) {
- resp.setStatus(500);
- }else {
- resp.setStatus(504);
- }
- }
- }
运行程序

抓包查看响应

再看一下 type=2
这个是浏览器默认的404的显示结果


也可以让显示的这个404更好看一些(比如这个里返回一个tomcat的404)


这些状态码具体应该怎么来处理,也就是页面中显示什么,都是属于程序员自定义的
设置响应的 header ,通过这个实现页面自动刷新,
header设置 refresh 属性,值是一个"秒数" 浏览器就会在时间到了之后自动刷新
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
-
- @WebServlet("/autoRefresh")
- public class AutoRefreshServlet extends HelloServlet{
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- // 直接返回响应就可以
- resp.setHeader("refresh", "2");
- resp.getWriter().write(System.currentTimeMillis() + "");
-
- }
- }
每2000ms自动刷新 (但这个并不是精确的,会比2000ms略多一点,是因为执行这个语句还要花费一点时间)

- 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("/redirect")
- public class RedirectServlet extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- // 进行重定向,收到请求,跳转到 百度主页
- // resp.setStatus(302);
- // resp.setHeader("Location","https://www.baidu.com");
- //和上面代码的效果一样
- resp.sendRedirect("https://www.baidu.com");
- }
- }
-
输入这个,按回车,就自动跳转到百度主页上去


