Servlet API 详解

目录

1. HttpServlet

1.1 Postman 构造请求

2. HttpServletRequest

2.1 HttpServletRequest 获取请求中传来的必要参数

2.1.1 获取 POST 请求中 application/x-www-form-urlencoded 格式的参数

 2.1.2 获取 POST 请求中 application/json 格式的参数

3. HttpServletRespnse

3.1 代码案例1 - 设置状态码

3.2 代码案例2 - 自动刷新

3.3 代码案例3 - 重定向


1. HttpServlet

Servlet 代码的时候 , 首先第一步就是先创建类 , 继承自 HttpServlet, 并重写其中的某些方法 .
核心方法
方法名称
调用时机
init只会在该 Servlet 类第一次被使用的时候调用到, 相当于是用来初始化
destroy
在 Servlet 对象被销毁的时候,才会调用到,相当于收尾操作。
service
每次收到请求 (无论是啥方法) 都会调用 service. 默认父类的 service 就会根据方法来调用 doGet, doPost, doPut.......
doGet
收到 GET 请求的时候调用 ( service 方法调用 )
doPost
收到 POST 请求的时候调用 ( service 方法调用 )
doPut/doDelete/doOptions/...
收到其他请求的时候调用 ( service 方法调用 )

上述方法中, 最常用的还是 doXXX系列的, init, destroy, 这些其实很少用...

【问题】上一篇博客我们已经通过浏览器输入 URL 构造 GET 请求了, 那么 POST 和 其他请求如何构造 ??

如果使用 form 表单的话, 就只能构造 GET 和 POST 请求, 所以这里我们使用 ajax 来构造.

doXXX代码:

@WebServlet("/hello") // 注解, 约定 Http 请求的 URL 是什么 path, 才会调用到当前的这个 Servlet 类
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 打印在服务器的控制台上
        System.out.println("doGet");
        // 此处是吧 "hello world" 字符串作为响应报文的 body 了, 浏览器就会把这个 body 显示到压面中
        resp.getWriter().write("doGet");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doPost");
        resp.getWriter().write("doPost");
    }

    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doPut");
        resp.getWriter().write("dpPut");
    }

    @Override
    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doDelete");
        resp.getWriter().write("doDelete");
    }
}

ajax代码:

    <div class="one">

    </div>
    <button id="doGet">get</button>
    <button id="doPost">post</button>
    <button id="doPut">put</button>
    <button id="doDelete">delete</button>
    <!-- 加载 jQuery -->
    <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
    <script>
        // GET 请求
        let doGetBtn = document.querySelector("#doGet");
        doGetBtn.onclick = function() {
            $.ajax({
                type: 'get',  // 请求类型
                url: 'hello', // 相对路径
                success: function(body) {
                    // console.log(body);
                    let div = document.querySelector(".one");
                    div.innerHTML = body;
                }
            });
        }
        // POST 请求
        let doPostBtn = document.querySelector("#doPost");
        doPostBtn.onclick = function() {
            $.ajax({
                type: 'post',
                url: 'hello', // 相对路径
                success: function(body) {
                    // console.log(body);
                    let div = document.querySelector(".one");
                    div.innerHTML = body;
                }
            });
        }
        // PUT 请求
        let doPutBtn = document.querySelector("#doPut");
        doPutBtn.onclick =  function() {
            $.ajax({
                type: 'put',
                url: 'hello',
                success: function(body) {
                    let div = document.querySelector(".one");
                    div.innerHTML = body;
                }
            });
        }
        // DELETE 请求
        let doDeleteBtn = document.querySelector("#doDelete");
        doDeleteBtn.onclick =  function() {
            $.ajax({
                type: 'delete',
                url: 'hello',
                success: function(body) {
                    let div = document.querySelector(".one");
                    div.innerHTML = body;
                }
            });
        }
    </script>

1. 我们把 html 文件放到 webapp 目录下, 此时该 html 也就相当于是 webapp 的一个部分了, 通过浏览器访问这个页面的时候, 就必须要带上这里的 Content Path.

2. 通过浏览器输入 URL 构造的请求得到的响应内容, 就是直接被浏览器渲染到界面上, 而通过 ajax 拿到的响应数据, 是由回调函数来处理的, 既可以直接渲染到界面, 也可以输出在控制台, 也可以不输出.

3. ajax 代码中的 url 写成 hello 是相对路径的写法, 指明我们要访问的是 /hello_servlet2/hello 这个路径, 而 ajax 代码是处在 hello_servlet2/test.html 这个路径下, 此时相当于两者在同级目录中, 所以直接写 hello 即可, 千万不要加 /.

运行结果:

 可以通过 fiddler 抓包查看请求报文, 理解过程:

1.1 Postman 构造请求

上述通过 ajax 构造请求固然可以, 但是还是要写代码的, 还是不够方便,于是我们可以使用 Postman 来构造请求, 我们只需要输入 URL , 并指定请求类型, 然后点击发送, 就看可以看到服务器返回的响应了. 

Postman 不仅可以构造 http 请求, 还可以根据请求导出相应的代码 (上图中右上角), 所以如果我们在编写前端代码忘记怎么写的时候, 可以直接复制张贴即可. 这个东西用起来还是挺香的!!
经典面试题: 谈谈 Servlet 的生命周期.
此处考察的就是什么时机, 调用什么方法.
1. 首次使用, 先调用一次 init .
2. 每次收到请求, 就会调用 service , 在 service 内部通过方法决定调用哪个 doXXX 方法.
3. 销毁之前调用 destroy 方法.

2. HttpServletRequest

HttpServletRequest 表示一个 http 请求

http 请求中主要有

1. 方法

2. url

3. 版本号

4. 各种 header

5. body

这些信息都包含在 HttpServletRequest 对象里头了.

核心方法
方法
描述
String getProtocol()
返回请求协议的名称和版本。
String getMethod()
返回请求的 HTTP 方法的名称,例如,GETPOST 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("/showRequest")
public class ShowRequestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(req.getProtocol());
        stringBuilder.append("<br>");
        stringBuilder.append(req.getMethod());
        stringBuilder.append("<br>");
        stringBuilder.append(req.getRequestURI());
        stringBuilder.append("<br>");
        stringBuilder.append(req.getContextPath());
        stringBuilder.append("<br>");
        stringBuilder.append(req.getQueryString());
        stringBuilder.append("<br>");

        Enumeration<String> headerNames = req.getHeaderNames();
        while(headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            stringBuilder.append(name + ": " + req.getHeader(name));
            stringBuilder.append("<br>");
        }
        // 指定响应报文的数据格式
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write(stringBuilder.toString());
    }
}

运行结果:

 

上述代码获取到请求信息, 返回响应之前, 要记得指定响应报文的数据格式 (和字符编码) , 否则返回的响应在控制台上就不会一行一行的显示, 而是挤在一堆, 可以通过 fiddler 抓包进行查看响应报文中是否有 Content-Type.

2.1 HttpServletRequest 获取请求中传来的必要参数

虽然我们前面通过 HttpServleRequest 的一些方法可以获取到一个请求中的各方面信息, 但更多情况下, 是希望通过 api 拿到请求中传来的必要参数.

假设客户端给服务器发送形如 /studentInfo?classId=123&studentId=101, 服务器这边就需要知道传过来的这两关键信息是啥, 然后通过班级 id, 学生 id 就可以确定班里的某一个人.

代码如下

@WebServlet("/studentInfo")
public class StudentInfoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 假设客户端发来的请求形如: / studentInfo?ClassId=10&studentId=20
        // 通过 getParameter 方法来拿到这两个参数
        String queryString = req.getQueryString();
        System.out.println(queryString);
        String classId = req.getParameter("classId");
        String studentId = req.getParameter("studentId");
        // 输出在服务器的控制台
        System.out.println("classId: " + classId + " studentId: " + studentId);
        // 输出到界面
        resp.getWriter().write("classId: " + classId + " studentId: " + studentId);
    }
}

在浏览器中构造 URL 请求时添加具体的 query string ,形如 : http://localhost:8080/hello_servlet2/studentInfo?classId=123&studentId=101

getParameter 的作用就是获取 query string 中的键值对, 根据 key 获取 value, 如果没有这个 key ,就返回 null.

前端除了通过 query string 来传参, 还有其他的传参方式, 例如通过 POST 请求的 body 传参到服务器, 两种数据格式 : 1.application/x-www-form-urlencoded  2.application/json.

2.1.1 获取 POST 请求中 application/x-www-form-urlencoded 格式的参数

服务器代码:

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 正对这种格式, 获取到值得方式, 仍然是 getParameter
        String classId = req.getParameter("classId");
        String studentId = req.getParameter("studentId");
        System.out.println("classId " + classId + " studentId: " + studentId);
        resp.getWriter().write("classId " + classId + " studentId: " + studentId);
    }

🍁Postman 构造 POST 请求

然后通过 fiddler 抓包进行查看请求报文:

本质上 urlencoded 的数据格式还是以键值对的方式进行构造...

🍁form 表单构造 POST 请求

    <form action="studentInfo" method="post">
        <input type="text" name="classId">
        <input type="text" name="studentId">
        <input type="submit" value="提交">
    </form>

 不仅要会 Postman 构造 POST 请求, 也要熟悉 form 表单构造出 POST 请求, 可以通过 fiddler 抓包进行查看:

 

 2.1.2 获取 POST 请求中 application/json 格式的参数

🍁Postman 构造 POST 请求

显然我们解析不到这俩参数, 因为我们还是按照 urlencoded 的格式对数据进行解析的.

通过 fiddler 进行抓包查看:

Content-Type 确实是 json , body 中数据也是按照 json 的格式来组织, 现在主要的问题就是如何在 服务器端把 body 中的键值对给获取到....

但是 json 这种格式解析起来并不容易, 如果还是通过写代码按照字符串的方式获取到这里的键值对, 将会非常费劲!!, 因为 json 格式支持一些更复杂的结构, 它的格式之间还可以嵌套,  value 的类型不仅仅可以是数字, 字符串, 还可以是 json, 还可以是数组, 形如下图:

所以, 更靠谱的处理方式, 就是使用 Spring 官方推荐使用的 Jackson 第三方库来解决. 在 maven 仓库中找到 Jackson Databind , 随便选中一个版本, 将其拷贝到 pom.xml 文件中的 <dependencies> 标签中进行下载即可.

 获取 json 中参数的代码:

// 此处这个类的属性的命名,要和 json 的 key 保持一致
// 并且属性的访问权限是 public, 或者提供公开的 getter 方法
class Student {
    public int classId;
    public int studentId;
}
@WebServlet("/studentInfo")
public class StudentInfoServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 处理 JSON 格式的请求, 从请求 body 中进行读取, 并解析
        ObjectMapper objectMapper = new ObjectMapper();
        // 使用 readValue 来把 json 字符串转成 Java 对象
        // 第一个参数是一个 String 或者 InputStream , 第二个参数是 json 转换的结果对应的 Java 类对象
         Student student = objectMapper.readValue(req.getInputStream(), Student.class);
         System.out.println(student.classId + ", " + student.studentId);
         resp.getWriter().write(student.classId + ", " + student.studentId);
         // objectMapper.writeValue(); Java 对象转成 json 字符串
    }
}

通过上述代码发现 jackson 的使用方法还是非常简单的, 只需要掌握两个操作:

1. 把 json 格式的字符串转成 Java 对象

2. 把 Java 对象转成 json 字符串 

这两个操作对应到 ObjectMapper 类提供的两个方法.


3. HttpServletRespnse

Servlet 中的 doXXX 方法的目的就是根据请求计算响应 , 然后把响应的数据设置到 Response 对象中 . 然后 Tomcat 就会把这个  Response 对象按照 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 中写入二进制格式数据.

3.1 代码案例1 - 设置状态码

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

部署程序, 在浏览器中通过 URL http://127.0.0.1:8080/hello_servlet2/status 访问, 可以看到如下界面:

通过 fiddler 抓包查看:

 

3.2 代码案例2 - 自动刷新

@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(System.currentTimeMillis() + "");
    }
}

部署程序, 在浏览器中通过 URL http://127.0.0.1:8080/hello_servlet2/refresh 访问, 可以看到如下界面, 时间戳每秒钟都在变化..

 通过 fiddler 抓包查看:

 

3.3 代码案例3 - 重定向

@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1.设置状态码为 3xx (典型: 302)
        resp.setStatus(302);
        // 2.给 header 里设置一个 Location ,表示跳转到的页面
        resp.setHeader("Location", "https://www.sogou.com");

        // 上面两行代码可以被下面一行代码代替
        // resp.sendRedirect("https://www.sogou.com");
    }
}

部署程序, 在浏览器中通过 URL http://127.0.0.1:8080/hello_servlet2/redirect 访问, 可以看到如下界面: 

 通过 fiddler 抓包查看:


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Master_hl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值