目录
一.Servlet运行原理
在Servlet的代码中我们并没有写main方法,那么对应的do Get代码是如何被调用的呢?响应又是如何返回给浏览器的?
这个代码是基于在Tomcat的基础上运行的
上面的流程图,表示的是Web Browser是客户端,通过HTTP协议发给HTTP服务器,即Tomcat.Tomcat拿到了HTTP请求后,就会对请求的进行解析成一个HTTPServletRequest对象,我们调用Servlet类,来执行程序员写好的逻辑(Servlet Program).
1.接收请求:
- 用户在浏览器输入一个URL,此时浏览器就会构造一个HTTP请求.
- 这个HTTP请求会经过网络协议栈逐层进行封装成二进制的bit流,最终通过物理层的硬件设备转换成光信号/电信号传输出去.
- 这些继承信息的光信号/电信号通过互联网上的一系列网络设备,最终到达目标主机(这个过程也需要通过网络层和数据链路层参与).
- 服务器主机收到这些光信号/电信号,又会通过网络协议栈逐层进行分用,层层解析,最终还原成HTTP请求,并交给Tomcat进程进行处理(根端口号确定进程)
- Tomcat通过Socket读取到这个请求(一个字符串),并按照HTTP请求的格式来解析这个请求.根据请求中的Context Path确定一个webapp,再通过Servlet Path确定一个具体的类.再根据当前请求的方法(GET/POST/...),决定调用这个类的doGET/doPOST方法的第一个参数HttpServletRequest就包含了这个HTTP请求的详细信息.
2.根据请求计算响应
在我们的doGet/doPost方法中, 就执行到了我们自己的代码,我们自己的代码会根据请求中的一些信息,来给HttpServletRespons对象设置一些属性,例如状态码.header,body等
二.ServletAPI
1.HttpServlet
我们写的Servlet代码的时候,首先第一步就是先创建类,继承HttpServlet,并重写其中的某些方法
常见面试题
说一下Servletd的生命周期
- Servlet在实例化之后调用一次init
- Servlet 每次收到请求,调用一次service
- Servlet 在销毁之前,调用一次 destroy
乱码问题:
当我们在body中写入中文后
在浏览器的控制台中查看body发现出现乱码了
原因是:IDEA中与浏览器的编码方式是不一样的,在IDEA中的编码方式UTF-8,而在浏览器中的编码方式是gbk
解决方案:
让浏览器按照UTF-8进行解析,则只要在响应中的header里面加上Conten-Type,在Context-Type里面注明响应的响应是UTF-8.
在webapp中创建一个html文件,在html中设置Get请求的按钮和POST请求的按钮,引入JQuery的第三方库来创建ajax表单,并且设置表单中的数据,发送的路径,打印日志的方法
<body>
<button onclick="sendGet()">发送 Get 请求</button>
<button onclick="sendPost()">发送 POST 请求</button>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
function sendGet() {
$.ajax({
type:"get",
url:"method",
sucess:function(data,status) {
console.log(data);//打印的是body
}
})
}
function sendPost() {
$.ajax({
type:"post",
url:"method",
data:"request body",//post方法中有正文
sucess:function(data,status) {
console.log(data);
}
})
}
</script>
</body>
在doGet/doPost方法中设置Type
单单设置还不够,还要设置该项目的编码方式.
2.HttpServletRequest
这个类就表示一个http请求,理解这个类的前提就是理解http协议的格式.
http的请求的报文格式:
1.首行:方法类型,URL,版本号.URL中进一步分出来path,query string
2.header,一堆键值对,键值对的类型也很多
3.空行
4.body
在HttpServletRequst类中,有很多方法,能够将HTTP报文格式的内容分类出来,由Tomcat把字符串结构的请求解析成一个结构化的数据,即从字符串到结构化的数据,这样的过程称为反序列化.
通过这些方法可以,获取到一个请求中的各个方法的信息,请求对象是服务器收到的内容,不应该修改,因此上面的方法都只是读方法,而不是写方法.
注意:
1.URL和URI的含义是类似的,都是网络上的一个资源,L指的是location(资源的位置),I是identity(资源的标识符)
2.String getQuerString()方法返回的是完整的query strung :如query string:a=10&b=20,返回的就是整个.而下面的三个方法,都是对应query string来操作的,第一个是返回的所有query string所有的key.以枚举返回.第二个是返回key对应的value.第三个是返回某个key的所有value值,返回的字符串数组类型,大多数用的参数是重复时候(可能参数为a=10&a=20)
3.下面这两个方法都是对header进行操作的
假设报头为:
那么第一个方法返回的是header头中的key.第二个方法返回的header头中对于的key的value值
2.1 获取GET请求中的参数
GET请求的参数一般都是通过query string传递给服务器/
https://v.bitedu.vip/personInf/student?userId=1111&classId=100
此时浏览器通过query string给服务器传递了两个参数,userId和classId值分别是1111和100,在服务器端可以通过getParamenter来获取参数的值
因此我们可以约定,在客户端中的quer string中假设只有两个参数,userId和classId,当我们从客户端访问服务器的时候,让服务器能够返回客户端中输入的userId和classId中的参数.
创建GetParameterServlet类
因为约定的参数只有userId和classId,因此调用getParameter的时候去查找名字相同的即可.
假设在客户端访问服务器的时候,没有带上参数(即127.0.0.1:8080/20220415/getParameter),那么页面显示为:
因此,如果当前的query string中key不存在,那么得到的value就是null.
2.2获取POST请求中的参数
POST请求的参数一般通过body传递给服务器.body中的数据格式有很多种.如果采用form表单的形式,仍然可以通过getParameter获取参数的值.
我们知道Post请求的body中的格式有三种:
1.application/x-www-form-urlencoded.如:a=10&b=20,根query string类似
2.mutipart/from-data主要用来传输文件的生成一个分隔符.
3.application/json
使用第一种方式,getParameter方法来获取也是可以的
我们需要写一个html页面来验证服务器的程序,此时使用form表单的形式即可.
此时在页面输入22:点击提交后
可以看到一个POST请求
2.3获取POST请求中的参数(json)
我们按照JSON的格式传递,那么获取的参数也发生调整.需要解析成json格式的body,即获取到userId和classId里面具体的值,但是json的格式解析起来是比较复杂的,因为json里面可以再嵌套json.我们可以可以使用第三方库--Jackson Databind,这个库也适用于Spring全家桶.
注意:使用jackson解析成json的时候,需要先明确,要把这个字符串转成什么样的对象.
3.HttpServletResonse
Servlet中的do...方法的目的就是根据请求计算得到响应,然后把响应的数据设置到HttpServletResponse对象中.然后Tomcat就会把这个HttpServletResponse对象按照HTTP协议的格式转成字符串,并通过Socket写回给浏览器.
注意:响应对象是服务器返回给浏览器的内容,这里的重要的信息都是程序员设置的,因此上面的方法都是写方法.注意:对于状态码/响应头的设置要放到getWriter/getOutputStream之前,否则可能失效
3.1设置状态码
3.2自动刷新
header中有一个属性--Refresh它的值就是隔多长时间刷新,单位是s
抓刷新后的包可以看到:
3.3 重定向
重定向就是"呼叫转移",状态码是302,可以设置location字段重定向
此时我们客户端访问服务器,可以看到页面跳转到搜狗地址上了.
4.Cookie和Session
4.1 什么是Cookie
因为HTTP是无状态的协议,无法根据之前的状态进行本次的请求处理,为了保留无状态协议这个特征,于是引入了Cookie信息来控制客户端的状态.Cookie会根据从服务器发送的响应报文内一个叫Set-Cookie的首部字段信息,通知客户端保存Cookie.当下次再给服务器发送请求的时候,客户端自动在请求报文中加入Cookie值后发送出去.
服务器端发现客户端发来的Cookie后,会检查是那一个客户端发送的连接请求,对比服务器上的记录,最后得到之前的状态信息
此时服务器这边就需要记录Cookie信息,这个就是Session机制所做的工作
4.2 什么是Session
Session译为会话,Session是存储在服务端的,当客户端第一次访问服务器端的时候,服务端会把客户端的信息存储在服务器上,当客户端再次访问的时候,就可以通过Session中查找该客户端的状态就可以了,存储的方式类似于哈希表.
4.3 Cookie和Session的区别
- Cookie是保存在客户端,Session是保存在服务端
- Cookie不安全
- 单个Cookie保存的数据不超过4k
- Cookie和Session经常会在一起配合使用,但是不是必须配合
4.4 相关方法
HttpServletRequest类中的相关方法
方法 | 描述 |
---|---|
HttpSession getSession() | 在服务器中获取会话. 参数如果为 true, 则当不存在会话时新建会话; 参数如果为 false, 则当不存在会话时返回 null |
Cookie[] getCookies() | 返回一个数组, 包含客户端发送该请求的所有的 Cookie 对象. 会自动把Cookie 中的格式解析成键值对. |
HttpServletResponse类相关方法
方法 | 描述 |
---|---|
void addCookie(Cookie cookie) | 把指定的 cookie 添加到响应中. |
HttpSession类中的相关方法
方法 | 描述 |
---|---|
Object getAttribute(String name) | 该方法返回在该 session 会话中具有指定名称的对象,如果没有指定名称的对象,则返回 nu |
void setAttribute(String name, Object value) | 该方法使用指定的名称绑定一个对象到该 session 会话 |
boolean isNew() | 判定当前是否是新创建出的会话 |
Cookie类中的相关方法
方法 | 描述 |
---|---|
String getName() | 该方法返回 cookie 的名称。名称在创建后不能改变。(这个值是 SetCooke 字段设置给浏览器的) |
String getValue() | 该方法获取与 cookie 关联的值 |
void setValue(String newValue) | 该方法设置与 cookie 关联的值。 |
注意事项
- 通过HttpServletRequest.getCookie()获取到请求中的一系列Cookie键值对,需要带有参数
- 当为true的时候,如果session不存在,就会创建,如果存在,就获取session然后返回,当为false的时候,如果session不存在就会返回null,如果存在,就获取session然后返回
- HTTP的cookie字段中存储的实际上是多组键值对,每个键值对在Servlet中都对应了一个Cookie对象,Cookie里主要有key和value两个属性.