Servlet的使用

作者:~小明学编程 

文章专栏:JavaEE

格言:热爱编程的,终将被编程所厚爱。
在这里插入图片描述

目录

什么是Servlet?

创建一个Servlet程序

1.创建一个Maven项目

2.引入依赖 

3.创建目录

4.编写代码

5.打包

6.部署程序

7.验证程序

修改一个Servlet程序

原始改法

Smart Tomcat

访问出错

404问题

405问题

500问题

出现空白页面

无法访问此网站

Servlet的运行原理

Tomcat的定位

Tomcat伪代码

Servlet的生命周期

Servlet API

HttpServlet

HttpServletRequest

获取POST中请求的参数

HttpServletResponse


今天开始给大家分享关于本人Servet的学习心得。

什么是Servlet?

Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一个 web app。

前面我们做到了用Tomcat实现了一个简单的静态页面的过程,但是静态页面的内容过于固定,里面的东西不会改变,我们现在想要实现一个动态的页面。

构建动态页面的技术有很多, 每种语言都有一些相关的库/框架来做这件事,Servlet 就是 Tomcat 这个 HTTP 服务器提供给 Java 的一组 API, 来完成构建动态页面这个任务。

Servlet 是一组 Tomcat 提供的 API, 让程序猿自己写的代码能很好的和 Tomcat 配合起来, 从
而更简单的实现一个 web app,而不必关注 Socket, HTTP协议格式, 多线程并发等技术细节, 降低了 web app 的开发门槛, 提高了开发效率。

创建一个Servlet程序

1.创建一个Maven项目

1)首先创建新项目,找到Maven。

 2)填写相关的路径

2.引入依赖 

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

1)在中央仓库中搜索servlet

 2)找到我们相应的版本号

 3) 把中央仓库中提供的 xml 复制到项目的 pom.xml 中

 刚复制完里面可能会标红,因为我们还没有下载好相关的依赖,

 

 点击这个左上角的刷新按钮。

3.创建目录

 这是我们刚开始自动生成的目录,现在我们要,向里面添加一些目录。

 其中要添加,webapp,WEB-INF,和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>

webapp 目录就是未来部署到 Tomcat 中的一个重要的目录. 当前我们可以往 webapp 中放一些静态资源, 比如 html , css 等.
在这个目录中还有一个重要的文件 web.xml. Tomcat 找到这个文件才能正确处理 webapp 中的动态资源.

关于这些目录:

src 表示源代码所在的目录
main/java 表示源代码的根目录. 后续创建 .java 文件就放到这个目录中.
main/resources 表示项目的一些资源文件所在的目录. 此处暂时不关注.
test/java 表示测试代码的根目录. 此处暂时不关注

4.编写代码

在 java 目录中创建一个类 HelloServlet, 代码如下:

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("hello world");
        resp.getWriter().write("hello world");
    }
}

关于这段代码:

  • 我们需要创建一个类 HelloServlet , 继承自 HttpServlet。
  • 在这个类上方加上 @WebServlet("/hello") 注解, 表示 Tomcat 收到的请求中, 路径为 /hello
    的请求才会调用 HelloServlet 这个类的代码.。(这个路径未包含 Context Path)
  • 重写 doGet 方法. doGet 的参数有两个, 分别表示收到的 HTTP 请求 和要构造的 HTTP 响应. 这个方法会在 Tomcat 收到 GET 请求时触发。
  • HttpServletRequest 表示 HTTP 请求. Tomcat 按照 HTTP 请求的格式把 字符串 格式的请求转
    成了一个 HttpServletRequest 对象. 后续想获取请求中的信息(方法, url, header, body 等) 都是
    通过这个对象来获取。
  • HttpServletResponse 表示 HTTP 响应. 代码中把响应对象构造好(构造响应的状态码, header,
    body 等)
  • resp.getWriter() 会获取到一个流对象, 通过这个流对象就可以写入一些数据, 写入的数据会被
    构造成一个 HTTP 响应的 body 部分, Tomcat 会把整个响应转成字符串, 通过 socket 写回给浏览器。

在刚刚的代码中我们也许会发现其没有main()方法的入口,其实不然main 方法已经被包含在 Tomcat 里, 我们写的代码会被Tomcat 在合适的时机调用起来。

那么怎么才能被Tomcat给调用呢?

a) 创建的类需要继承自 HttpServlet。

b) 这个类需要使用 @WebServlet 注解关联上一个 HTTP 的路径。

c) 这个类需要实现 doXXX 方法。

5.打包

打包之前要在我们的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>test_1_8_maven</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <!--配置依赖-->
    <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>
<!--    将其打包成一个war包,默认是一个jar包-->
    <packaging>war</packaging>
    <build>
        <finalName>test_hello</finalName>
    </build>


</project>

接着:

 双击package.

BUILD SUCCESS这样就是打包成功了。

左边可以看到我们已经打包好的war包。

6.部署程序

找到我们testhello.war包的路径然后将其复制到我们Tomcat相应的路径下面:

 接着开启服务器:

 开启后可以找到我们相应的文件。

 同时可以看到我们的文件也被解压缩了。

7.验证程序

打开浏览器输入我们相应的地址:127.0.0.1:8080/test_hello/hello

URL 中的 PATH 分成两个部分:第一个test_hello就是我们解压后的文件名Context path,然后hello就是我们Servlet path,就是我们前面加的路径。

@WebServlet("/hello")

修改一个Servlet程序

原始改法

首先我们要注意的是我们这里虽然也是简单的打印一个hello world但是这里不一样,我们这里的是动态的页面,比如现在我们在原有的程序打印hello world的里面加上一个时间戳。

        resp.getWriter().write("hello world"+System.currentTimeMillis());

在我们改了程序时候肯定是要重新上传部署的,之后这里我们需要重复上面的5,6,7的操作。

当我们刷新页面之后就会看到我们的时间戳也会发生变化:

 当然这种方式修改代码会比较的麻烦,我们还要重新的部署,然后复制粘贴等步骤,下面我们有更加简单的方法。

Smart Tomcat

前面我们说到修改代码以及部署服务器比较麻烦,我们可以把Tomcat 直接放在IDEA里面后面我们的打包部署等操作就可以一键完成了。

首先我们要安装Smart Tomcat:

在我们的扩展里面找到Smart Tomcat,然后下载下来就行了。

 接下来就需要部署相关的配置:右上角找到这个Add

 完成之后

 之后我们就可以点击这个小三角完成一键部署启动Tomcat了。

可以看到此时就成功部署完毕了,如果出现404就检查前面的步骤是否出错了。

访问出错

 在我们实际的部署运行代码的过程中,会出现很多的问题,我们的服务器可能会抛出各种各样的非正常而定响应,下面就给大家讲讲几种常见的问题。

404问题

 如图所示:我们这里给了个404的响应状态,这种情况一般有下面三种可能

  1. 少写了 Context Path,或者Context Path写错了。
  2. 少写了 Servlet Path,或者Servlet Path写错了。
  3. web.xml 写错了,前面我们配置相关环境的时候向web.xml里面放入了相关的代码,如果内容错误或者没有也会导致404。

405问题

 在浏览器地址栏直接输入 URL , 会发送一个 HTTP GET 请求,此时我们会通过路径找到相关的类,并且调用doGet()方法,如果没有该方法那就会抛出405。

500问题

出现500往往是 Servlet 代码中抛出异常导致的,就是我们代码本身有问题。

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String str = null;
        System.out.println(str.length());
        System.out.println("hello world");
        resp.getWriter().write("hello world"+System.currentTimeMillis());
    }

我们这个代码将会抛出空指针的异常。

服务器直接告诉了我们这个错误。 

出现空白页面

        resp.getWriter().write("hello world"+System.currentTimeMillis());

这句话是向我们的响应里面写出相关的数据,如果没有这句话我们响应里面的body将会是空的,我们的浏览器也不会有任何的东西。

无法访问此网站

 这种情况一般就是我们的服务器关闭了,所以无法访问。

Servlet的运行原理

在 Servlet 的代码中我们并没有写 main 方法, 那么对应的 doGet 代码是如何被调用的呢? 响应又是如何返回给浏览器的?

Tomcat的定位

在我们了解Servlet之前我们肯定要先了解一下Tomcat的,毕竟Servlet是我们tomcat所提供的一组api。

当浏览器给服务器发送请求的时候, Tomcat 作为 HTTP 服务器, 就可以接收到这个请求,HTTP 协议作为一个应用层协议, 需要底层协议栈来支持工作。

具体我们可以参考下面这幅图:

 1.接受请求:

  • 用户在浏览器输入一个 URL, 此时浏览器就会构造一个 HTTP 请求。
  • 这个 HTTP 请求会经过网络协议栈逐层进行 封装 成二进制的 bit 流, 最终通过物理层的硬件设备转换成光信号/电信号传输出去。
  • 这些承载信息的光信号/电信号通过互联网上的一系列网络设备, 最终到达目标主机(这个过程也需要网络层和数据链路层参与)。
  • 服务器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成HTTP 请求. 并交给 Tomcat 进程进行处理(根据端口号确定进程)。
  • Tomcat 通过 Socket 读取到这个请求(一个字符串), 并按照 HTTP 请求的格式来解析这个请求, 根据请求中的 Context Path 确定一个 webapp, 再通过 Servlet Path 确定一个具体的 类. 再根据当前请求的方法 (GET/POST/...), 决定调用这个类的 doGet 或者 doPost 等方法. 此时我们的代码中的 doGet / doPost 方法的第一个参数 HttpServletRequest 就包含了这个 HTTP 请求的详细信息。
     

2.根据请求计算响应:

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

3.返回响应:

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

Tomcat伪代码

Tomcat 初始化流程:

class Tomcat {
    // 用来存储所有的 Servlet 对象
    private List<Servlet> instanceList = new ArrayList<>();
    public void start() {
// 根据约定,读取 WEB-INF/web.xml 配置文件;
// 并解析被 @WebServlet 注解修饰的类
// 假定这个数组里就包含了我们解析到的所有被 @WebServlet 注解修饰的类.
        Class<Servlet>[] allServletClasses = ...;
// 这里要做的的是实例化出所有的 Servlet 对象出来;
        for (Class<Servlet> cls : allServletClasses) {
// 这里是利用 java 中的反射特性做的
// 实际上还得涉及一个类的加载问题,因为我们的类字节码文件,是按照约定的
// 方式(全部在 WEB-INF/classes 文件夹下)存放的,所以 tomcat 内部是
// 实现了一个自定义的类加载器(ClassLoader)用来负责这部分工作。
            Servlet ins = cls.newInstance();
            instanceList.add(ins);
        }
// 调用每个 Servlet 对象的 init() 方法,这个方法在对象的生命中只会被调用这一次;
        for (Servlet ins : instanceList) {
            ins.init();
        }
// 利用我们之前学过的知识,启动一个 HTTP 服务器
// 并用线程池的方式分别处理每一个 Request
        ServerSocket serverSocket = new ServerSocket(8080);
// 实际上 tomcat 不是用的固定线程池,这里只是为了说明情况
        ExecuteService pool = Executors.newFixedThreadPool(100);
        while (true) {
            Socket socket = ServerSocket.accept();
// 每个请求都是用一个线程独立支持,这里体现了我们 Servlet 是运行在多线程环境下的
            pool.execute(new Runnable() {
                doHttpRequest(socket);
            });
        }
// 调用每个 Servlet 对象的 destroy() 方法,这个方法在对象的生命中只会被调用这一次;
        for (Servlet ins : instanceList) {
            ins.destroy();
        }
    }
    public static void main(String[] args) {
        new Tomcat().start();
    }
}

这里我们通过伪代码简单的描述了Tomcat初始化的过程,其中我们需要注意的是:

  1. Tomcat 的代码中内置了 main 方法. 当我们启动 Tomcat 的时候, 就是从 Tomcat 的 main 方法开始执行的。
  2. 被 @WebServlet 注解修饰的类会在 Tomcat 启动的时候就被获取到, 并集中管理,因为我们要根据Servlet Path来确定一个具体的类,所以被注释过的要集中管理。
  3. 这些实例被创建完了之后, 会点调用其中的 init 方法进行初始化. (这个方法是 HttpServlet 自带的,我们自己写的类可以重写 init,同时destory也一样)
  4. Tomcat 内部也是通过 Socket API 进行网络通信。
  5. Tomcat 为了能同时相应多个 HTTP 请求, 采取了多线程的方式实现. 因此 Servlet 是运行在 多线程环境下的。

Tomcat 处理请求流程:

class Tomcat {
    void doHttpRequest(Socket socket) {
// 参照我们之前学习的 HTTP 服务器类似的原理,进行 HTTP 协议的请求解析和响应构建
        HttpServletRequest req = HttpServletRequest.parse(socket);
        HttpServletRequest resp = HttpServletRequest.build(socket);
// 判断 URL 对应的文件是否可以直接在我们的根路径上找到对应的文件,如果找到,就是静态
        内容
// 直接使用我们学习过的 IO 进行内容输出
        if (file.exists()) {
// 返回静态内容
            return;
        }
// 走到这里的逻辑都是动态内容了
// 根据我们在配置中说的,按照 URL -> servlet-name -> Servlet 对象的链条
// 最终找到要处理本次请求的 Servlet 对象
        Servlet ins = findInstance(req.getURL());
// 调用 Servlet 对象的 service 方法
// 这里就会最终调用到我们自己写的 HttpServlet 的子类里的方法了
        try {
            ins.service(req, resp);
        } catch (Exception e) {
// 返回 500 页面,表示服务器内部错误
        }
    }
}
  1. Tomcat 从 Socket 中读到的 HTTP 请求是一个字符串, 然后会按照 HTTP 协议的格式解析成一个HttpServletRequest 对象.
  2. Tomcat 会根据 URL 中的 path 判定这个请求是请求一个静态资源还是动态资源. 如果是静态资源,直接找到对应的文件把文件的内容通过 Socket 返回. 如果是动态资源, 才会执行到 Servlet 的相关逻辑.
  3. Tomcat 会根据 URL 中的 Context Path 和 Servlet Path 确定要调用哪个 Servlet 实例的 service方法.
  4. 通过 service 方法, 就会进一步调用到我们之前写的 doGet 或者 doPost。

Servlet 的 service 方法的实现:

class Servlet {
    public void service(HttpServletRequest req, HttpServletResponse resp) {
        String method = req.getMethod();
        if (method.equals("GET")) {
            doGet(req, resp);
        } else if (method.equals("POST")) {
            doPost(req, resp);
        } else if (method.equals("PUT")) {
            doPut(req, resp);
        } else if (method.equals("DELETE")) {
            doDelete(req, resp);
        }
......
    }
}

Servlet 的 service 方法内部会根据当前请求的方法, 决定调用其中的某个 doXXX 方法.
在调用 doXXX 方法的时候, 就会触发 多态 机制, 从而执行到我们自己写的子类中的 doXXX 方法

Servlet的生命周期

在了解上述原理之后下面就来说说我们Servlet的生命周期:

  1. 加载Servlet:当Tomcat第一次访问Servlet的时候,Tomcat会负责创建Servlet的实例。
  2. 初始化:当Servlet被实例化后,Tomcat会调用init()方法初始化这个对象。
  3. 处理服务:当浏览器访问Servlet的时候,Servlet 会调用service()方法处理请求。
  4. 销毁:当Tomcat关闭时或者检测到Servlet要从Tomcat删除的时候会自动调用destroy()方法,让该实例释放掉所占的资源,一个Servlet如果长时间不被使用的话,也会被Tomcat自动销毁。
  5. 卸载:当Servlet调用完destroy()方法后,等待垃圾回收。如果有需要再次使用这个Servlet,会重新调用init()方法进行初始化操作。

Servlet API

HttpServlet

我们写 Servlet 代码的时候, 首先第一步就是先创建类, 继承自 HttpServlet, 并重写其中的某些方法。

核心方法:

方法名称调用时机
init在 HttpServlet 实例化之后被调用一次
destory在 HttpServlet 实例不再使用的时候调用一次
service收到 HTTP 请求的时候调用
doGet收到 GET 请求的时候调用(由 service 方法调用)
doPost收到 POST 请求的时候调用(由 service 方法调用)
doPut/doDelete/doOptions/...收到其他请求的时候调用(由 service 方法调用)

我们实际开发的时候主要重写 doXXX 方法, 很少会重写 init / destory / service。

前面我们说了get请求的模拟,下面我们就来模拟一个post的请求。

post的请求和get不一样,get请求直接通过url就可以了,但是post则不行,我们这里尝试用ajax来构造请求:

@WebServlet("/method")
public class MyServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("test/html; charset=utf8");//处理乱码
        resp.getWriter().write("post:响应");
    }
}
<!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>hello</title>
</head>
<body>
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    <script>
        $.ajax({
            type: 'post',
            url: 'method',
            success: function(body) {
                console.log(body);
            }

        });
    </script>
</body>
</html>

HttpServletRequest

当 Tomcat 通过 Socket API 读取 HTTP 请求(字符串), 并且按照 HTTP 协议的格式把字符串解析成
HttpServletRequest 对象。

常见方法:

方法描述
String getProtocol()返回请求协议的名称和版本。
String getMethod()返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。
String getRequestURI()从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该
请求的 URL 的一部分。
String getContextPath()返回指示请求上下文的请求 URI 部分。
String getQueryString()返回包含在路径后的请求 URL 中的查询字符串。
Enumeration
getParameterNames()
返回一个 String 对象的枚举,包含在该请求中包含的参数的名
称。
String getParameter(String
name)
以字符串形式返回请求参数的值,或者如果参数不存在则返回
null。
String[]
getParameterValues(String
name)
返回一个字符串对象的数组,包含所有给定的请求参数的值,
如果参数不存在则返回 null。
Enumeration
getHeaderNames()
返回一个枚举,包含在该请求中包含的所有的头名。
String getHeader(String
name)
以字符串形式返回指定的请求头的值。
String
getCharacterEncoding()
返回请求主体中使用的字符编码的名称。
String getContentType()返回请求主体的 MIME 类型,如果不知道类型则返回 null。
int getContentLength()以字节为单位返回请求主体的长度,并提供输入流,或者如果
长度未知则返回 -1。
InputStream
getInputStream()
用于读取请求的 body 内容. 返回一个 InputStream 对象.

通过这些方法可以获取到一个请求中的各个方面的信息,请求对象是服务器收到的内容, 不应该修改. 因此上面的方法也都只是 "读" 方法, 而不是 "写"方法。

打印请求信息:

@WebServlet("/show")
public class showRequestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset=utf-8");
        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>");
        Enumeration<String> headerNames = req.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            respBody.append(headerName + " ");
            respBody.append(req.getHeader(headerName));
            respBody.append("<br>");
        }
        resp.getWriter().write(respBody.toString());
    }
}

同时我们也可以获取query string中的信息。

获取POST中请求的参数

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

@WebServlet("/postGet")
public class PostGetParameterServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String userId = req.getParameter("userId");
        String classId = req.getParameter("classId");
        resp.getWriter().write("userId:"+userId+" classId:"+classId);
    }
}
<!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>hello</title>
</head>
<body>
    <form action="postGet" method="post">
        <input type="text" name="userId">
        <input type="text" name="classId">
        <input type="submit" value="提交">
    </form>
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    <script>
    </script>
</body>
</html>

如果 POST 请求中的 body 是按照 JSON 的格式来传递, 那么获取参数的代码就要发生调整。

<!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>hello</title>
</head>
<body>
    <input type="text" id="userId">
    <input type="text" id="classId">
    <input type="button" value="提交" id="submit">

    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    <script>

        let userInInput = document.querySelector('#userId');
        let classIdInput = document.querySelector('#classId');
        let button = document.querySelector('#submit');
        button.onclick = function() {
            $.ajax({
                type: 'post',
                url: 'postGet',
                contentType: 'application/json',
                data: JSON.stringify({
                    userId: userInInput.value,
                    classId: classIdInput.value
                }),
                success: function(body) {
                    console.log(body);
                }
            });
        }
    </script>
</body>
</html>

 前面我们获取参数都是通过构造一个一个的字符串然后通过getParameter方法来获的,有没有更加简单的方法呢?

引入 Jackson 这个库, 进行 JSON 解析:首先的我们要去maven仓库中下载这个依赖,

        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.6</version>
        </dependency>
class JsonData {
    public String userId;
    public String classId;
}
@WebServlet("/postJson")
public class PostJsonServlet extends HttpServlet {
    //创建一个JSON的核心对象
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //readValue就是将这个JSON请求转换成Java对象
        //其中第一个参数表示对哪一个字符串进行转换,第二个参数表示要把这个Json格式的字符串转换成哪一个类对象
        JsonData jsonData = objectMapper.readValue(req.getInputStream(),JsonData.class);
        resp.getWriter().write("userId:"+jsonData.userId+" classId:"+jsonData.classId);

    }
}

这里我们要注意readValue()这个方法,该方法的作用是首先将我们的req数据流中的json数据解析成一个一个的键值对,接着遍历这个键值对同时我们通过反射获取到了我们的类属性,然后将键值对的键和类中的属性做对比,如果在类中找到了该键然后就把响应的值给赋过去。

HttpServletResponse

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

核心方法

方法描述
void setStatus(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 字符集)例
如,UTF-8。
void sendRedirect(String
location)
使用指定的重定向位置 URL 发送临时重定向响应到客户端。
PrintWriter getWriter()用于往 body 中写入文本格式数据.
OutputStream
getOutputStream()
用于往 body 中写入二进制格式数据.

设置状态码

@WebServlet("/status")
public class StatusServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(404);
        resp.getWriter().write("hello");
    }
}

自动刷新

@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setHeader("Refresh","1");
        resp.getWriter().write("time:"+System.currentTimeMillis());
    }
}

重定向

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

@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //方法1
        resp.setStatus(302);
        resp.setHeader("Location","https://www.baidu.com");
//        //方法2
//        resp.sendRedirect("https://www.baidu.com");
    }
}


 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值