16. Servlet 知识点

目录

一、动态页面 vs 静态页面

二、Servlet

1.编写一个Servlet代码

2.web.xml

3.在浏览器中输入127.0.0.1:8088/hello

4.Tomcat内部的大概流程

5.Servlet运行原理

5.1接受请求

5.2根据请求计算响应

5.3返回响应

三、HttpServletRequest 对象的使用

1.读取请求信息

2.读取请求体

3.1按字节读取

3.2按字符读取 

四、HttpServletResponse 对象的使用

1.读取网页中用户填写的信息

1.1获取GET请求信息 --- form表单姓名密码

1.2获取POST请求信息 --- form表单姓名密码

1.3标准的读取参数

五、GET方法和POST方法的区别


一、动态页面 vs 静态页面

  • 静态页面也就是内容始终固定的页面. 即使 用户不同/时间不同/输入的参数不同 , 页面内容也不会发生变化. (除非网站的开发人员修改源代码, 否则页面内容始终不变)
  • 动态页面指的就是 用户不同/时间不同/输入的参数不同, 页面内容会发生变化

二、Servlet

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

通过继承HttpServlet抽象类完成我们类的编写

重写其中的(get()、post()),输出资源内容的过程

把动态资源和路径建立绑定关系1.通过web.xml2.使用Java中的注解语法@WebServlet完成

类似下图

1.编写一个Servlet代码

package com.first_webapp.servlet;

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.io.PrintWriter;

/**
 * @author happy
 */
// 6.2 通过用 @WebServlet 注解修饰类,来实现绑定
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(202);
        resp.setContentType("text/css; charset=utf-8");
        PrintWriter writer = resp.getWriter();
        writer.print("你好世界");
    }
}
  • 1、创建一个类HelloServlet , 继承自 HttpServlet
  • 2、在这个类上方加上 @WebServlet("/hello") 注解, 表示 Tomcat 收到的请求中, 路径为 /hello 的请求才会调用 HelloServlet 这个类的代码. (这个路径未包含 Context Path)
  • 3、重写 doGet 方法. doGet 的参数有两个, 分别表示收到的 HTTP 请求 和要构造的 HTTP 响应. 这个方法会在 Tomcat 收到 GET 请求时触发
  • 4、HttpServletRequest 表示 HTTP 请求. Tomcat 按照 HTTP 请求的格式把 字符串 格式的请求转成 了一个 HttpServletRequest 对象. 后续想获取请求中的信息(方法, url, header, body 等) 都是通 过这个对象来获取.
  • 5、HttpServletResponse 表示 HTTP 响应. 代码中把响应对象构造好(构造响应的状态码, header, body 等)
  • 6、resp.getWriter() 会获取到一个流对象, 通过这个流对象就可以写入一些数据, 写入的数据会被 构造成一个 HTTP 响应的 body 部分, Tomcat 会把整个响应转成字符串, 通过 socket 写回给浏览器.

@WebServlet("")中填写的资源路径的问题

1.必须使用 / 开头

2.资源路径不能重复

3.资源路径可以写/hello   /hello/h  /hello.jpg  /hello.html

4.可以使用通配符  *.do  后缀名是.do的资源都可以访问;只出现 / 说明所有的URL都可以访问;只出现 @WebServlet("")  代表在根目录下

2.web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1"
         metadata-complete="false">

    <servlet>
        <servlet-name>MyFirstServlet</servlet-name>
        <servlet-class>com.first_webapp.servlet.HelloServlet</servlet-class>
    </servlet>


    <servlet-mapping>
        <servlet-name>MyFirstServlet</servlet-name>
        <url-pattern>/first</url-pattern>
    </servlet-mapping>
</web-app>

3.在浏览器中输入127.0.0.1:8088/hello

4.Tomcat内部的大概流程

1. Tomcat 收到一个HTTP请求

2. Tomcat 根据 HTTP 协议的规定,解析请求,得到请求方法、资源路径、请求头、请求体等信息

3. Tomcat 根据资源路径(Path = Context Path + Servlet Path)

   3.1先根据Context Path决定这个资源交给哪个Webapp来处理(Tomcat支持多个Webapp             同时存在)

   3.2当确定好Webapp时,根据 Servlet Path找到对应的资源进行处理

          3.2.1如果是动态资源,根据资源得到一个类名(上面例子指

                    com.first_webapp.servlet.HelloServlet)

          3.2.2doGet执行结束后,resp对象得到信息(状态码202,Content-Type是text/css),                        如果这个类没有实例化对象就实例化对象,否则直接获取对象,根据请求方法                        调用该对象的doGet/doPost

          3.2.3如果是静态资源,就根据对应的路径去查找对应的文件,并响应文件内容

          3.2.4如果动态资源/静态资源都没有找到就会404

5.Servlet运行原理

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

5.1接受请求

用户在浏览器输入一个 URL, 此时浏览器就会构造一个 HTTP 请求

这个 HTTP 请求会经过网络协议栈逐层进行 封装 成二进制的 bit 流, 最终通过物理层的硬件设备转 换成光信号/电信号传输出去

这些承载信息的光信号/电信号通过互联网上的一系列网络设备, 最终到达目标主机(这个过程也需要 网络层和数据链路层参与)

服务器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成 HTTP 请求. 并交给 Tomcat 进程进行处理(根据端口号确定进程)

Tomcat 通过 Socket 读取到这个请求(一个字符串), 并按照 HTTP 请求的格式来解析这个请求, 根据 请求中的 Context Path 确定一个 webapp, 再通过 Servlet Path 确定一个具体的 类. 再根据当前请 求的方法 (GET/POST/...), 决定调用这个类的 doGet 或者 doPost 等方法. 此时我们的代码中的 doGet / doPost 方法的第一个参数 HttpServletRequest 就包含了这个 HTTP 请求的详细信息

5.2根据请求计算响应

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

5.3返回响应

我们的 doGet / doPost 执行完毕后, Tomcat 就会自动把 HttpServletResponse 这个我们刚设置好 的对象转换成一个符合 HTTP 协议的字符串, 通过 Socket 把这个响应发送出去

此时响应数据在服务器的主机上通过网络协议栈层层 封装, 最终又得到一个二进制的 bit 流, 通过物 理层硬件设备转换成光信号/电信号传输出去

这些承载信息的光信号/电信号通过互联网上的一系列网络设备, 最终到达浏览器所在的主机(这个过 程也需要网络层和数据链路层参与)

浏览器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成 HTTP 响应, 并交给浏览器处理

浏览器也通过 Socket 读到这个响应(一个字符串), 按照 HTTP 响应的格式来解析这个响应. 并且把 body 中的数据按照一定的格式显示在浏览器的界面上

三、HttpServletRequest 对象的使用

图源icon-default.png?t=M4ADhttp://c.biancheng.net/servlet2/httpservletrequest.html

Servlet 的生命周期主要为三个步骤初始化 init() 、业务处理 service() 、 销毁 destory()

1.读取请求信息

@WebServlet("/req-info")
public class GetRequestInfoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        System.out.println("HTTP 方法: " + method);
        System.out.println();

        String requestURI = req.getRequestURI();
        System.out.println("requestURI: " + requestURI);
        StringBuffer requestURL = req.getRequestURL();
        System.out.println("requestURL: " + requestURL.toString());
        String pathInfo = req.getPathInfo();
        System.out.println("pathInfo: " + pathInfo);
        String contextPath = req.getContextPath();
        System.out.println("contextPath: " + contextPath);
        String servletPath = req.getServletPath();
        System.out.println("servletPath: " + servletPath);
        String pathTranslated = req.getPathTranslated();
        System.out.println("pathTranslated: " + pathTranslated);
        String queryString = req.getQueryString();
        System.out.println("queryString: " + queryString);
        System.out.println();

        String serverName = req.getServerName();
        System.out.println("serverName: " + serverName);
        int serverPort = req.getServerPort();
        System.out.println("serverPort: " + serverPort);
        String scheme = req.getScheme();
        System.out.println("schema: " + scheme);
        String protocol = req.getProtocol();
        System.out.println("protocol: " + protocol);
        System.out.println();

        // 请求头们(本质就是一组 Key-Value;不同之处 Key 是可以重复的)
        Enumeration<String> headerNames = req.getHeaderNames();
        // Enumeration 类似 Iterator(hasNext() + next())
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            String value = req.getHeader(name);
            System.out.println(name + " := " + value);
        }
    }
}

2.读取请求体

GET方法是不携带请求体的,我们需要写一个POST方法来获取请求体

send-post.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post" action="/req-body">
    <input type="text" name="username">
    <input type="text" name="password">
    <button>提交</button>
</form>
</body>
</html>

3.1按字节读取

@WebServlet("/req-body")
public class ReqBodyServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        InputStream inputStream = req.getInputStream();
        byte[] buf = new byte[1024];
        int n = inputStream.read(buf);

        // byte[] -> String  字符集解码
        String reqBody = new String(buf, 0, n, "UTF-8");
        System.out.println(reqBody);

    }
}

3.2按字符读取 

@WebServlet("/req-body")
public class ReqBodyServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        BufferedReader reader = req.getReader();
        char[] buf = new char[1024];
        int n = reader.read(buf);   // n 单位是字符
        String reqBody = new String(buf, 0, n);
        System.out.println(reqBody);
    }
}

我们输入的时候输入的是中文字符,为什么输出就会变成乱码?

 

事实上并不是乱码,是URL编码

四、HttpServletResponse 对象的使用

1.读取网页中用户填写的信息

  • GET方法的参数放在query string中
  • POST方法的参数在query string 和 request body中,必须是form表单提交的POST请求,请求的Content-Type 是 application/x-www-form-urlencoded类型

1.1获取GET请求信息 --- form表单姓名密码

1.请求参数放在URL的query string中,格式是name=value&password=value

2.value不管是中文还是英文都可以正常读取

3.如果只有name或者password没有value读到的是一个空字符串

4.如果name或password都没有那么读取到的是null

@WebServlet("/get-param")
public class GetParam extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        System.out.println("|" + username + "|");
        System.out.println("|" + password + "|");
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="get" action="/get-param">
    <input type="text" name="username">
    <input type="text" name="password">
    <button>提交</button>
</form>
</body>
</html>

 

1.2获取POST请求信息 --- form表单姓名密码

1.请求参数可以放在URL的query string中,也可以放在request body中,用form表单提交

2.value是英文可以正确读取,如果是中文需要设置请求的字符集                                                                             设置请求的字符集编码   req.setCharacterEncoding("UTF-8");

3.如果只有name或者password没有value读到的是一个空字符串

4.如果name或password都没有那么读取到的是null

@WebServlet("/post-param")
public class PostParam extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        System.out.println("|" + username + "|");
        System.out.println("|" + password + "|");
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post" action="/post-param">
    <input type="text" name="username">
    <input type="text" name="password">
    <button>提交</button>
</form>
</body>
</html>

  

输入的是中文为什么会出现乱码?

Tomcat在解析时是不知道请求体的字符集编码是什么,只能按照默认的编码,默认的字符集编码不是UTF-8的所以会出现乱码,在代码中加上req.setCharacterEncoding("UTF-8"); 设置请求的字符集编码

1.3标准的读取参数

public class StandardParam extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");  // 虽然对 get 没用,但写上没副作用,所以一律都写上
        String username = req.getParameter("username");
        // String.trim() 去除字符串两边的空格  "  hello  ".trim() -> "hello"   "      ".trim() -> ""
        if (username == null || username.trim().isEmpty()) {
            // 用户没有正确地填写需要的信息
            // 进行一定的错误处理
            // 比如:响应 400 或者 重定向其他页面
        }
        // 正常地使用 username 了
        // 读取到的 value 一定 String 类型,如果需要其他类型,比如 int,需要使用 Integer.parseInt(...) 转换
        // 但是转换的时候可能出异常 Integer.parseInt("hello") 就会异常

        // 如果还有其他的严格要求,需要代码自行处理
    }
}

五、GET方法和POST方法的区别

post请求和get请求都是HTTP的请求方式,本质上来说并无区别,底层实现都是基于TCP/IP协议。

1. 根本区别就是语义不同,GET代表的是获取的语义,POST代表的是提交的语义

2.GET请求放在查询字符串中,POST请求放在查询字符串或者请求体中(由form表单提交),GET方式需要使用Request.QueryString来取得变量的值,而POST方式通过Request.Form来获取变量的值。

由于各个浏览器的实现,URL的长度限制短于请求体的限制,URL一般会记录在日志中,请求体不会记录日志,POST会比GET稍微安全一点,要想实现真正的安全,使用HTTPS协议

3.为了提升访问速度,HTTP响应支持缓存,GET请求支持缓存,POST不支持缓存

4.GET产生一个TCP数据包;POST产生两个TCP数据包。对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。

图源icon-default.png?t=M4ADhttps://zhuanlan.zhihu.com/p/275695831 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

学习java的张三

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

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

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

打赏作者

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

抵扣说明:

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

余额充值