【JavaWeb】Servlet API详解

目录

前言

一、HttpServlet

1.1、HttpServlet的方法

1.2、Servlet的生命周期

1.3、HttpServlet方法的代码演示及一些问题

 二、HttpRequest

2.1、核心方法

2.2、使用HttpRequest获取请求中的各种信息

​编辑 2.3、前端给后端传数据的三种方式

2.3.1、通过Query String传递

2.3.2、通过body传参(form表单)

2.3.3、通过body传参(json)重点 

三、HttpServletResponse

 3.1、核心方法

3.2、代码展示

 四、简单的留言墙网站


前言

        在上一篇博客中,我们已经介绍了Tomcat和Servlet的基本使用,在本篇博客中,将继续接上一篇博客继续深入介绍Servlet,主要介绍一下Servlet的一些常用API。

一、HttpServlet

        HttpServlet这个类我们在前面的Servlet版本的Hello World就已经使用过了,我们一般都是继承HttpServlet类,接着重写里面的一些方法就可以了。

1.1、HttpServlet的方法

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

收到其他请求的时候调用(由 service 方法调用)

 在实际开发中,我们一般都是重写do×××方法,而不会重写init/destory/service。

1.2、Servlet的生命周期

        生命周期:这些方法的调用时机, 就称为 "Servlet 生命周期". (也就是描述了一个 Servlet 实例从生到死的过程).

        这是一个经典的面试题:①在HttpServlet实例化之后,首次收到匹配的请求(也就是首次收到Http请求时)的时候会执行一次init方法;②destory方法是结束之前调用一次;③service会在每次收到路径匹配时调用一次。 

        这里虽然是说到destroy方法会在结束之前会执行一次,但是是否真的会调用,还是有条件的。具体什么条件可以接着往下看。 

1.3、HttpServlet方法的代码演示及一些问题

        在下面的代码中我们重写了多个do方法,接下去我们就通过Postman来构造请求,看看响应的结果。

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("/test")
public class TestDo extends HttpServlet {
    @Override
    public void init() throws ServletException {
        System.out.println("这是init方法");
    }

    @Override
    public void destroy() {
        System.out.println("这是destroy方法");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("调用GET方法");
        resp.getWriter().write("调用GET方法");
    }

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

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

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

    @Override
    protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("调用Options方法");
        resp.getWriter().write("调用Options方法");
    }
}

通过以上图片发现的两个问题:

        init方法的调用时机和destroy方法存在的问题:以上图片我们分别构造了一个post和options请求,并且强制结束之后的结果。我们可以看到服务器端先调用了一次init方法,并不是一执行起来就调用的,而是收到第一次请求之后才调用的,并且只调用一次。强制结束之后却没有调用destroy方法,因此,destroy其实是不靠谱的,我们不应该去依赖destroy。destroy只有通过8005管理端口来停止服务器才会执行到,但是一般我们都是直接停止服务器的,所以是不会执行destroy方法的。

        Postman收到的响应乱码问题:通过图片我们可以看出Postman收到的响应中莫名出现了很多的问号,而且原本该出现的中文却都没了,其实这里就是出现了乱码的情况,这里乱码的原因就是IDEA和Postman编码方式和解码方式的不同导致的,我们IDEA这边使用的utf8的编码方式。而Postman使用的是gbk的编码方式,这必然就会出现乱码。我们只需要在IDEA中显示的告诉对方使用utf8编码方式解码,乱码的问题自然就解决了。只要加上以下语句即可:resp.setContentType("text/html; charset=utf8");

 二、HttpRequest

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

2.1、核心方法

方法描述
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 对象.

2.2、使用HttpRequest获取请求中的各种信息

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.Enumeration;

@WebServlet("/showRequest")
public class ShowRequest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //将响应设置成html格式
        resp.setContentType("text/html;charset=utf8");
        //在doGet中获取请求中的键值对
        StringBuilder result = new StringBuilder();
        result.append(req.getProtocol());//获取协议名称及版本号
        result.append("<br>");
        result.append(req.getMethod());//获取请求的方法名
        result.append("<br>");
        result.append(req.getRequestURI());//获取请求的URI
        result.append("<br>");
        result.append(req.getContextPath());//获取指示请求上下文的请求URI 部分
        result.append("<br>");
        result.append(req.getQueryString());//获取请求中包含在路径后的查询字符串
        result.append("<br>");
//        resp.getWriter().write(result.toString());//将result写入到响应中,并显示出来
        result.append("------------------------------<br>");//分割线
        //获取全部的head中的键值对
        Enumeration<String> headerNames = req.getHeaderNames();//获取head中的所有键
        while (headerNames.hasMoreElements()){//循环遍历到每一个head
            String headerName = headerNames.nextElement();//先获取到header中的key
            String headValue = req.getHeader(headerName);//通过键获取值
            result.append(headerName+":"+headValue+"<br>");
        }
        resp.getWriter().write(result.toString());
    }
}

运行效果:(跟抓包看到的结果是一样的) 

 2.3、前端给后端传数据的三种方式

        前端给后端传递数据主要有三种通过Query String直接在URI后加上对应的键值对即可,也可以通过body的来传递,body又分为通过form和json来传参。接下来我们就逐一来介绍一下具体该如何传递。

2.3.1、通过Query String传递

        通过Query String传参实际上就是直接在输入的网址中加入对应的键值对,在二级路径后加上英文问号,接着就可以书写键值对了。

2.3.2、通过body传参(form表单)

代码如下所示,分别是doGet方法和doPost方法: 

 这里我们通过Postman来构造一个form表单类型的doPost请求就可以了。如下所示:

        假设我们在后端这边没设置解码方式的话,使用汉字就可能出现乱码的情况,如下所示:(因此我们如果需要使用汉字就需要注意编码和解码的问题,否则非常容易出现乱码问题。)

        注意事项:当我们通过Query String传递参数时,如果涉及到使用汉字进行传参,为了保险起见,可以先把汉字通过转码工具转成编码来传参,如下所示:(以下是将张三转成了对应的编码来传参):

2.3.3、通过body传参(json)重点 

        json本身也是键值对格式的数据,但是Servlet本身没有内置json解析的功能,因此我们需要引入第三方库,常用的解析json的第三方库有很多,比如fastjson,gson,jackson等等,这里我们使用jackson,jackson也是Spring官方指定的。

第一步:引入jackson

引入jackson我们可以去MVN中央仓库引入即可

 点击进去之后有很多的版本,这些版本都差别不大,任选一个下载就可以:

        复制进去之后点击刷新之后就会自动下载,等到字体变白了就可以了,太久没变白也可以尝试重启以下IDEA就可以了。

解析json格式数据代码展示:

import com.fasterxml.jackson.core.FormatSchema;
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.HashMap;


class User{
    public String username;
    public String password;
}
@WebServlet("/json")
public class JsonServlet extends HttpServlet {
    //ObjectMapper是一个可以将Java对象和json格式互转的对象,json中的一个核心类
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //使用readValue方法来将请求中的json格式数据转成Java对象,并且存入对象中
        //使用HashMap来接收对象
//        HashMap<String,String> info = objectMapper.readValue(req.getInputStream(), HashMap.class);
//        System.out.println(info);
        //也可以创建一个类来接收json格式的数据
        User user = objectMapper.readValue(req.getInputStream(), User.class);
        System.out.println("username="+user.username+";password="+ user.password);
        resp.getWriter().write("ok");
    }
}

        对于代码中的readValue方法的解释:readValue方法有很多重载的方法,其中我们上面使用的都是第一个参数都是传入的是一个字节流对象第二个参数是一个类对象,在这里使用Map和自己创建的类都是可以的,但是使用自己创建的类的话会相对灵活一点。相应的,writeValue就是将我们这边的数据转成json格式数据并写进响应中。

        readValue方法在解析过程中到底做了什么:

        ①解析json字符串,转换成若干键值对;

        ②根据第二个参数,去找到类对象中的所有public属性,一次遍历;

        ③遍历属性,根据属性名字,去上述准备好的键值对中,查询,匹配,看看这个属性名字是否存在对应的value,如果存在及将value的值赋给该属性。(这里类里面的属性名和传过来的json数据中键的属性名要一致才可以,需要事先约定好)

三、HttpServletResponse

 3.1、核心方法

方法描述
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 中写入二进制格式数据.

        注意: 响应对象是服务器要返回给浏览器的内容, 这里的重要信息都是程序猿设置的. 因此上面的方 法都是 "写"(set) 方法. 

3.2、代码展示

以下是设置自动刷新及设置状态码:

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("/status")
public class StatusServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置状态码
        resp.setStatus(200);
        resp.setContentType("text/html;charset=utf8");
        resp.getWriter().write("返回200响应<br>");
        //设置自动刷新,显示当前时间戳
        resp.setHeader("Refresh","1");
        resp.getWriter().write(String.valueOf(System.currentTimeMillis()));
    }
}

效果展示:这里时间戳是会一直刷新的

 设置重定向跳转:以下代码的效果是输入之后直接跳转到搜狗主页

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.sogou.com");
        //resp.sendRedirect("https://www.sogou.com");//和上面的两行代码是等效的
    }
}

输入对应的网址后就会跳转到搜狗主页,这就是重定向: 

 四、简单的留言墙网站

网站介绍:

        这个留言墙的网站主要是基于Servlet的,主要的功能就是:进入网站之后,就将所有的留言展示出来,留言的数据是保存在数据库中的。。每次访问网站都会将数据库中已有的数据加载出来,不提供修改留言和删除留言的功能,只能新增留言,这个练习的目的就是为了练习使用Servlet来进行前后端的交互。

网站的效果图: 

 网站的源代码可以参照我的gitee链接: MessageWall · 白志彬/java -code - 码云 - 开源中国 (gitee.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值