Servlet API详解

在这里插入图片描述

一、Servlet运行原理

细心的同学会发现,我们的Servlet代码中并没有写main方法,那么我们的doGet代码是如何被调用的?相应又是如何返回给浏览器的?

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

接受请求:
我们在浏览器输入一个URL,浏览器就会构造一个HTTP请求,这个请求会经过网络协议栈逐层的进行封装直到bit流,通过物理层硬件设备转换为光/电信号传输出去,服务器收到该信号后,又通过网络协议栈逐层分用,层层解析,最终还原成HTTP请求交给Tomcat进行处理,Tomcat通过socket读取到该字符串,按照HTTP请求的格式来解析该请求,根据Context path确定一个webapp,再通过Servlet path确定一个具体的类,然后再根据HTTP请求的方法,确定该类的具体方法,我们的方法中的HttpServletRequest中就包含该HTTP请求的详细信息
计算响应:
我们在方法中,根据请求,然后计算响应
返回响应:
我们的方法执行完,Tomcat会自动的把HttpServletResponse我们设置的对象转换成一个HTTP协议的字符串,通过Socket将该响应发出去,层层封装最后浏览器获取到HTTP响应,浏览器的Socket读到该响应,按照HTTP响应的格式来解析该响应,并把body中的数据按照一定的格式显示出来

二、Servlet API

HttpServlet

核心方法:

方法 调用时机
init 在HttpServlet实例化之后被调用一次
destroy 在HttpServelet实例不再使用时调用一次
service 收到HTTP请求时调用
doGet 收到GET请求时调用
doPost 收到POST请求时调用

doPut,doDelete…也是同理,在收到对应请求时调用,由service调用
init方法: 该方法是在tomcat首次收到了该类相关联(访问/hello路径的请求)的请求时,就会调用到HelloServlet,就需要先对HelloServlet进行实例化,后续在收到请求时,不必再实例化了,直接复用之前的HelloServlet实例即可,只执行一次

destroy方法: 当HttpServlet实例不再使用时调用该方法,啥时候该实例就不再使用了?服务器只要不停止,该实例就一直被使用,只有当服务器停止后了,才会调用该方法,只执行一次
这里的destroy能否被执行到,是存在争议的:
如果是通过停止按钮,这个本质操作是通过tomcat的8005端口,主动停止,才能触发destroy
如果是直接杀死进程,此时就来不及执行destroy
所以不建议在destroy内执行有效代码

service: 收到HTTP请求就会调用
在这里插入图片描述
Service中根据请求的类型不同,调用不同的方法,doGet,doPost方法等等,会执行多次,每收到一次HTTP请求就执行一次
Servlet的生命周期:
1.开始的时候执行init
2.每次收到请求后,执行service
3.销毁之前执行destroy

处理请求

我们重写doGET方法,当浏览器使用GET方法访问该Servlet路径(method)时,我们在控制台打印hello GET,并给浏览器发一个hello GET响应
在这里插入图片描述

在这里插入图片描述
我们在webapp目录下创建testMethod.html,我们的Servlet程序中可以同时部署静态文件,静态文件我们放在webapp目录下即可
我们编写前端代码,一般使用VScode编写
在这里插入图片描述
我们打开文件路径后,直接右键打开方式VScode打开即可,然后我们开始使用ajax构造请求发送给服务器
我们首先引入jquery,我们在前面已经教过大家,在浏览器搜索 query cdn即可

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    <script>
        $.ajax({
      
      
            type: 'get',
            url: 'method',
            success: function(body,status) {
      
      
                console.log(body);
            }
        });
    </script>

然后我们启动tomcat,使用浏览器访问testMethod.html
在这里插入图片描述
我们打开开发者工具看一下
在这里插入图片描述
在这里插入图片描述
大家需要注意这里的访问路径,我们这里访问的是testMethod.html文件
在这里插入图片描述
我们这里的method其实写的是相对路径,基准路径就是Html所在的路径,大家也可以写作绝对路径
我们想要处理其他方法也是同理,在html中type类型修改,servelet方法中重写对应方法即可,我们再来演示一个
在这里插入图片描述
在这里插入图片描述
我们每次修改完代码之后都需要重启服务器
在这里插入图片描述
在这里插入图片描述

三、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() 返回请求body的长度
InputStream getInputStream() 用于读取请求的 body 内容. 返回一个 InputStream 对象

我们query string 是键值对结构,我们可以通过getParameter根据key获取到value
我们接下来就使用这些方法来将请求信息打印到浏览器上

@WebServlet("/showRequest")
public class ShowRequest extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //指定返回响应的格式
        resp.setContentType("text/html; charset = utf8");
        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>");
        stringBuilder.append("<br>");
        Enumeration<String> headerNames = req.getHeaderNames();
        while(headerNames.hasMoreElements()) {
    
    
            String headerName = headerNames.nextElement();
            stringBuilder.append(headerName+": "+req.getHeader(headerName));
            stringBuilder.append("<br>");
        }
        resp.getWriter().write(stringBuilder.toString());
    }
}

在这里插入图片描述

前后端交互

1.GET通过query string
2.POST通过form
3.POST通过json
我们下来演示一下上述三种方法,前端给后端传参,我们后端获取到数据
GET query string
我们前端这里直接通过地址栏构造一个URL发送给后端,useId = 10 & classId = 001,我们后端使用getParameter()方法获取到数据然后响应给浏览器

@WebServlet("/getParameter")
public class GetParameterServelet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //使用getParameter获取前端query string的数据 useId = 10 & classId = 001
        String userId = req.getParameter("useId");
        String classId = req.getParameter("classId");
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write(userId +", " + classId);
    }
}

在这里插入图片描述
这里请求中的query string键值对会被tomcat处理成形似map结构的数据,我们就可以通过key去获取value了,需要注意的是我们这里的value都是String类型的,如果我们getParameter的参数前端并没有传递,那么我们的value就是null
POST form
我们前端使用form构造POST请求,我们后端仍然是使用getParameter来获取,因为它的数据也是键值对,只不过这部分是在body中
在这里插入图片描述
我们在该HTML文件中构造一个form表单

    <form action="getParameter" method="post">
        <input type="text" name="useId">
        <input type="text" name="classId">
        <input type="sumbit" value="提交">
    </form>

在这里插入图片描述
我们来提交一组数据
在这里插入图片描述
我们可以发现报了405错误,因为我们servlet没有重写doPOST方法
在这里插入图片描述
我们接下来重写doPost方法

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        String userId = req.getParameter("useId");
        String classId = req.getParameter("classId");
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write(userId +", " + classId);
    }

在这里插入图片描述
此时我们就可以获取到前端数据,并且响应给浏览器,使用getParameter,既可以获取到query string中的键值对,也可以获取到form表单body中的键值对
POST json
json是目前比较主流的一种数据格式,也是键值对格式,我们可以将body按照该格式组织,我们在前端既可以使用ajax的方式构造,也可以使用postman直接构造.
我们先实现一下后端处理逻辑

@WebServlet("/postParameter2")
public class PostParameter2 extends HttpServlet {
    
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //读取body为json的数据
        //这里我们使用getInputstream 将body数据读出来
        int length = req.getContentLength();
        byte[] buffer = new byte[length];
        InputStream inputStream = req.getInputStream();
        inputStream.read(buffer);
        String body = new String(buffer,0,length,"utf8");
        System.out.println("body= "+body);
        resp.getWriter().write(body);
    }
}

然后我们在postman构造一下josn格式的请求,大家需要注意一些格式细节
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们可以看到我们读取到的body数据,我们也可以抓包来看一下。
在这里插入图片描述
我们这个代码的执行流程和form表单传参是类似的,只不过是传输的数据格式是不同的,但是我们当前通过Json传输数据时,我们服务器只是把整个body读出来了,并没有按照键值对的方式来处理,这里我们使用第三方库比较合适:jackson,我们通过maven引入该依赖
在这里插入图片描述
在这里插入图片描述

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.14.1</version>
</dependency>

在这里插入图片描述
我们在pom.xml引入该依赖,记得刷新,我们现在读取body为json格式的数据时,就可以简化了

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

我们这里需要根据前端json中的key来设置我们类中的属性,一一对应,如果前端传的参数多了,会报500错误
在这里插入图片描述
ObjectMapper是我们jackson中涉及到的核心对象
这一步操作我们实际会做以下操作:
1.会将body中json格式的数据取出
在这里插入图片描述
2.根据第二个类参数,创建Student实例
3.根据json格式的字符串,处理成map键值对结构
4.遍历键值对,看键的名字是否与Student实例的属性名匹配,如果匹配就将value赋值给该属性
5.返回该Student对象
在这里插入图片描述
我们写好处理逻辑后,前端再发一次请求
在这里插入图片描述

四、HttpServletResponse

核心方法:

方法 描述
void setStatus(int sc) 给响应设置状态码
void setHeader(String name,String value) 设置一个header,如果存在覆盖value
void addHeader(String name,String value) 添加一个带有给定的名称和值的 header. 如果 name 已经存在,不覆盖旧的值, 并列添加新的键值对
void setCharacterEncoding(String charset) 设置被发送到客户端响应的字符编码
void sendRedirect(String location) 使用重定向位置URL发送临时重定向给客户端
PrintWriter getWriter() 往body中写入文本数据
OutputStream getOutputStream() 往body写入二进制数据

我们来演示一下部分方法:在这里插入图片描述
我们之前处理Get请求的方法,我们在写回数据时,我们加入中文会怎样?
在这里插入图片描述
在这里插入图片描述

因为我们没有指明编码方式,此时浏览器只能随便指定一个编码方式,出不出现乱码完全是运气问题,所以我们需要指明编码方法
在这里插入图片描述
大家需要注意这里是text/html而不是test/html
在这里插入图片描述
当我们指定后,浏览器就能正确识别中文相应了

        resp.setContentType("text/html; charset=utf8");

我们也可以这样指定。
我们现在在来演示一下重定向方法,我们就来实现,当有访问该servlet路径时,我们跳转到百度主页
在这里插入图片描述
当我们访问redirect路径时,我们就会跳转到百度主页,我们来抓包看一下
在这里插入图片描述
在这里插入图片描述
这里请求就是一个正常的请求,正常的访问servlet路径
我们可以发现响应是一个302类型,临时重定向,loaction代表的是我们要定向到那个路径

猜你喜欢

转载自blog.csdn.net/buhuisuanfa/article/details/129734446