servletAPI超详__解老公不在家, 一个人偷看Servlet

目录

tomcat的定位

Servlet 详解

HttpServlet

核心方法

Get请求

 关于乱码的问题

HttpServletRequest

核心方法

代码案例1: 打印请求信息

 代码案例2: 获取GET请求中的参数

代码案例3:获取POST请求中的参数

 代码案例4:获取POST请求中的参数(2)

引入JSON库解析String字符串 

HttpServletResponse

核心方法

代码案例1: 设置状态码

代码案例2: 重定向


创作不易, 多多支持


tomcat的定位

        我们自己的实现是在Tomcat基础上, 运行的

servletAPI超详__解老公不在家, 一个人偷看Servlet_第1张图片

 当浏览器给服务器发送请求的时候, tomcat 作为 http 服务器, 就可以接受到这个请求

http协议作为一个应用层协议, 需要底层协议栈来支持工作:

servletAPI超详__解老公不在家, 一个人偷看Servlet_第2张图片

 servletAPI超详__解老公不在家, 一个人偷看Servlet_第3张图片

 1. 接收请求:
用户在浏览器输入一个 URL, 此时浏览器就会构造一个 HTTP 请求.
这个 HTTP 请求会经过网络协议栈逐层进行 封装 成二进制的 bit 流, 最终通过物理层的硬件设备转
换成光信号/电信号传输出去.
这些承载信息的光信号/电信号通过互联网上的一系列网络设备, 最终到达目标主机(这个过程也需要
网络层和数据链路层参与).
服务器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成
HTTP 请求. 并交给 Tomcat 进程进行处理(根据端口号确定进程)
Tomcat 通过 Socket 读取到这个请求(一个字符串), 并按照 HTTP 请求的格式来解析这个请求, 根据
请求中的 Context Path 确定一个 webapp, 再通过 Servlet Path 确定一个具体的 类. 再根据当前请
求的方法 (GET/POST/...), 决定调用这个类的 doGet 或者 doPost 等方法. 此时我们的代码中的
doGet / doPost 方法的第一个参数 HttpServletRequest 就包含了这个 HTTP 请求的详细信息.
2. 根据请求计算响应:
在我们的 doGet / doPost 方法中, 就执行到了我们自己的代码. 我们自己的代码会根据请求中的一
些信息, 来给 HttpServletResponse 对象设置一些属性. 例如状态码, header, body 等.
3. 返回响应:
我们的 doGet / doPost 执行完毕后, Tomcat 就会自动把 HttpServletResponse 这个我们刚设置好
的对象转换成一个符合 HTTP 协议的字符串, 通过 Socket 把这个响应发送出去.
此时响应数据在服务器的主机上通过网络协议栈层层 封装, 最终又得到一个二进制的 bit 流, 通过物
理层硬件设备转换成光信号/电信号传输出去.
这些承载信息的光信号/电信号通过互联网上的一系列网络设备, 最终到达浏览器所在的主机(这个过
程也需要网络层和数据链路层参与).
浏览器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成
HTTP 响应, 并交给浏览器处理.
浏览器也通过 Socket 读到这个响应(一个字符串), 按照 HTTP 响应的格式来解析这个响应. 并且把
body 中的数据按照一定的格式显示在浏览器的界面上.

Servlet 详解

HttpServlet

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

核心方法

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

我们实际开发的时候主要重写 doXXX 方法, 很少会重写 init / destory / service
这些方法的调用时机, 就称为 "Servlet 生命周期". (也就是描述了一个 Servlet 实例从生到死的过
程)
 

servletAPI超详__解老公不在家, 一个人偷看Servlet_第4张图片

 注意: HttpServlet 的实例只是在程序启动时创建一次. 而不是每次收到 HTTP 请求都重新创建实例

Get请求

创建MethodGet.java, 创建doGet方法:

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("/doGet")
public class MethodGet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("doget!!");
    }
}

 创建 testMethod.html, 放到 webapp 目录中, 形如

servletAPI超详__解老公不在家, 一个人偷看Servlet_第5张图片

 注意 他是和WEB-INF并列的html文件:





    
    
    test







然后重启tomcat

访问testMethod.html资源:

 

点击 发送get请求, 就可以在控制台上面看到响应内容

 servletAPI超详__解老公不在家, 一个人偷看Servlet_第6张图片

 分析:

  • 当浏览器中输入URL之后, 浏览器会给服务器发送了一个HTTP get请求, 来获取testMethod.html资源
  • 访文到testMethod.html资源之后, 然后点击get 发送请求的按钮, 然后浏览器又通过ajax 给服务器发送了一个http get请求
  • 此处应该注意这里的ajax里面的url中写的是 url: 'myGet', 这只是一个相对路径, 最终真是发送的请求URL路径为/myMaven/myGet

 关于乱码的问题

        如果我们在代码中写入中文, 例如:

resp.getWriter().write("GET 响应");

 在浏览器访问的时候, 会看到乱码的情况:

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

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

Post 请求

        创建一个类, 实现doPost方法, 然后在里面设置如下:

@WebServlet("/myPost")
public class MethodPost extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().write("Post 响应!!!");
    }
}

在testMethod.html中新增一个按钮, 和对应的点击事件处理函数

function sendPost() {
        ajax({
            method:'POST',
            url:'myPost',
            callback: function(body, status) {
                console.log(body);
            }
        });
    }

 点击post, 然后查看控制台:servletAPI超详__解老公不在家, 一个人偷看Servlet_第7张图片

 类似的方法还可以验证doPut, doDelete

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() 以字节为单位返回请求主体的长度,并提供输入流,或者如果长
度未知则返回 -1。
InputStream
getInputStream()
用于读取请求的 body 内容. 返回一个 InputStream 对象.

代码案例1: 打印请求信息

         在maven项目myMaven中创建ShowRequest类

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 {
        resp.setContentType("text/html;charset=utf8");
        StringBuilder respBody = new StringBuilder();
        respBody.append("请求协议的名称和版本:").append(req.getProtocol());
        respBody.append("
"); respBody.append("请求返回http方法和名称").append(req.getMethod()); respBody.append("
"); respBody.append("请求返回协议名知道http请求的第一行查询字符串中").append(req.getRequestURI()); respBody.append("
"); respBody.append("返回指示请求上下文的请求uri部分").append(req.getContextPath()); respBody.append("
"); respBody.append("返回URL中的查询字符串").append(req.getQueryString()); respBody.append("
"); respBody.append("

headers:

"); Enumeration headerNames = req.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); respBody.append(headerName + " "); respBody.append(req.getHeader(headerName)); respBody.append("
"); } resp.getWriter().write(respBody.toString()); } }

访问127.0.0.1:8080/myMaven/ShowRequest

servletAPI超详__解老公不在家, 一个人偷看Servlet_第8张图片

 代码案例2: 获取GET请求中的参数

GET请求中的参数一般是通过QueryString的方式传递给服务器的, 例如,

https://search.bilibili.com/all?keyword=%E4%BD%A0%E5%A5%BD&from_source=webtop_search&spm_id_from=333.1007&search_source=5

此时浏览器通过queryString 给服务器传递了好几个参数, 例如:

  • keyword =  %E4%BD%A0%E5%A5%BD
  • from_source = webtop_search
  • spm_id_from = 333.1007
  • search_source = 5

服务器就可以通过getParameter来获取到这些以&为分隔符的键值对

下面是代码案例:

在maven项目myMaven中创建GetParameter类:

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("/getParameter")
public class GetParameter extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf8");
        
        String parameter1Value = req.getParameter("parameter1");
        String parameter2Value = req.getParameter("parameter2");
        resp.getWriter().write("parameter1 :" + parameter1Value);
        resp.getWriter().write("parameter2 :" + parameter2Value);
    }
}

重新部署tomcat:

 访问127.0.0.1:8080/myMaven/getParameter?parameter1=123¶meter2=456

 

 此时服务器已收到了来自客户端的在queryString中传来的参数

代码案例3:获取POST请求中的参数

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

在maven项目myMaven里面创建PostParameter类:

@WebServlet("/postParameter")
public class PostParameter extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf8");
        String userId = req.getParameter("userId");
        String classId = req.getParameter("classId");
        resp.getWriter().write("userId :" + userId + " - - " + "classId :" + classId );

    }
}

在webapp中创建testPost.html文件:




    
    
    
    testPost


重新部署tomcat, 然后访问:

http://localhost:8080/myMaven/testPost.html

显示如下:

输入:

发送此请求, 通过form表单, 发送给 

postParameter, 然后调用相关的doPost方法, 显示:

通过fiddler抓包可以看到form表单的构造的body数据格式为:

POST http://127.0.0.1:8080/myMaven/postParameter HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Content-Length: 22
Cache-Control: max-age=0
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
Origin: http://127.0.0.1:8080
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/91.0.4472.114 Safari/537.36
Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,imag
e/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://127.0.0.1:8080/ServletHelloWorld/testPost.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

userId=123&classId=456

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

 代码案例4:获取POST请求中的参数(2)

        上面的代码案例3 是使用form表单的形式来传输数据给服务器的, 下面我们介绍使用json的格式来传递数据, 

        同样的我们在maven项目myMaven底下创建一个PostParameterJson类:

@WebServlet("/postParameterJson")
public class PostParameterJson extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("application/json;charset=utf8");
        String body = readBody(req);
        resp.getWriter().write(body);
    }

    private String readBody(HttpServletRequest req) throws IOException {
        int contentLength = req.getContentLength();
        byte[] buffer  = new byte[contentLength];
        InputStream inputStream = req.getInputStream();
        inputStream.read(buffer);
        return new String(buffer, StandardCharsets.UTF_8);
    }
}

创建testPsotJson.html:




    
    
    
    testPostJson


    
    
    

重新部署tomcat然后访问: http://localhost:8080/myMaven/testPostJson.html

点击发送, 然后查看后台如下:

抓包可以看到留恋其给服务器发送的一个Post请求:

POST http://127.0.0.1:8080/ServletHelloWorld/postParameterJson HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Content-Length: 28
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/91.0.4472.114 Safari/537.36
Content-Type: application/json; charset=utf-8
Accept: */*
Origin: http://127.0.0.1:8080
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:8080/ServletHelloWorld/testPostJson.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

{"userId":123,"classId":456}

 其中最后面一行就是Json格式的数据

但是需要注意的是, 服务器拿到的这个json数据 依然是一个整体的String 类型, 如果你想哟啊获取到具体的值(123, 456). 还需要搭配JSON库进一步分析

引入JSON库解析String字符串 

引入Jackson这个库, 对JSON进行解析:

首先, 访问中央仓库:

中央仓库https://mvnrepository.com/servletAPI超详__解老公不在家, 一个人偷看Servlet_第9张图片

 选择版本:

servletAPI超详__解老公不在家, 一个人偷看Servlet_第10张图片

 复制maven地址:

servletAPI超详__解老公不在家, 一个人偷看Servlet_第11张图片

 粘贴到pom.xml中的dependencies标签中去:

servletAPI超详__解老公不在家, 一个人偷看Servlet_第12张图片

 然后刷新maven项目, 在idea右侧边栏中

servletAPI超详__解老公不在家, 一个人偷看Servlet_第13张图片

 如果下载速度过于慢, 我们可以在settings.xml中配置国内源

修改PostParameterJson中的代码, 修改好的如下:

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.io.InputStream;
import java.nio.charset.StandardCharsets;


@WebServlet("/postParameterJson")
public class PostParameterJson extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        resp.setContentType("application/json;charset=utf8");
//        String body = readBody(req);
//        resp.getWriter().write(body);

        resp.setContentType("application/json;charset=utf8");
        String body = readBody(req);

        // 创建ObjectMapper对象, 这是jackson中的核心类
        ObjectMapper objectMapper = new ObjectMapper();

        // 通过readvalue 方吧body , 这个字符串转换成 JsonData 对象
        JsonData jsonData = objectMapper.readValue(body, JsonData.class);

        resp.getWriter().write("userId: " + jsonData.userId + ',' + "calssId:" + jsonData.classId);
    }

    private String readBody(HttpServletRequest req) throws IOException {
        int contentLength = req.getContentLength();
        byte[] buffer  = new byte[contentLength];
        InputStream inputStream = req.getInputStream();
        inputStream.read(buffer);
        return new String(buffer, StandardCharsets.UTF_8);
    }
}

// 创建一个新的类表示JSON数据, 属性的名字需要和json字符串中的key一样
class JsonData {
    public String userId;
    public String classId;
}

HttpServletResponse

        HttpServlet中的doXXX方法目的就是根据请求计算得到响应,然后把相应的数据设置到HttpServletResponse对象中

        然后tomcat 就会把这个HttpServletResponse对象按照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 中写入二进制格式数据.

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

注意: 对于状态码 / 响应的设置要放到 getWriter / getOutputStream 之前, 否则设置可能失效

代码案例1: 设置状态码

        实现一个程序, 用户在浏览器通过参数指定要返回响应的状态码

首先在maven项目(myMaven) 中创建一个 StatusServlet类 :

@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);
    }
}

通过tomcat部署程序, 然后访问 http://localhost:8080/myMaven/statusServlet?status=200:

 然后通过fiddler抓包可以看到

HTTP/1.1 200
Content-Length: 11
Date: Mon, 21 Jun 2021 08:05:37 GMT
Keep-Alive: timeout=20
Connection: keep-alive
status: 200

变换不同的 status 的值, 就可以看到不同的响应结果
 

代码案例2: 自动刷新

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

        创建一个AutoRefreshServlet类

@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("timstamp: " + timeStamp);
    }
}
  • 通过HTTP响应报头中的Refresh字段, 可以控制浏览器 自动刷新的时机
  • 通过Date 类的getTime方法, 可以获取到当前时刻的毫秒级时间戳

部署程序:

访问 :http://localhost:8080/myMaven/autoRefreshServlet

提升训练, 如何把他改成具体的日期时间,. 而不是时间戳

代码案例3 重定向

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

        创建RedirectServlet类

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

部署然后 直接访问:http://127.0.0.1:8080/myMaven/redirectServlet

抓包结果:

HTTP/1.1 302
Location: http://www.baidu.com
Content-Length: 0
Date: Mon, 21 Jun 2021 08:17:26 GMT
Keep-Alive: timeout=20
Connection: keep-alive


servletAPI超详__解老公不在家, 一个人偷看Servlet_第14张图片

你可能感兴趣的:(服务端,JavaEE初阶,servlet)