【 Servlet API 】

一、HttpServlet

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

1.1 核心方法

在这里插入图片描述

我们实际开发的时候主要重写 doXXX 方法, 很少会重写 init / destory / service .在前面我们讲了这些方法的调用时机就成为"Servlet的生命周期"

1.2 代码示例

1.2.1 处理 Get 请求:

创建一个 doGetMethod 类并重写doGet方法

@WebServlet("/hellodoGet")
public class doGetMethod extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 不能调用父类的 doGet !!!要自己实现一个
        // super.doGet(req, resp);

        // 把 hello world 字符串, 放到 http 响应的 body 中. 浏览器就会把 body 的内容显示到页面上了,如果不给响应对象中设置任何内容, 这个时候就会出现 空白页面
        //resp:响应对象  write:真正写数据的方法
        //getWriter:返回一个Writer对象(字符流对象),此时Writer是往http响应的body中写入数据
        resp.getWriter().write("hello world");
    }
}

部署项目后,在浏览器输入 http://localhost:8080/HelloWorldservlet/hellodoGet 访问页面

在这里插入图片描述

1.2.2 处理 POST 请求:

创建 doPostMethod 类并重写doPost

@WebServlet("/hellodoPost")
public class doPostMethod extends HttpServlet{
    
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //super.doPost(req, resp);


        resp.setContentType("text/html;charset = utf8");
        resp.getWriter().write("post 响应");
    }
}

创建test.html,并放入webapp中

在这里插入图片描述

一个 Servlet 程序中可以同时部署静态文件. 静态文件就放到 webapp 目录中即可。但是注意此时
test.html文件在webapp中,与WEB-INF是同级

test.html 的代码如下:

<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <script>
        $.ajax({
    
    
            type:'post',
            url:'hellodoPost',
            success:function(body){
    
    
                console.log(body);
            },
        });
    </script>

重新部署程序, 使用 URL http://localhost:8080/HelloWorldservlet/test.html 访问页面

在这里插入图片描述

通过 Fiddler 抓包, 可以看到:

1.当浏览器中输入 URL 之后, 浏览器先给服务器发送了一个 HTTP GET 请求,请求一个test.html页面,而这个页面中没有写内容,所以是空白

在这里插入图片描述

2.浏览器又通过 ajax 给服务器发送了一个 HTTP POST 请求

在这里插入图片描述

并返回了一个响应:post 响应

在这里插入图片描述

注意:

这个 ajax 请求的 URL 路径. 代码中写的 url: 'hellodoPost’不加 /, 为一个相对路径, 相对于当前test.html的路径,而test.html的路径就是HelloWorldservlet 最终真实发送的请求的 URL 路径为 /HelloWorldservlet/hellodoPost

关于乱码问题

上述代码已经做过处理所以没有乱码!如果不处理,我们直接在响应中写中文此时浏览器就会看到乱码,如下

在这里插入图片描述

为什么呢? 中文的编码方式有很多种. 其中最常见的就是 utf-8 .如果没有显式的指定编码方式, 则浏览器不能正确识别编码, 就会出现乱码的情况

如何解决! 可以在代码中, 通过 resp.setContentType(“text/html; charset=utf-8”); 显式的指定编码方式

此时通过抓包可以看到, 当加上了 resp.setContentType(“text/html; charset=utf-8”); 代码之后, 响应中多了 Content-Type 字段, 内部指定了编码方式. 浏览器看到这个字段就能够正确解析中文了

在这里插入图片描述


二、HttpServletRequest

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

2.1 核心方法

在这里插入图片描述

注意: 请求对象是服务器收到的内容, 不应该修改. 因此上面的方法也都只是 “读” 方法, 而不是 "写"方法

2.2 代码示例

2.2.1 打印请求信息

@WebServlet("/showRequest")
public class HttpServletRequestMethod extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //super.doGet(req, resp);

        //使用 HttpServletRequest 的各种方法获取到 http请求的各种属性,在作为响应的body返回到页面中
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("<h3>首行部分</h3>");
        stringBuilder.append(req.getProtocol());//协议名和版本
        stringBuilder.append("<br>");
        stringBuilder.append(req.getMethod());//请求 方法
        stringBuilder.append("<br>");
        stringBuilder.append(req.getRequestURI());//URL /helloservelet/showRequest
        stringBuilder.append("<br>");
        stringBuilder.append(req.getContextPath());//   helloservelet
        stringBuilder.append("<br>");
        stringBuilder.append(req.getQueryString());// 查询字符串内容
        stringBuilder.append("<br>");
        stringBuilder.append("<h3>header 部分</h3>");
        Enumeration<String> headerNames = req.getHeaderNames();//返回一个枚举,包含在该请求中包含的所有的头名
        while (headerNames.hasMoreElements()) {
    
    //遍历枚举里面的每一个元素
            String headerName = headerNames.nextElement(); //请求头的名字
            String headerValue = req.getHeader(headerName);//以字符串形式返回指定的请求头的值
            stringBuilder.append(headerName + ": " + headerValue + "<br>");
        }

        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write(stringBuilder.toString());
    }
}

部署后通过URL访问页面,如下

在这里插入图片描述

2.2.2 获取 GET 请求中的参数
GET 请求中的参数一般都是通过 query string 传递给服务器的. 形如
https://v.bitedu.vip/personInf/student?userId=1111&classId=100
此时浏览器通过 query string 给服务器传递了两个参数, userId 和 classId, 值分别是 1111 和 100
在服务器端就可以通过 getParameter 来获取到参数的值.

创建 etParameterTest 类

@WebServlet("/getParameter")
public class getParameterTest extends HttpServlet {
    
    
    @Override
    //Post请求一般通过body传递,如果body中的格式是 form表单的形式,任然可以通过 getParameter 获取参数!
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //super.doGet(req, resp);
        //预期浏览器传来一个请求:/getParameter?userID=123&classID=456
        String userId = req.getParameter("userID");//获取到请求中的参数
        String classId = req.getParameter("classID");

        resp.getWriter().write("userId=" + userId + ", classId=" + classId);
    }
}

重新部署程序, 在浏览器中通过 http://localhost:8080/HelloWorldservlet/getParameter 访问页面

在这里插入图片描述

当没有 query string的时候, getParameter 获取的值为null.再在浏览器输入URL
http://localhost:8080/HelloWorldservlet/getParameter?userId=123&classId=456

在这里插入图片描述

此时说明服务器已经获取到客户端传递过来的参数

getParameter 的返回值类型为 String. 必要的时候需要手动把 String 转成 int


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

创建类 PostParameter

@WebServlet("/postParameter")
public class PostParameter extends HttpServlet {
    
    
    @Override
    protected 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);
    }
}

创建 testPost.html, 放到 webapp 目录中

<form action="postParameter" method="POST">
   <input type="text" name="userId">
   <input type="text" name="classId">
   <input type="submit" value="提交">
</form>

重新部署程序, 通过 URL http://127.0.0.1:8080/ServletHelloWorld/testPost.html 访问, 可以
看到 HTML
在这里插入图片描述
在输入框中输入内容, 点击提交
在这里插入图片描述

可以看到跳转到了新的页面, 并显示出了刚刚传入的数据
在这里插入图片描述
此时通过抓包可以看到, form 表单构造的 body 数据的格式为:
在这里插入图片描述

Content-Type: application/x-www-form-urlencoded, 对应的 body 数据格式就形如
userId=123&classId=456


2.2.4 获取 POST 请求中的参数(2)
如果 POST 请求中的 body 是按照 JSON 的格式来传递json里面可能涉及到嵌套,所以手动解析其实并不容易。此时就可以使用第三方库来协助

引入 Jackson 这个库, 进行 JSON 解析.,步骤如下:

  1. 在中央仓库中搜索 Jackson, 选择 JackSon Databind

在这里插入图片描述

  1. 把中央仓库中的依赖配置添加到 pom.xml 中, 形如:

在这里插入图片描述

  1. 编写代码

后端:

class User{
    
    
    public int userId;
    public int classId;
}

//如果post请求body中是 json格式!就需要通过第三方库 Jackson 来获取里面的数据
@WebServlet("/postJson")
public class PostJsonServlet extends HttpServlet {
    
    
    //1.创建 Jackson 的核心对象
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
      //2.读取body中的请求,使用 objectMapper 来解析成需要的对象
      //readValue:将 json格式的字符串转换成 java的对象
      //第一个参数:对哪个字符串转换(形如:{"userId":"123","classId":"456"}),//第二个参数:转换成哪个 java 对象(根据键值对里的key与user类里面的属性进行对比,看是否匹配,
      //如果匹配就将此key对应的value赋值给此时的属性,同时进行类型转换。不匹配就跳过,进行下一个key)
        User user = objectMapper.readValue(req.getInputStream(),User.class);//通过反射可以拿到这个类对象,获取里面所以的属性

        resp.getWriter().write("userId:" + user.userId + ", classId:" + user.classId);
    }
}

注意:

  1. 引入jackSon三方库后,创建ObjectMapper 对象,并调用readValue方法将 json格式的字符串转换成 java的对象.其中第一个参数:对哪个json字符串转换,第二个参数:转换成哪个 java 对象

  2. 因为要将 json格式的字符串转换成 java的对像,所以需要提前准备一个User类。然后根据键值对里的key与user类里面的属性进行对比,看是否匹配,如果匹配就将key对应的value赋值给User类中的属性同时进行类型转换,若不匹配就跳过取下一个key,最终就会构造一个User对象

  3. readValue 的第二个参数为 User 的 类对象. 通过这个类对象, 在 readValue 的内部就可以借
    助反射机制来构造出 User 对象, 并且根据 JSON 中key 的名字, 把对应的 value 赋值给
    User 的对应字段.

或者说:

User.class就是通过反射来拿到User类当中的属性,然后拿json字符串中的key值与这些属性进行匹配,若匹配就将key对应的value赋值给这个属性,最终将所有 json 键值对放到了一个User对象,需要用的时候就直接User.属性


前端:同样在webapp中创建test.html文件并写入以下代码:

    <input type="text" id="userId">
    <input type="text" id="classId">
    <input type="button" value="提交" id="submit">

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

    <script>
        let userIdInput = document.querySelector('#userId');
        let classIdInput = document.querySelector('#classId');
        let button = document.querySelector('#submit');

        button.onclick = function(){
    
    
            $.ajax({
    
    
                type:'post',
                url:'postJson',
                contentType: 'application/json',
                //post这样的请求,ajax允许用data来构造请求的body部分(此时构造的其实是一个js对象,通过JSON.stringfy将这个js对象转换成一个json格式的字符串)
                data:JSON.stringify({
    
    
                    userId:userIdInput.value,
                    classId:classIdInput.value
                }),
                success:function(body){
    
    
                    console.log(body);
                }
            })
        }
    </script>

注意:

  1. 此时对于post这样的请求,ajax就允许使用data属性来构造请求中的body部分。通过JSON.stringfy来把这个js对象转换成一个字符串,而这个字符串就是json格式

在浏览器中输入URL访问页面:

在这里插入图片描述

通过抓包来观察:

请求
在这里插入图片描述

响应
在这里插入图片描述


三、HttpServletResponse

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

3.1 核心办法

在这里插入图片描述

注意:

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

3.2 代码示例

3.2.1 设置状态码

@WebServlet("/statusServlet")
public class StatusServlet extends HttpServlet {
    
    
    @Override
    protected 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);
    }
}

部署程序, 在浏览器中通过 URL http://127.0.0.1:8080/ServletHelloWorld/statusServlet?
status=200 访问, 可以看到

在这里插入图片描述

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

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

@WebServlet("/redirectServlet")
public class RedirectServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
    
    
        resp.sendRedirect("http://www.sogou.com");
    }
}

部署程序, 通过 URL http://127.0.0.1:8080/ServletHelloWorld/redirectServlet 访问, 可以看
到, 页面自动跳转到 搜狗主页 了

抓包结果:

在这里插入图片描述

在这里插入图片描述

3.2.3 自动刷新
实现一个程序, 让浏览器每秒钟自动刷新一次. 并显示当前的时间戳

@WebServlet("/autoRefreshServlet")
public class AutoRefreshServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
    
    
        resp.setHeader("Refresh", "1");
        long timeStamp = new Date().getTime();
        resp.getWriter().write("timeStamp: " + timeStamp);
    }
}

1.通过 HTTP 响应报头中的 Refresh 字段, 可以控制浏览器自动刷新的时机.
2.通过 Date 类的 getTime 方法可以获取到当前时刻的毫秒级时间戳

部署程序, 通过 URL http://127.0.0.1:8080/ServletHelloWorld/autoRefreshServlet 访问, 可
以看到浏览器每秒钟自动刷新一次

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

在这里插入图片描述


猜你喜欢

转载自blog.csdn.net/qq_64317046/article/details/129857802