Servlet 详解

news/2024/5/19 5:53:07

1. Servlet

1.1 Servlet 是什么

Servlet 就是 Tomcat 这个 HTTP 服务器提供给 Java 的一组 API,根据 用户不同 /时间不同 /输入的参数不同,来构建动态页面。不必关注 Socket, HTTP协议格式, 多线程并发等技术细节, 降低了 web app 的开发门槛, 提高了开发效率。

1.2 Servlet 主要做的工作

  • 允许程序猿注册一个类, 在 Tomcat 收到某个特定的 HTTP 请求的时候, 执行这个类中的一些代码.
  • 帮助程序猿解析 HTTP 请求, 把 HTTP 请求从一个字符串解析成一个 HttpRequest 对象.
  • 帮助程序猿构造 HTTP 响应. 程序猿只要给指定的 HttpResponse 对象填写一些属性字段, Servlet 就会自动的按照 HTTP 协议的方式构造出一个 HTTP 响应字符串, 并通过 Socket 写回给客户端.

1.3 Servlet 使用演示

1.3.1 创建项目

使用 IDEA 创建一个 Maven 项目.

  1. 菜单 -> 文件 -> 新建项目 -> Maven
    在这里插入图片描述
  2. 选择项目要存放的目录
    在这里插入图片描述
  3. 项目创建完毕
    一般右下角会弹出以下对话框. 选择 Enable Auto-Import。
    在这里插入图片描述

1.3.2 引入依赖

Maven 项目创建完毕后, 会自动生成一个 pom.xml 文件,需要在 pom.xml 中引入 Servlet API 依赖的 jar 包.

  1. 在Maven中央仓库中搜索 “servlet”, 一般第一个结果就是在这里插入图片描述
  2. 选择版本.
    可以在Tomcat查询版本对应关系。查看本地Tomcat版本
    在这里插入图片描述
    使用 Tomcat 8.5, 那么就需要使用 Servlet 3.1.0
    在这里插入图片描述
  3. 把中央仓库中提供的 xml 复制到项目的 pom.xml 中在这里插入图片描述
    修改后的 pom.xml 形如:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>ServletHelloWorld</artifactId><version>1.0-SNAPSHOT</version><dependencies><!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency></dependencies>
</project>

dependencies 标签内部放置项目依赖的 jar 包. maven 会自动下载依赖到本地
groupId: 表示组织名称,artifactId: 表示项目名称,version: 表示版本号中央仓库就是按照这三个字段来确定唯一一个包的. 在这里插入图片描述

1.3.3 创建webapp目录

当项目创建好了之后, IDEA 会帮我们自动创建出一些目录. 形如在这里插入图片描述

  • src 表示源代码所在的目录
  • main/java 表示源代码的根目录. 后续创建 .java 文件就放到这个目录中.
  • main/resources 表示项目的一些资源文件所在的目录. 此处暂时不关注.
  • test/java 表示测试代码的根目录.
  1. 创建 webapp 目录
    在 main 目录下, 和 java 目录并列, 创建一个 webapp 目录。webapp 目录就是未来部署到 Tomcat 中的一个重要的目录. 当前我们可以往 webapp 中放一些静态资源, 比如 html , css 等在这里插入图片描述
  2. 创建 web.xml
    在 webapp 目录内部创建一个 WEB-INF 目录, 并创建一个 web.xml 文件,Tomcat 找到这个文件才能正确处理 webapp 中的动态资源.
    在这里插入图片描述
  3. 编写 web.xml
    往 web.xml 中拷贝以下代码,具体细节暂时不关注
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app><display-name>Archetype Created Web Application</display-name>
</web-app>

1.3.4 编写代码

  1. 在 java 目录中创建一个类 HelloServlet, 继承自 HttpServlet,代码如下:
@WebServlet("/hello")  // 注解, 表示 Tomcat 收到的请求中路径为 /hello 的请求才会调用 HelloServlet 这个类的代码// 这个路径未包含 Context Path
public class HelloServlet extends HttpServlet {@Override    // 重写 doGet 方法 ,这个方法会在 Tomcat 收到 GET 请求时触发protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// doGet 的参数有两个, 分别表示收到的 HTTP 请求 和要构造的 HTTP 响应// HttpServletRequest 表示 HTTP 请求// Tomcat 按照 HTTP 请求的格式把 字符串 格式的请求转成了一个 HttpServletRequest 对象. //后续想获取请求中的信息(方法, url, header, body 等) 都是通过这个对象来获取. // HttpServletResponse 表示 HTTP 响应  代码中把响应对象构造好(构造响应的状态码, header, body 等)System.out.println("hello");// resp.getWriter() 会获取到一个流对象, // 通过这个流对象就可以写入一些数据, 写入的数据会被构造成一个 HTTP 响应的 body 部分// Tomcat 会把整个响应转成字符串, 通过 socket 写回给浏览器.resp.getWriter().write("hello");}
}

代码不是通过 main 方法作为入口了. 可以理解为main 方法已经被包含在 Tomcat 里, 我们写的代码会被 Tomcat 在合适的时机调用起来,接下来会演示。

满足以下三个条件的类才能被调用:
  • 创建的类需要继承自 HttpServlet
    
  • 这个类需要使用 @WebServlet 注解关联上一个 HTTP 的路径
    
  • 这个类需要实现 doXXX 方法.
    

1.3.5 打包程序

使用 maven 进行打包

1.3.5.1 jar包

jar 包是普通的 java 程序打包的结果. 里面会包含一些 .class 文件,war 包是 java web 的程序, 里面除了会包含 .class 文件之外, 还会包含 HTML, CSS, JavaScript, 图片, 以及其他的 jar 包.打成 war 包格式才能被 Tomcat 识别.

  1. 打开 maven 窗口 (一般在 IDEA 右侧就可以看到 Maven 窗口, 如果看不到的话, 可以通过 菜单 -> View -> Tool Window -> Maven 打开)
  2. Lifecycle , 双击 package 即可进行打包在这里插入图片描述
  3. 打包成功
    在这里插入图片描述
    可以看到在 target 目录下, 生成了一个 jar 包
    在这里插入图片描述

ServletHelloWorld-1.0-SNAPSHOT.jar 这个名字相当于把 artifactId 和 version 拼接起来了
在这里插入图片描述

但是 jar 包并不是我们需要的, Tomcat 需要识别的是另外一种 war 包格式

1.3.5.2 war包
  1. 在 pom.xml 中新增一个 packing 标签, 表示打包的方式是打一个 war 包.
<packaging>war</packaging>
  1. 在 pom.xml 中再新增一个 build 标签, 内置一个 finalName 标签, 表示打出的 war 包的名字是HelloServlet
<build><finalName>ServletHelloWorld</finalName>
</build>

完整的pom.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>ServletHelloWorld</artifactId><version>1.0-SNAPSHOT</version><dependencies><!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api 
--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency></dependencies><packaging>war</packaging><build><finalName>ServletHelloWorld</finalName></build>
</project>
  1. 重新使用 maven 打包, 可以看到生成的新的 war 包的结果.
    在这里插入图片描述

1.3.6 部署程序

  1. 把 war 包拷贝到 Tomcat 的 webapps 目录下。(Tomcat的使用)在这里插入图片描述

  2. 启动 Tomcat , Tomcat 就会自动把 war 包解压缩,看到这个日志说明 Tomcat 已经正确识别了 ServletHelloWorld 这个 webapp.
    在这里插入图片描述

1.3.7 验证程序

通过浏览器访问 http://127.0.0.1:8080/ServletHelloWorld/hello
在这里插入图片描述
URL 中的 PATH 分成两个部分, 其中 ServletHelloWorld 为 Context Path, hello 为 Servlet Path
在这里插入图片描述

1.3.7 使用 IDEA 中的 Smart Tomcat 插件部署

1.3.7.1 安装 Smart Tomcat 插件
  1. 菜单 -> 文件 -> Settings
    在这里插入图片描述
  2. Plugins -> Marketplace -> 搜索 “tomcat” -> Install.

在这里插入图片描述

  1. 安装完毕之后, 会提示 “重启 IDEA”
    在这里插入图片描述

1.3.7.2 配置 Smart Tomcat 插件

  1. 点击右上角的 “Add Configuration”
    在这里插入图片描述
  2. 选择左侧的 “Smart Tomcat”

在这里插入图片描述

  1. 配置Tomcat
    在 Name 这一栏填写一个名字(可以随便写),
    在 Tomcat Server 这一栏选择 Tomcat 所在的目录. 其他的选项不必做出修改。
    其中 Context Path 默认填写的值是项目名称
    在这里插入图片描述
  2. 点击 OK
    右上角变成了在这里插入图片描述
    点击绿色的三角号, IDEA 就会自动进行编译, 部署, 启动 Tomcat 的过程.

在这里插入图片描述此时 Tomcat 日志就会输出在 IDEA 的控制台中, 可以看到现在就不再乱码了.

  1. 访问页面
    在浏览器中使用 http://127.0.0.1:8080/ServletHelloWorld/hello 访问页面.
    在这里插入图片描述
    路径的对应关系.
    在这里插入图片描述
1.3.7.2 Smart Tomcat 工作原理

Smart Tomcat 相当于是在 Tomcat 启动的时候直接引用了项目中的 webapp 和 target 目录.
在这里插入图片描述

1.3.7.3 Tomcat 的伪代码

1.4 Servlet程序的运行原理

当浏览器给服务器发送HTTP请求的时候, Tomcat 作为 HTTP 服务器, 就可以接收到这个请求
在这里插入图片描述

  • 详细交互流程:
    在这里插入图片描述
  1. Tomcat 接收 HTTP请求

<1> 用户在浏览器输入一个 URL, 此时浏览器就会构造一个 HTTP 请求
<2> 这个 HTTP 请求会经过网络协议栈逐层进行 封装 成二进制的 bit 流, 最终通过物理层的硬件设备转换成光信号/电信号传输出去.
<3> 这些光信号/电信号通过互联网上的一系列网络设备, 最终到达目标主机(这个过程也需要网络层和数据链路层参与).
<4> 目的主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成HTTP 请求. 并交给 Tomcat 进程进行处理(根据端口号确定进程)
<5> Tomcat 通过 Socket 读取到这个请求(一个字符串), 并按照 HTTP 请求的格式来解析这个请求。

<<1>>根据请求中的 Context Path 确定一个 webapp
<<2>>通过 Servlet Path 确定一个具体的类
<<3>> 根据当前请求的方法(GET/POST/…)决定调用这个类的doGet / doPost方法,此时代码中的doGet / doPost方法的第 一个参数HttpServletRequest就包含了这个HTTP请求的详细信息。

  1. 根据请求计算响应

在doGet / doPost 方法中, 就执行到其他代码,这些代码会根据请求中的一些信息, 来给 HttpServletResponse 对象设置一些属性. 例如状态码, header, body 等.

  1. 返回响应

<1> doGet / doPost 执行完毕后, Tomcat 就会自动把刚设置好的 HttpServletResponse 对象转换成一个符合 HTTP 协议的字符串, 通过 Socket 把响应发送出去.
<2>响应数据在服务器的主机上通过网络协议栈层层 封装, 最终又得到一个二进制的 bit 流, 通过物理层硬件设备转换成光信号/电信号传输出去.
<3>这些光信号/电信号通过互联网上的一系列网络设备, 最终到达浏览器所在的主机(这个过程也需要网络层和数据链路层参与).
<4>主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成HTTP 响应, 并交给浏览器处理
<5>浏览器通过 Socket 读到这个响应(一个字符串), 按照 HTTP 响应的格式来解析这个响应. 并且把body 中的数据按照一定的格式显示在浏览器的界面上.

2. Servlet API详解

2.1 HttpServlet

写 Servlet 代码的时候, 首先第一步就是先创建类继承 HttpServlet, 并重写其中的某些方法. 实际开发的时候主要重写 doXXX 方法,这些方法的调用时机, 就称为 “Servlet 生命周期” 很少会重写 init / destory / service 。HttpServlet 的实例只是在程序启动时创建一次. 而不是每次收到 HTTP 请求都重新创建实例,在这里插入图片描述

2.1.1 HttpServlet 核心方法在这里插入图片描述

2.1.2 简单的前后端交互演示

  1. 创建一个新的maven项目,再pom.xml中添加servlet的依赖,在main包下创建一个webapp目录
  2. 创建 MethodServlet.java, 重写 doGet 方法
@WebServlet("/method")
public class MethodServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {	resp.getWriter().write("GET response");}
}
  1. 创建 testMethod.html,放到 webapp 目录中

// 点击按钮触发sedGet()函数
<button onclick="sendGet()">发送 GET 请求</button>   // 使用之前封装好的Ajax函数来发送请求
<script>// 定义 sengGet() 函数function sendGet() { // 调用ajax()函数,发送Ajax请求  ajax({ // 键值对格式method: 'GET',url: 'method',// 请求的正文数据  // body:// 请求正文的格式// contentType:// 处理响应的回调函数,有两个参数,响应的文本内容和响应状态callback: function (body, status) {// 打印响应文本到控制台console.log(body);}});}// 定义 ajax() 函数function ajax(args) {  // ajax() 函数接受一个包含请求参数的对象作为参数// 创建一个 XMLHttpRequest 对象var xhr = new XMLHttpRequest();// 设置xhr.onreadystatechange 属性,这是一个事件处理函数,当 xhr 对象的状态发生改变时会被调用。xhr.onreadystatechange = function () {// 0: 请求未初始化// 1: 服务器连接已建立// 2: 请求已接收// 3: 请求处理中// 4: 请求已完成,且响应已就绪// 通过 xhr.readyState 属性来检查请求的状态,当 xhr.readyState 变为 4 时,表示请求已完成且响应已就绪if (xhr.readyState == 4) {  // 当 xhr.readyState 变为 4 时,表示请求已完成且响应已就绪// 调用传入的回调函数// 将响应的文本内容和响应状态作为参数传递给回调函数args.callback(xhr.responseText, xhr.status)}}// 调用 xhr.open(args.method, args.url) 方法来初始化请求xhr.open(args.method, args.url);  // args.method 是请求的方法,此处为 ‘GET’,args.url 是请求的目标 URL// 根据是否有设置 args.contentType,决定是否设置请求头的 Content-Type。if (args.contentType) {xhr.setRequestHeader('Content-type', args.contentType);}// 根据是否有设置 args.body,决定调用 xhr.send(args.body) 或者 xhr.send() 来发送请求。if (args.body) {xhr.send(args.body);} else {xhr.send();}}
</script>

在这里插入图片描述
4. 重新部署程序
5. 访问:http://127.0.0.1:8080/ServletHelloWorld/testMethod.html页面
在这里插入图片描述
6. 点击 “发送 GET 请求” 按钮, 即可在控制台看到响应内容
在这里插入图片描述

通过 Fiddler 抓包, 可以看到

  • 当浏览器中输入 URL 之后, 浏览器先给服务器发送了一个 HTTP GET 请求
    在这里插入图片描述
  • 当点击 “发送 GET 请求” 按钮, 浏览器又通过 ajax 给服务器发送了一个 HTTP GET 请求
    在这里插入图片描述
  • 如果在响应代码里写入中文,此处可能出现乱码的情况
resp.getWriter().write("GET 响应");

在这里插入图片描述
可以在代码中显式的指定编码方式

resp.setContentType("text/html; charset=utf-8"); 
resp.getWriter().write("GET 响应");

此时通过抓包可以看到响应中多了 Content-Type 字段。
内部指定了编码方式.浏览器看到这个字段就能够正确解析中文了.
在这里插入图片描述

2.2 HttpServletRequest详解

2.2.1 核心方法

在这里插入图片描述

2.2.2 代码示例

2.2.2.1 打印请求信息

发起一个请求,将请求信息作为响应在浏览器中打印出来

  1. 创建 ShowRequest 类
@WebServlet("/showRequest")
public class ShowRequest extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException, IOException {// 设置响应数据类型及编码格式resp.setContentType("text/html; charset=utf-8");// 获取信息并加入换行符,拼接成字符串放到respBody中StringBuilder respBody = new StringBuilder();respBody.append(req.getProtocol());respBody.append("<br>");respBody.append(req.getMethod());respBody.append("<br>");respBody.append(req.getRequestURI());respBody.append("<br>");respBody.append(req.getContextPath());respBody.append("<br>");respBody.append(req.getQueryString());respBody.append("<br>");respBody.append("<h3>headers:</h3>");// 从请求对象req中获取所有请求头的名称,放入枚举集合 headernames中Enumeration<String> headerNames = req.getHeaderNames();while (headerNames.hasMoreElements()) {// 遍历枚举集合中的请求头字符串加入换行符并拼接到respBody中String headerName = headerNames.nextElement();respBody.append(headerName + " ");respBody.append(req.getHeader(headerName));respBody.append("<br>");}// 返回响应resp.getWriter().write(respBody.toString());}
}
  1. 部署程序
  2. 在浏览器通过 URL http://127.0.0.1:8080/ServletHelloWorld/showRequest 访问, 可以看到
    在这里插入图片描述
2.2.2.2 获取 GET 请求中的参数

GET 请求中的参数一般都是通过 query string 传递给服务器的. 形如:

https://v.bedu.vip/personInf/student?userId=1111&classId=100

此时浏览器通过 query string 给服务器传递了两个参数, userId 和 classId, 值分别是 1111 和 100,在服务器端就可以通过 getParameter 来获取到参数的值。getParameter 的返回值类型为 String. 必要的时候需要手动把 String 转成 int.

  1. 创建 GetParameter 类
@WebServlet("/getParameter")
public class GetParameter extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException, IOException {resp.setContentType("text/html; charset=utf-8");String userId = req.getParameter("userId");String classId = req.getParameter("classId");resp.getWriter().write("userId: " + userId + ", " + "classId: " + classId);}
}
  1. 重新部署程序,
  2. 在浏览器中访问:
http://127.0.0.1:8080/ServletHelloWorld/getParameter

可以看到当没有 query string的时候, getParameter 获取的值为 null.
在这里插入图片描述
4. 再次访问:

http://127.0.0.1:8080/ServletHelloWorld/getParameter?userId=123&classId=456

可以看到,服务器已经获取到客户端传递过来的参数并返回
在这里插入图片描述

2.2.2.3 获取 POST 请求中的参数(body参数格式为form表单)

POST 请求的参数一般通过 body 传递给服务器. body 中的数据格式有很多种. 如果是采用 form 表单的形式, 仍然可以通过 getParameter 获取参数的值.

  1. 创建类 PostParameter
@WebServlet("/postParameter")
public class PostParameter extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException, IOException {resp.setContentType("text/html; charset=utf-8");String userId = req.getParameter("userId");String classId = req.getParameter("classId");resp.getWriter().write("userId: " + userId + ", " + "classId: " +
classId);}
}
  1. 创建 testPost.html, 放到 webapp 目录中
<form action="postParameter" method="POST"><input type="text" name="userId"><input type="text" name="classId"><input type="submit" value="提交">
</form>
  1. 重新部署程序
  2. 访问URL
http://127.0.0.1:8080/ServletHelloWorld/testPost.html

可以看到 HTML
在这里插入图片描述
在输入框中输入内容, 点击提交,可以看到跳转到了新的页面, 并显示出了刚刚传入的数据.
在这里插入图片描述

  • 此时通过抓包可以看到, form 表单构造的 body 数据的格式为:
    在这里插入图片描述
2.2.2.4 获取 POST 请求中的参数(body为json格式字符串)
  1. 创建 PostParameterJson 类
@WebServlet("/postParameterJson")
public class PostParameterJson extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException, IOException {resp.setContentType("application/json;charset=utf-8");String body = readBody(req);resp.getWriter().write(body);}private String readBody(HttpServletRequest req) throws IOException {int contentLength = req.getContentLength(); // 获取请求体的内容长度byte[] buffer = new byte[contentLength];  // 创建了一个与内容长度相等的字节数组InputStream inputStream = req.getInputStream();  // 获取请求体的输入流对象inputStream.read(buffer);  // 从输入流中读取数据填充到buffer数组中return new String(buffer, "utf-8");  //  将字节数组转换成字符串}
}
  1. 创建 testPostJson.html
<button onclick="sendJson()">发送 JSON 格式 POST 请求</button>
<script>function sendJson() {ajax({url: 'postParameterJson',method: 'POST',contentType: 'application/json; charset=utf-8',body: JSON.stringify({userId: 123,classId: 456 }),callback: function (body, status) {console.log(body);}});}function ajax(args) {// 函数体略.... 参考之前封装的版本. }
</script>
  1. 在浏览器中URL
http://127.0.0.1:8080/ServletHelloWorld/testPostJson.html
  1. 可以看到html页面
    在这里插入图片描述

  2. 点击按钮, 则浏览器就会给服务器发送一个 POST 请求, body 中带有 JSON 格式

  • 抓包观察
    在这里插入图片描述
  1. 服务器收到这个结果之后, 又把数据返回了回去, 浏览器中看到了响应结果.
    目前服务器拿到的 JSON 数据仍然是一个整体的 String 类型, 如果要想获取到 userId 和classId 的具体值, 还需要搭配 JSON 库进一步解析.

在这里插入图片描述

2.2.2.5 获取 POST 请求中的参数(JSON解析)

通过ObjectMapper 对象的 readValue 方法把一个 JSON 字符串转成 JsonData 对象
通过ObjectMapper 对象的 writeValueAsString 方法把一个 Java 对象转成 JSON 格式字符串

  1. 引入 Jackson 这个库, 进行 JSON 解析.
  1. 在中央仓库)中搜索 Jackson, 选择 JackSon Databind
    在这里插入图片描述
  2. 依赖配置添加到 pom.xml 中
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.3</version>
</dependency>
  1. 修改PostParameterJson 代码
// 创建一个新的类表示 JSON 数据, 属性的名字需要和 JSON 字符串中的 key 一致. 
class JsonData {public String userId;public String classId;
}
@WebServlet("/postParameterJson")
public class PostParameterJson extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html;charset=utf-8");String body = readBody(req);// 创建 ObjectMapper 对象. 这个是 Jackson 中的核心类. ObjectMapper objectMapper = new ObjectMapper();// 通过ObjectMapper 对象的 readValue 方法把一个 JSON 字符串转成 JsonData 对象// 通过ObjectMapper 对象的 writeValueAsString 方法把一个 Java 对象转成 JSON 格式字符串// JsonData 这个类用来表示解析之后生成的 JSON 对象. 这个类的属性的名字和类型要和 JSON 字符串的 key 相对应// readValue 的第二个参数为 JsonData 的 类对象. 通过这个类对象, 在 readValue 的内部就可以借助// 反射机制来构造出 JsonData 对象, 并且根据 JSON 中key 的名字, 把对应的 value 赋值给 JsonData 的对应字段 JsonData jsonData = objectMapper.readValue(body, JsonData.class);resp.getWriter().write("userId: " + jsonData.userId + ", " + "classId: " + jsonData.classId);}private String readBody(HttpServletRequest req) throws IOException {int contentLength = req.getContentLength();byte[] buffer = new byte[contentLength];InputStream inputStream = req.getInputStream();inputStream.read(buffer);return new String(buffer, "utf-8");}
}

2.3 HttpServletResponse详解

Servlet 中的 doXXX 方法的目的就是根据请求计算得到相应, 然后把响应的数据设置到HttpServletResponse 对象中. 然后 Tomcat 就会把这个 HttpServletResponse 对象按照 HTTP 协议的格式, 转成一个字符串, 并通过Socket 写回给浏览器.

2.3.1 核心方法

在这里插入图片描述
响应对象是服务器要返回给浏览器的内容, 这里的重要信息都是程序猿设置的. 因此上面的方
法都是 “写” 方法。状态码/响应头的设置要放到 getWriter / getOutputStream 之前. 否则可能设置失效。

2.3.2 代码示例

2.3.2.1 设置状态码

实现一个程序, 用户在浏览器通过参数指定要返回响应的状态码.

  1. 创建 StatusServlet 类
@WebServlet("/statusServlet")
public class StatusServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String statusString = req.getParameter("status");if (statusString != null) {resp.setStatus(Integer.parseInt(statusString));}resp.getWriter().write("status: " + statusString);}
}
  1. 部署程序
  2. 在浏览器中访问 URL
http://127.0.0.1:8080/ServletHelloWorld/statusServlet?

可以看到
在这里插入图片描述

  • 抓包结果
    在这里插入图片描述
    变换不同的 status 的值, 就可以看到不同的响应结果.
2.3.2.2 自动刷新

实现一个程序, 让浏览器每秒钟自动刷新一次. 并显示当前的时间戳.
通过 HTTP 响应报头中的 Refresh 字段, 可以控制浏览器自动刷新的时机.
通过 Date 类的 getTime 方法可以获取到当前时刻的毫秒级时间戳.

  1. 创建 AutoRefreshServlet 类
@WebServlet("/autoRefreshServlet")
public class AutoRefreshServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException, IOException {resp.setHeader("Refresh", "1");long timeStamp = new Date().getTime();resp.getWriter().write("timeStamp: " + timeStamp);}
}
  1. 部署程序
  2. 访问 URL
http://127.0.0.1:8080/ServletHelloWorld/autoRefreshServlet

可以看到浏览器每秒钟自动刷新一次.
在这里插入图片描述

  • 抓包结果
    在这里插入图片描述
2.3.2.3 重定向

实现一个程序, 返回一个重定向 HTTP 响应, 自动跳转到另外一个页面.

  1. 创建 RedirectServlet 类
@WebServlet("/redirectServlet")
public class RedirectServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException, IOException {resp.sendRedirect("http://www.sogou.com");}
}
  1. 部署程序
  2. 访问 URL
http://127.0.0.1:8080/ServletHelloWorld/redirectServlet

可以看到页面自动跳转到 搜狗主页 了.

  • 抓包结果
    在这里插入图片描述

http://www.mrgr.cn/p/73718414

相关文章

C#下使用正则表达式

常用元字符字符 描述\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,n 匹配字符 "n"。\n 匹配一个换行符。序列 \\ 匹配 "\" 而 "\(" 则匹配 "("。^ 匹配输入字符串的开始位置。如果…

平衡二叉树(AVLTree)

AVLTree 1、树的分类2、平衡二叉树2.1、构建一个平衡二叉树2.2、删除节点2.3、搜索方式2.3.1、广度优先搜索&#xff08;BFS&#xff09;2.3.2、深度优先搜索&#xff08;DFS&#xff09; 1、树的分类 树形结构是编程当中特别常见的一种数据结构。比如电脑中的文件管理系统就大…

Linux加强篇-Vim编辑器

目录 ⛳️推荐 Vim文本编辑器 编写简单文档 配置主机名称 配置网卡信息 配置软件仓库 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 Vim文本编辑器 在Linux系统中一切都…

数据结构基础第5讲

数据结构基础第5讲 树和二叉树 本章内容:考点一:基本术语 1.数的引入2.树的定义 层次,分支,一对多。互不相交的含义:3.结点的分类结点的度:4.结点的关系树的深度:树中结点最大高度称为树的高度(或树的深度)行兄弟结点:在同一层但不是兄弟的结点路径长度:等于路径所通…

C语言(扫雷游戏)

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸各位能阅读我的文章&#xff0c;诚请评论指点&#xff0c;关注收藏&#xff0c;欢迎欢迎~~ &#x1f4a5;个人主页&#xff1a;小羊在奋斗 &#x1f4a5;所属专栏&#xff1a;C语言 本系列文章为个人学习笔记&#x…

R语言随机森林RandomForest、逻辑回归Logisitc预测心脏病数据和可视化分析|附代码数据

全文链接:http://tecdat.cn/?p=22596 最近我们被客户要求撰写关于预测心脏病的研究报告,包括一些图形和统计输出。 本报告是对心脏研究的机器学习/数据科学调查分析。更具体地说,我们的目标是在心脏研究的数据集上建立一些预测模型,并建立探索性和建模方法。但什么是心脏研…

数据结构基础第3讲

数据结构基础第3讲 栈及其应用 内容考点一:栈的概念 1.顺序栈的定义:出栈顺序情况计算 给定n个元素,出栈顺序的情形满足卡特兰数,计算公式: \[\frac{C_{2n}^{n}}{n+1} \]例题: 确定第一个出栈的谁。有两种可能: 找带头大哥。栈的顺序存储结构 顺序栈操作顺序栈4要素栈空条…

数据结构基础第4讲

数据结构基础第4讲 队列 内容考点一: 队列概念 代码不考 1.队列的定义考点二:顺序队列的定义考点三顺序队列的性质与操作 4要素:考点四:循环队列的定义 由于顺序队列会存在假溢出问题,引入循环队列。 假溢出:描述:考点五:循环队列的操作判断空满:性质: 考频75%元素个…

深入探索GDB:Linux下强大的调试神器

目录 一、GDB简介&#xff1a;源码级调试的基石 二、GDB基础操作&#xff1a;从入门到熟练 启动与基本命令 三、GDB进阶功能&#xff1a;解锁更深层次的调试能力 1. 回溯追踪&#xff1a;洞察调用栈 2. 动态内存检测&#xff1a;揪出内存问题 3. 条件断点与观察点&#…

【视频】N-Gram、逻辑回归反欺诈模型文本分析招聘网站欺诈可视化|附数据代码

原文链接:https://tecdat.cn/?p=36028 原文出处:拓端数据部落公众号 随着互联网的快速发展,招聘网站已成为求职者与雇主之间的重要桥梁。然而,随之而来的欺诈行为也日益猖獗,给求职者带来了极大的困扰和风险。因此,如何帮助客户有效地识别和防范招聘网站上的欺诈行为,已…

02 IO口的操作

目录前言一、IO的概念1.IO接口2.IO端口二、CPU和外设进行数据传输的方法1.程序控制方式1.1 无条件1.2 查询方式2.中断方式3.DMA方式一、方法介绍和代码编写1.前置知识2.程序方式1.1 无条件方式1.1.1 打开对应的GPIO口1.1.2 初始化对应的GPIO引脚1.1.2.1 推挽输出1.1.2.2 开漏输…

vmstat命令详解

一、参数信息 vmstat 命令是用于报告虚拟内存统计信息的工具&#xff0c;常用于 Unix/Linux 系统上。它可以提供关于系统资源使用情况的详细信息&#xff0c;包括 CPU、内存、虚拟内存、磁盘、系统调用等方面的统计数据。以下是常见的 vmstat 命令参数的详解&#xff1a; vms…

题解 UOJ577【[ULR #1] 打击复读】

别学基本子串结构(这篇没写完)题解 UOJ577【[ULR #1] 打击复读 reference https://www.cnblogs.com/crashed/p/17382894.html https://www.cnblogs.com/sizeof127/articles/17579027.html 字符串——黄建恒,广东实验中学 题目描述 为了提升搜索引擎的关键词匹配度以加大访问…

VUE识别图片文字OCR(tesseract.js)

效果:1&#xff1a; 效果图2&#xff1a; 一、安装tesseract.js npm i tesseract.js 二、静态页面实现 <template><div><div style"marginTop:100px"><input change"handleChage" type"file" id"image-input"…

吴恩达机器学习-第二课-第四周

吴恩达机器学习 学习视频参考b站:吴恩达机器学习 本文是参照视频学习的随手笔记,便于后续回顾。 决策树 决策树模型(Decision Tree Model) 猫分类示例通过决策树模型判断是否为猫 一些术语:根结点,决策节点(包括根结点),叶子结点决策树算法是在所有的决策树模型中选一…

【EI会议征稿】2024年先进机械电子、电气工程与自动化国际学术会议(ICAMEEA 2024)

2024 International Conference on Advanced Mechatronic, Electrical Engineering and Automation ●会议简介 2024年先进机械电子、电气工程与自动化国际学术会议&#xff08;ICAMEEA 2024&#xff09;将汇聚全球机械电子、电气工程与自动化领域的专家学者&#xff0c;共同…

【QT进阶】Qt Web混合编程之使用ECharts显示各类折线图等

往期回顾 【QT进阶】Qt Web混合编程之QWebEngineView基本用法-CSDN博客 【QT进阶】Qt Web混合编程之CMake VS2019编译并使用QCefView&#xff08;图文并茂超详细版本&#xff09;-CSDN博客【QT进阶】Qt Web混合编程之html、 js的简单交互-CSDN博客 【QT进阶】Qt Web混合编程之使…

编译用于Qt的opencv问题解决

CMake was unable to find a build program corresponding to "MinGW Makefiles"解释: 这个错误表明CMake无法找到用于生成Makefiles的构建程序。在使用CMake生成项目文件时,如果指定了"MinGW Makefiles",CMake需要一个Make工具来构建项目,而这个工具通…

破解生产瓶颈,提升时效性——蓝鹏测控推进效率革新

在日益激烈的市场竞争中&#xff0c;蓝鹏公司近日宣布采取一系列措施&#xff0c;旨在解决生产过程中的关键短板问题&#xff0c;特别是设计定稿延迟、原料采购不及时等问题&#xff0c;以确保生产部门能够按时完成订单&#xff0c;提高整体运营效率。 蓝鹏公司位于经济发展活…

操作系统八股

操作系统八股 1. 你了解IO多路复用么? 我们熟悉的 select/poll/epoll 内核提供给用户态的多路复用系统调用,进程可以通过一个系统调用函数从内核中获取多个事件。 select/poll/epoll 是如何获取网络事件的呢?在获取事件时,先把所有连接(文件描述符)传给内核,再由内核返回…