额外知识点
Web核心
-
Web
- 全球广域网,也成为万维网(www),可通过浏览器访问的网站
-
JavaWeb
- 使用Java技术来解决相关Web互联网领域的技术栈
-
JavaWeb技术栈
-
B/S架构:Browser/Server,即浏览器/服务器 架构模式
- 特点:客户端只需要浏览器,应用程序的逻辑和数据都存储在服务器端。浏览器只需要请求服务器来获取Web资源(服务器接收到请求后会把Web资源发送给浏览器)
- 优点: 易于维护升级(即服务器端升级后,客户端无需任何部署即可使用到最新版本)
- 放在服务器端的资源
- 静态资源:HTML、CSS、JavaScript、图片等负责页面展现
- 动态资源:Servlet、JSP等负责逻辑处理
- 数据库:负责存储数据
- HTTP协议:定义通信规则
- Web服务器:负责解析HTTP协议,解析请求数据并发送响应数据
- 设计的技术
- HTTP、Tomcat、Servlet
- Requset(请求)、Response(响应)
- JSP、会话技术(Cookie、Session)
- Filter(过滤器)、Listener(监听器)
- Ajax、Vue、ElementUI
-
HTTP协议
-
定义
-
超文本传输协议(Hyper Text Transfer Protocol),规定了浏览器和服务器之间数据传输的规则(即浏览器的请求数据和服务器的响应数据需要按照一定的格式规则来写)
-
浏览器F12打开开发者模式,NetWork下为浏览器请求的所有数据包
-
现在随便打开一个请求数据包以www.baidu.com这个数据包为例,详见图示说明
-
-
-
特点
- 基于TCP协议:面向连接,安全
- 基于请求-响应模型的:一次请求对应一次响应
- 是无状态的协议:对于事务处理没有记忆能力。每次请求-响应都是独立的
- 优点:速度快
- 缺点:多次请求不能共享数据(注意:Java中会使用会话技术来解决该问题)
HTTP请求数据格式
-
请求数据分为三部分
- 请求行:请求数据的第一行
- GET标识请求方式
- 请求方式有GET、POST等
- /表示请求资源路径
- HTTP/1.1表示协议版本
- GET标识请求方式
- 请求头:从请求数据的第二行开始,格式为
key:value
键值对形式 - 请求体(只有post方式才有请求体):POST请求的最后一部分,存放请求参数
- 请求行:请求数据的第一行
-
常见的HTTP请求头
请求头 解释 Host 表示请求的主机名 User-Agent 浏览器版本,例如:Chrome浏览器的标识类似Mozilla/5.0 …Chrome/79,IE浏览器的标识类似Mozilla/5.0 (Windows NT …)like Gecko; Accept 表示浏览器能接收的资源类型,如text/*,image/或者/*表示所有; Accept-Language 表示浏览器偏好的语言,服务器可以据此返回不同语言的网页; Accept-Encoding 表示浏览器可以支持的压缩类型,例如gzip, deflate等。 -
GET请求和POST请求的区别
- GET请求的请求参数在请求行中,没有请求体;而POST请求的请求参数在请求体中
- POST请求请求参数在请求体中
HTTP响应数据格式
-
响应数据分为三部分
- 响应行:响应数据的第一行。
- HTTP/1.1标识协议版本
- 200标识响应状态码
- OK标识状态码的描述
- 响应头:从响应数据的第二行开始,格式为
key:value
键值对形式 - 响应体:最后一部分存放响应数据。上图中
<html>...</html>
这部分内容就是响应体
- 响应行:响应数据的第一行。
-
常见的HTTP响应头
请求头 解释 Content-Type 表示该响应内容的类型,例如text/html,image/jpeg; Content-Length 表示该响应内容的长度(字节数); Content-Encoding 表示该响应压缩算法,例如gzip; Cache-Control 指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒
响应状态码大类
查看所有状态码详见网址:https://cloud.tencent.com/developer/chapter/13553
-
常见的响应状态码
状态码 英文 解释 200 OK
客户端请求成功。即处理成功,这是最想看到的状态码 302 Found
提示所请求的资源已移动到由Location响应头给定的URL,浏览器会自动重新访问到这个页面 304 Not Modified
告诉客户端,你请求的资源至上次取得后,服务器端并未更改,你直接使用本地缓存即可。此为隐式重定向 400 Bad Request
客户端请求由语法错误,不能被服务器所理解 403 Forbidden
服务器端收到请求但是拒绝提供服务。比如没有权限访问相关资源 404 Not Found
请求资源不存在,一般是URL输入错误或网站资源被删除 405 Method Not Allowed
请求方式有误,比如应该用GET请求方式的资源使用了POST 428 Precondition Required
服务器要求有条件的请求,告诉客户端要想访问资源就必须携带特定的请求头 429 To Many Requests
太多请求,可以限制客户端请求某个资源的数量,配合Retry-After(多长时间后可以请求)响应体一起使用 431 Request Header Fields Too Large
请求头太大,服务器不愿意处理请求,因为它的头部字段太大,请求可以在减少请求头域的大小后重新提交 500 Internal Server Error
服务器发生不可预期的错误。服务器出异常了,赶紧看日志 503 Service Unavailable
服务器尚未准备好处理请求,服务器刚刚启动,还未初始化好 511 Network Authentication Required
客户端需要进行身份验证才能获取网络访问权限
Servlet
- 定义
- 它是Java提供的一门动态Web资源开发的技术
- 特点
- 它是JavaEE规范之一,其实就是一个接口,我们需要定义Servlet类来实现Servlet接口,并由服务器运行Servlet
Servlet快速入门
-
步骤
-
Step1:创建Web项目,导入Servlet依赖坐标以及Tomcat插件,详见图一及pom.xml文件代码
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>WebDemo</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>WebDemo Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- Servlet依赖--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> <!-- 依赖范围关键字provided:在编译环境和测试环境有效,但在真正运行时就不会在使用该jar包--> </dependency> </dependencies> <build> <finalName>WebDemo</finalName> <plugins> <!-- Tomcat插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> </plugin> </plugins> </build> </project>
-
Step2:创建
at.guigu.web
包并在该web
包下定义一个类(此处以ServletDemo1类为例)实现Servlet
接口,如图一,并重写接口中所有的方法,并在service方法中随便输入一句话,若将来service方法执行成功则说明Servlet接口成功被访问,详见ServletDemo1类的代码package at.guigu.web; import javax.servlet.*; import java.io.IOException; public class ServletDemo1 implements Servlet{ //将来Servlet接口被访问时service方法会被自动执行 //若service执行成功就说明Servlet接口访问成功 @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("SERVLET Hello World"); } @Override public String getServletInfo() { return ""; } @Override public void destroy() { } @Override public void init(ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig() { return null; } }
-
Step3:若想要
Servlet
接口被访问,就必须在刚刚的ServletDemo1
类上使用@WebServlet("路径")
注解来配置该Servlet访问路径,如下所示package at.guigu.web; import javax.servlet.*; import javax.servlet.annotation.WebServlet; import java.io.IOException; @WebServlet("/demo1") public class ServletDemo1 implements Servlet{ //将来Servlet接口被访问时service方法会被自动执行 //若service执行成功就说明Servlet接口访问成功 @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("SERVLET Hello World"); } @Override public String getServletInfo() { return ""; } @Override public void destroy() { } @Override public void init(ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig() { return null; } }
-
Step4:启动Tomcat,浏览器输入URL
-
注意:
-
若用Tomcat插件启动Web项目时URL为
http://localhost:8080/WebDemo/demo1
访问该Servlet- WebDemo为该Web项目名称
- demo1为使用
@WebServlet
注解配置的Servlet访问路径
-
若用配置的本地插件启动Web项目时URL为默认URL加上
/demo1
来访问该Servlet,如图,此时URL即为http://localhost:8080/WebDemo_war/demo1
-
浏览器打开的URL为
http://localhost:8080/WebDemo
或http://localhost:8080/WebDemo_war
,都不会带/demo1
,所以需要手动将其加上,之后刷新浏览器,此时idea服务器端才会运行service
方法,此时才说明Servlet接口被成功访问,如图所示
-
-
Servlet执行流程
-
问题
- 在快速入门中,我们创建了一个实现
Servlet
接口的类ServeltDemo1
,在我们并没有创建该类的对象以及调用该类中service
方法的情况下,为什么能够运行成功?那么是谁创建了该类的对象并调用了service
方法,请详见执行流程
- 在快速入门中,我们创建了一个实现
-
URL各部分解析,以
http://localhost:8080/WebDemo/demo1
为例http://localhost:8080
:可访问到Tomcat服务器/WebDemo
:可访问到Web项目/demo1
:可访问到对应的Servlet
所以若想要使Servlet接口被访问到则需要将url后补充上
/demo1
,即注解@WebServlet(urlPatterns = "/demo2")
中urlPatterns
的属性值 -
注意
-
实现
Servlet
接口的类ServeltDemo1
是由Tomcat Web
服务器创建的,且该类中的service
方法也是由Tomcat Web
服务器调用的,当该方法被调用时服务端就会向浏览器发送响应数据,由于我们在快速入门中并未向该方法中写入什么代码,所以只能看到一个空白页面,如图所示,当我们为该方法public void service(ServletRequest servletRequest, ServletResponse servletResponse)
传入ServletRequest
对象和ServletResponse
对象两个参数之后就会给客户端浏览器返回对应的信息了 -
注解
@WebServlet(urlPatterns = "/demo2")
有时会写为@WebServlet(value = "/demo2")
,这两个属性表示的含义是一样的-
urlPatterns:可映射多个URL模式,示例如下
@WebServlet(urlPatterns = {"/example", "/example2"})
-
value:只能映射单个URL模式,示例如下
@WebServlet("/example")
-
它俩不能同时使用
-
-
-
问题:服务器怎么知道
Serlet
中一定有servlet
方法?- 因为我们自定义的
Serlet
必须实现Servlet
接口并重写该接口中的方法,而Servlet接口中有service
方法
- 因为我们自定义的
Servlet生命周期
-
生命周期定义
- 对象的生命周期是指一个对象从被创建到销毁的整个过程
-
Servlet运行在Servlet容器(即Web服务器)中,其生命周期由容器来管理,分为四个阶段:
- 阶段一加载和实例化: 默认情况下,当
Servlet
第一次被访问时,由容器创建对象- 我们可以利用
@WebServlet
注解的属性loadOnStartup
来更改创建Servlet对象的时机@WebServlet(urlPatterns="/demo1", loadOnStartup=1)
:当loadOnStartup
属性值为负整数 时,代表默认情况即Servlet
第一次被访问时,由容器创建对象;当loadOnStartup
属性值为非负整数 时,代表服务器启动时就立即由容器创建对象,此时数字越小优先级越高。
- 我们可以利用
- 阶段二初始化: 在
Servlet
实例化之后,容器将调用Servlet
接口的init()
方法初始化这个对象,并完成一些如加载配置文件、创建连接等初始化的工作。注意:init()
方法只会调用一次 - 阶段三请求处理: 每次请求Servlet时,
Servlet
容器都会调用Servlet
的service()
方法对请求进行处理 - 阶段四服务终止: 当需要释放内存或容器关闭时,容器就会调用Servlet实例的
destroy()
方法来完成资源的释放。在destroy()
方法调用之后,容器就会释放这个Servlet
实例(说白了就是释放init()
方法所生成的资源),该实例随后会被Java的垃圾收集器所回收
- 阶段一加载和实例化: 默认情况下,当
-
示例
package at.guigu.web; import javax.servlet.*; import javax.servlet.annotation.WebServlet; import java.io.IOException; //@WebServlet(urlPatterns = "/demo2", loadOnStartup = 1) @WebServlet("/demo2") public class ServletDemo2 implements Servlet { /** * 初始化方法 * 1. 调用时机:默认情况下,Servlet被第一次访问时,容器将调用该方法 * 2. 调用次数:只会被调用一次 * @param servletConfig * @throws ServletException */ @Override public void init(ServletConfig servletConfig) throws ServletException { System.out.println("Init()方法被调用"); } /** * 提供服务的方法 * 1. 调用时机及调用次数:每次Servlet被访问时都会调用该方法 * @param servletRequest * @param servletResponse * @throws ServletException * @throws IOException */ @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("service()方法被调用"); } /** * 销毁方法 * 1. 调用时机:内存释放或服务器关闭时,容器会调用该方法来进行Servlet对象的被销毁 * 2. 调用次数:只会被调用1次 */ @Override public void destroy() { System.out.println("destroy()方法"); } @Override public ServletConfig getServletConfig() { return null; } @Override public String getServletInfo() { return ""; } }
-
init()
及service()
方法演示-
@WebServlet
注解的属性loadOnStartup
的属性值默认为负整数时(即@WebServlet("/demo2")
,此时loadOnStartup
属性省略),则会在Servlet接口被第一次访问时 调用init()
和service()
方法且init()
方法只会被调用一次,而service()方法会被调用多次(Servlet接口被访问一次则会调用一次)。如下所示 -
@WebServlet
注解的属性loadOnStartup
的属性值默认为非负整数(即@WebServlet(urlPatterns = "/demo2", loadOnStartup = 1)
)时,则会在服务器启动时 调用init()
和service()
方法init()
方法只会被调用一次,而service()方法会被调用多次(Servlet接口被访问一次则会调用一次)。如下所示
-
-
destroy()
方法演示-
该方法在关闭服务器时会被调用,但是要注意,不能使用IDEA中的强制关闭按钮(它就相当于直接拔掉了电源),若使用则不会调用该方法,强制关闭按钮如图所示
-
若想要正常关闭则需要在Terminal控制台来启动该Web项目然后利用Ctrl+C来进行关闭,如下所示
-
Step1:打开Terminal控制台,输入
mvn tomcat7:run
命令运行当前Web项目 -
Step2:按住Ctrl+C关闭服务器
-
-
Servlet接口方法介绍
方法 | 解释 |
---|---|
void init(ServletConfig config) | 初始化方法,在Servlet对象被创建时执行且只执行一次 |
void service(ServletRequest req, ServletResponse res) | 提供服务方法,每次Servlet 接口被访问都会调用该方法 |
void destroy() | 销毁方法,当Servlet实例被销毁时调用该方法。在内存释放或服务器关闭时销毁Servlet 实例 |
ServletConfig getServletConfig() | 获取ServletConfig 对象 |
String getServletInfo() | 获取Servlet信息,比如:作者、版本、版权等,一般情况下返回空字符串或null |
前三个已在Servlet生命周期中讲解,后两个方法了解即可
Servlet体系结构(重点说明HttpServlet
实现类)
-
Servlet体系中提供了一个Servlet接口以及它的两个实现类
Servlet
接口:Servlet体系的根接口GenericServlet
类:Servlet接口的抽象实现类HttpServlet
类:是对HTTP协议封装的Servlet接口的实现类,该类继承自GenericServlet
类
由于我i们开发的B/S架构的Web项目都是针对HTTP协议的,所以我们自定义实现Servlet接口的类就可以都继承自
HttpServlet
类,并在重写其doGet()
和doPost()
方法后就完成了对Servlet的编写好处:不用像之前一样直接实现Servlet接口然后重写五个方法,因为那五个方法中其中有两个是不常用的,所以写了就会很麻烦,没有必要;使用继承自
HttpServlet
的类则会大大减少不必要的代码冗余 -
HttpServlet
实现类使用步骤- Step1:继承HttpServlet
- Step2:重写doGet和doPost方法
-
HttpServlet
实现类原理- 获取请求方式,并根据请求方式的不同来调用不同的doXxx方法
- 注意:目前只学习了get和post请求方式,其它请求方式后续会学习
-
代码示例,供测试
doGet()
和doPost()
方法使用package at.guigu.web; 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("/demo3") public class ServletDemo3 extends HttpServlet { //若通过get方式访问Servlet接口时则会调用doGet方法 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("get..."); } //若通过post方式访问Servlet接口时则会调用doPost方法 @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("post..."); } }
-
若通过get方式访问Servlet接口时则会调用
doGet()
方法 -
若通过post方式访问Servlet接口时则会调用
doPost()
方法-
测试
doPost()
方法时我们需要先创建一个html文件并写入表单,设置表单的提交方式为post
,并将action
的属性值设置为服务器url,如图所示 -
Tomcat运行该Web项目,补全url并回车,如图一所示;填写页面的表单并提交,如图二所示,此时由于客户端是通过
post
方式向服务端发送数据,所以服务端调用doPost()
方法,如图三所示
-
-
问题:HttpServlet中为什么要根据请求方式不同调用不同的方法,并且它们是如何被调用的?
-
若我们不使用HttpServlet这个实现类,自己写实现Servlet接口的实现类(假如类为ServletDemo4)的话,就需要重写
service()
方法,在该方法中我们首先要获取请求方式,然后根据请求方式的不同(get还是post)来进行不同的处理,service()
方法的简略代码如下:@Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { //由于请求方式需要在Http的协议下才能获得,所以需要先将ServletRequest对象强转为HttpServletRequest对象 HttpServletRequest request = (HttpServletRequest) req; //获取请求方式 String method = request.getMethod(); //判断请求方式,根据请求方式的不同进行分别处理 if (method.equals("GET")) { //get方式的处理逻辑 } else if (method.equals("POST")) { //post方式的处理逻辑 } }
- 那么问题就来了,如果我们都用自己写的实现类,那么每写一个实现类就要重写一下该代码,这样就很麻烦,所以官方就直接将其封装成一个实现类(即
HttpServlet
实现类)供用户使用,这样用户可以通过继承HttpServlet
实现类(相当于间接继承了Servlet接口)来使用Servlet接口中的方法
- 那么问题就来了,如果我们都用自己写的实现类,那么每写一个实现类就要重写一下该代码,这样就很麻烦,所以官方就直接将其封装成一个实现类(即
-
Servlet的urlPattern配置
-
Servlet要想被访问就必须配置其访问路径(即urlPattern)
-
一个Servlet可配置多个urlPattern,此时
url+任意一个urlPattern
就可成功访问Servlet
@WebServelt(urlPatterns = {"/demo5", "/demo6"})
-
代码示例(仅以get方式为例,post方式省略)
package at.guigu.web; 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(urlPatterns = {"/demo5", "/demo6"}) public class ServletDemo5 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("get..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("post..."); } }
-
-
urlPattern配置规则
-
精确匹配: 精确匹配特定的路径
-
我们在该小节之前的Servlet知识点中使用的都是精确匹配
-
比如
@WebServlet(urlPatterns="/路径1/路径2")
,那么此时在浏览器中url就需要加上/路径1/路径2
之后才能成功访问Servlet接口 -
注意:精确匹配的路径可以自己随便写,不需要是真是存在的路径
-
示例如下
package at.guigu.web; 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(urlPatterns = "/qwe/nihao") public class ServletDemo6 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("get..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("post..."); } }
-
-
目录匹配(路径前缀匹配): 匹配指定路径 下的所有请求,路径以
/*
结尾。-
比如
@WebServlet(urlPatterns="/路径1/*")
,此时我们就可匹配指定路径下的所有请求,也就是说/路径1/aaa
、/路径1/bbb
都可以,*
可以被我们替换成任意的路径,路径均可以是不存在的路径;而路径1是我们已经确定下来的具体的路径(它也可以是不存在的路径),所以就不能更改路径1,只能更改*
-
示例如下
package at.guigu.web; 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(urlPatterns = "/qwe/*") public class ServletDemo7 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("get..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("post..."); } }
-
注意:
@WebServlet(urlPatterns = "/*")
也属于目录匹配(即路径前缀匹配),但它比较特殊,会在四种配置规则学完之后讲解
-
-
扩展名匹配: 匹配特定扩展名的请求
-
比如
@WebServlet(urlPatterns="*.do")
,此时*.do
会匹配所有以.do
结尾的请求,如/foo.do
、/bar.do
等等。 -
注意:扩展名匹配不能以斜杠
/
开头 -
示例
package at.guigu.web; 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(urlPatterns = "*.do") public class ServletDemo8 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("get..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("post..."); } }
-
-
任意匹配:
@WebServlet(urlPatterns = "/")
: 匹配未被其它三种配置规则匹配的请求,该方式会覆盖掉Tomcat中DefaultServlet,此时就无法访问静态资源(比如html页面)了-
示例
package at.guigu.web; 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(urlPatterns = "/") public class ServletDemo9 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("get..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("post..."); } }
此时Tomcat运行该Web项目,并将url改为http://localhost:8080/WebDemo/a.html,我们发现并不会出现对应的html页面,如下图所示
-
-
-
目录匹配中的特例
@WebServlet(urlPatterns = "/*")
: 它属于目录匹配(即路径前缀匹配)中的特例,它会覆盖扩展名匹配和任意匹配,匹配除未被精确匹配和一般目录匹配匹配的请求;而且该方式也会覆盖掉Tomcat中DefaultServlet
,此时就无法访问静态资源(比如html页面)了(这个不在进行示例,可自行按照任意匹配中的示例自己进行举例)-
示例1:Tomcat运行该Web项目,解释详见图示
-
-
注意
- 当配置路径既满足精确匹配又满足目录匹配,则精确匹配优先级更高
- 优先级:精确路径>目录路径>
/*
>扩展名路径>/
- 一般情况下,我们不会使用任意匹配和目录匹配中的特例,尽量使用其它几种配置规则
XML配置Servlet
-
Servlet从3.0版本之后才开始支持使用注解配置,3.0之前只支持XML配置文件的配置方式
-
配置步骤
-
编写Servlet实现类(利用XML配置Servlet时,不需要用注解)
package at.guigu.web; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class ServletXmlDemo extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("xml get..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("xml post..."); } }
-
在web.xml中配置该Servlet
- Step1:配置实现该Servlet接口的实现类的全类名
- Step2:配置该Servlet的路径映射(即访问路径)
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <!--Servlet 全类名--> <servlet> <!--为该Servlet起个名:自己随便起名--> <servlet-name>demo</servlet-name> <!--写入该实现类的全类名--> <servlet-class>at.guigu.web.ServletXmlDemo</servlet-class> </servlet> <!--配置Servlet的访问路径--> <servlet-mapping> <!--写入自己刚刚给Servlet起的名--> <servlet-name>demo</servlet-name> <!--相当于@WebServlet(urlPatterns = "/demo") 此处用的是精确路径--> <url-pattern>/demo</url-pattern> </servlet-mapping> </web-app>
-
运行截图如下
-
Request(请求)
- 作用:使用request对象获取浏览器的请求数据
- Request解释: 浏览器与服务端之间会进行请求和响应,其中浏览器向服务端发送请求时会将HTTP的请求数据(就是一些字符串)发送到服务端,当服务端接收到请求数据时Tomcat会将这些数据解析,解析完成后Tomcat会将其封装到request对象中进行保存,所以可通过request对象来获取浏览器的请求数据来进行一系列操作
- request是一个接口,它的实现类
RequestFacade
由Tomcat提供 - 在学Servlet接口时,里面有个
void service(ServletRequest request, ServletResponse response)
方法,它的两个参数其实就是Request
对象和Response
对象。
Request继承体系
-
Request是三层继承体系
ServletRequest
:Java提供的请求对象根接口HttpServletRequest
:Java提供的对Http协议封装的请求对象接口RequestFacade
:由Tomcat定义的实现类HttpServletRequest
是ServletRequest
的子接口,RequestFacade
是HttpServletRequest
的实现类
-
注意
-
使用request对象中的方法时只需查阅JavaEE API 文档的
HttpServletRequest
接口即可 ,因为它是起其实现类。 -
HttpServletRequest
是HttpServlet实现类中所用到的接口,ServletRequest
是Servlet接口中所用到的接口,Java不提供ServletRequest
和HttpServletRequest
的实现类,他们的实现类RequestFacade
由Tomcat提供- 原因: 浏览器的请求数据发送到服务端后会被Tomcat解析,Tomcat解析完成后会将其封装到request对象中,然后作为参数传递给对应的方法作为参数。由于是Tomcat解析的,所以也该有Tomcat对request接口进行实现
-
我们学习
Servlet
时,如果我们实现的是Servlet
接口的话,那么service
方法中的参数是ServletRequest
接口,如图一所示;但是我们是通过继承HttpServlet
来写Servlet代码的话则doGet
和doPost
方法使用的是HttpServletRequest
接口,如图二所示。
-
Request获取请求数据
- 请求数据分为三部分
- 请求行
- 请求头
- 请求体
Request获取请求行
-
请求行由三部分组成:请求方式 /请求资源路径和请求参数 / 请求的协议和版本号
-
请求行用到的方法
方法 解释 String getMethod()
获取请求方式: GET
String getContextPath()
动态获取虚拟目录(即项目访问路径): /request-demo
StringBuffer getRequestURL()
获取URL(统一资源定位符: ?
前面的那一串):http://localhost:8080/request-demo/req1
String getRequestURI()
获取URI(统一资源标识符): /request-demo/req1
String getQueryString()
获取GET方式的请求参数(get方式的请求参数在url中): username=zhangsan&password=123
- 注意:除了最后一个方法外,其余几个方法可放在
doPost
方法中,详见请求体代码示例2
- 注意:除了最后一个方法外,其余几个方法可放在
-
代码示例
package at.guigu.webb; 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("/req1") public class RequestOne extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1 获取请求方式 String method = req.getMethod(); System.out.println(method); //2 动态获取虚拟目录即项目访问路径 //由于我并未去设置项目访问路径,所以默认为改项目名称即WebDemo String contextPath = req.getContextPath(); System.out.println(contextPath); //3 获取URL StringBuffer reqUrl = req.getRequestURL(); System.out.println(reqUrl.toString());//将其转为字符串 //4 获取URI String uri = req.getRequestURI(); System.out.println(uri); //5 获取Get的请求参数 String message = req.getQueryString(); System.out.println(message); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("post..."); } }
Request获取请求头
-
请求头:格式为
key:value
键值对形式-
String getHeader(String name)
:根据请求头名称来获取对应的值 -
代码示例
package at.guigu.webb; 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.BufferedReader; import java.io.IOException; @WebServlet("/req1") public class RequestOne extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1 获取请求方式 String method = req.getMethod(); System.out.println(method); //2 动态获取虚拟目录即项目访问路径 //由于我并未去设置项目访问路径,所以默认为改项目名称即WebDemo String contextPath = req.getContextPath(); System.out.println(contextPath); //3 获取URL StringBuffer reqUrl = req.getRequestURL(); System.out.println(reqUrl.toString());//将其转为字符串 //4 获取URI String uri = req.getRequestURI(); System.out.println(uri); //5 获取Get的请求参数 String message = req.getQueryString(); System.out.println(message); System.out.println("-------------------------------------"); //获取请求头 String header = req.getHeader("User-Agent"); System.out.println(header); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } }
-
Request获取请求体(只有post请求才有请求体)
-
请求体(只有post请求才有请求体):存放浏览器的请求参数
-
ServletInputStream getInputStream()
:获取字节输入流 -
BufferedReader getReader()
:获取字符输入流 -
代码示例1
java代码
package at.guigu.webb; 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.BufferedReader; import java.io.IOException; @WebServlet("/req1") public class RequestOne extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1 获取请求方式 String method = req.getMethod(); System.out.println(method); //2 动态获取虚拟目录即项目访问路径 //由于我并未去设置项目访问路径,所以默认为改项目名称即WebDemo String contextPath = req.getContextPath(); System.out.println(contextPath); //3 获取URL StringBuffer reqUrl = req.getRequestURL(); System.out.println(reqUrl.toString());//将其转为字符串 //4 获取URI String uri = req.getRequestURI(); System.out.println(uri); //5 获取Get的请求参数 String message = req.getQueryString(); System.out.println(message); System.out.println("-------------------------------------"); //获取请求头 String header = req.getHeader("User-Agent"); System.out.println(header); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取请求体 //获取字符输入流 BufferedReader br = req.getReader(); //读取数据 String line = br.readLine(); System.out.println(line); } }
html代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--规定当提交表单时向何处发送表单数据,该属性值就是URL.--> <!--注意:数据都要提交到服务端,所以该属性需要书写服务端的URL--> <!--书写时本地可省略主机:端口号--> <!--我的url为http://localhost:8080/WebDemo/req1--> <!--所以action属性值为:/WebDemo/req1--> <!--method设置表单的提交方式,此处设置为post--> <form action="/WebDemo/req1" method="post"> <input type="text" name="username"> <input type="password" name="password"> <input type="submit"> </form> </body> </html>
若想要使html页面展示出来,则在Tomcat运行之后需要在url后加上
/html的文件名
,而不是/req1
-
代码示例2:html代码与示例1一样,此处省略
package at.guigu.webb; 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.BufferedReader; import java.io.IOException; @WebServlet("/req1") public class RequestOne extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1 获取请求方式 String method = req.getMethod(); System.out.println(method); //2 动态获取虚拟目录即项目访问路径 //由于我并未去设置项目访问路径,所以默认为改项目名称即WebDemo String contextPath = req.getContextPath(); System.out.println(contextPath); //3 获取URL StringBuffer reqUrl = req.getRequestURL(); System.out.println(reqUrl.toString());//将其转为字符串 //4 获取URI String uri = req.getRequestURI(); System.out.println(uri); //5 获取请求体---获取Post请求参数 //5.1 获取字符输入流 BufferedReader br = req.getReader(); //5.2 读取数据 String line = br.readLine(); System.out.println(line); System.out.println("-------------------------------------"); //获取请求头 String header = req.getHeader("User-Agent"); System.out.println(header); } }
-
Request通用方式获取请求参数
-
不同方式获取请求参数(详见Request获取请求数据)
-
Get方式获取请求参数:
String getQueryString()
-
POST方式获取请求参数:
ServletInputStream getInputStream()
、BufferedReader getReader()
-
-
问题:Get方式获取请求参数用
doGet
方法;Post方式获取请求参数用doPost
方法。在doGet
方法和doPost
方法中除了获取请求参数用到的方法不同外,其余请求行所用到的方法均相同(可详见请求行示例和请求体示例2代码),所以就会造成代码冗余的问题,为了解决该问题就提供了一种统一获取请求参数的方式。-
该方式会先获取请求方式,然后根据请求方式的不同来调用不同的方法来获取请求参数。如下图所示
-
获取到请求参数后会将请求参数中的各个参数分割出来,然后转为键值对形式,并将其存储到Map集合中(由于一个键可能对应多个值,所以键值用
String[]
(即字符串数组)表示,由于请求参数中键是字符串,所以Map<String, String[]>
),之后即可使用相应方法来获取Map集合中的键值对,如下图所示
-
-
用到的方法
方法 解释 Map<String, String[]> getParameterMap()
获取所有参数Map集合 String getParameterValues(String name)
根据键的名称获取参数值(数组) String getParameter(String name)
根据键的名称获取参数值(单个值) -
测试get请求 代码示例:
-
html文件代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--规定当提交表单时向何处发送表单数据,该属性值就是URL--> <!--注意:数据都要提交到服务端,所以该属性需要书写服务端的URL--> <!--书写时本地可省略主机:端口号--> <!--我的url为http://localhost:8080/WebDemo/req3--> <!--所以action属性值为:/WebDemo/req3--> <!--method设置表单的提交方式,此处设置为get--> <form action="/WebDemo/req3" method="get"> <input type="text" name="username"> <input type="password" name="password"> <input type="checkbox" name="hobby" value="1">游泳 <input type="checkbox" name="hobby" value="2">爬山<br> <input type="submit"> </form> </body> </html>
java代码
package at.guigu.webb; 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.Map; @WebServlet("/req3") public class RequestThree extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //get请求逻辑 System.out.println("get..."); //1 获取所有参数的Map集合 Map<String, String[]> map = req.getParameterMap(); for (String key : map.keySet()) { //返回键 System.out.print(key + ":"); //返回键值 String[] values = map.get(key); for (String value : values) { System.out.print(value + " "); } System.out.println();//换行 } System.out.println("-----------------------------------"); //2 根据键的名称获取参数值数组 String[] values = req.getParameterValues("hobby"); for (String value : values) { System.out.print(value + " "); } System.out.println();//换行 System.out.println("-----------------------------------"); //3 根据键的名称获取单个参数值 String value = req.getParameter("username"); System.out.println(value); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //post请求逻辑 System.out.println("post..."); } }
-
-
测试post请求 代码示例
html文件代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--规定当提交表单时向何处发送表单数据,该属性值就是URL--> <!--注意:数据都要提交到服务端,所以该属性需要书写服务端的URL--> <!--书写时本地可省略主机:端口号--> <!--我的url为http://localhost:8080/WebDemo/req3--> <!--所以action属性值为:/WebDemo/req3--> <!--method设置表单的提交方式,此处设置为post--> <form action="/WebDemo/req3" method="post"> <input type="text" name="username"> <input type="password" name="password"> <input type="checkbox" name="hobby" value="1">游泳 <input type="checkbox" name="hobby" value="2">爬山<br> <input type="submit"> </form> </body> </html>
java代码
package at.guigu.webb; 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.Map; @WebServlet("/req3") public class RequestThree extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //get请求逻辑 System.out.println("get..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //post请求逻辑 System.out.println("post..."); //1 获取所有参数的Map集合 Map<String, String[]> map = req.getParameterMap(); for (String key : map.keySet()) { //返回键 System.out.print(key + ":"); //返回键值 String[] values = map.get(key); for (String value : values) { System.out.print(value + " "); } System.out.println();//换行 } System.out.println("-----------------------------------"); //2 根据键的名称获取参数值数组 String[] values = req.getParameterValues("hobby"); for (String value : values) { System.out.print(value + " "); } System.out.println();//换行 System.out.println("-----------------------------------"); //3 根据键的名称获取单个参数值 String value = req.getParameter("username"); System.out.println(value); } }
-
此时由于get方式和post方式是利用通用方式来获取请求参数,屏蔽了Get和Post请求方式的代码不同,所以此时代码定义为如下格式
-
将相同代码写在
doGet
(或doPost
)方法中,然后在doPost
(或doGet
)方法中调用即可package at.guigu.webb; 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.Map; @WebServlet("/req3") public class RequestThree extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1 获取所有参数的Map集合 Map<String, String[]> map = req.getParameterMap(); for (String key : map.keySet()) { //返回键 System.out.print(key + ":"); //返回键值 String[] values = map.get(key); for (String value : values) { System.out.print(value + " "); } System.out.println();//换行 } System.out.println("-----------------------------------"); //2 根据键的名称获取参数值数组 String[] values = req.getParameterValues("hobby"); for (String value : values) { System.out.print(value + " "); } System.out.println();//换行 System.out.println("-----------------------------------"); //3 根据键的名称获取单个参数值 String value = req.getParameter("username"); System.out.println(value); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req,resp); } }
-
IDEA模板创建Servlet
-
步骤
-
Step1:右键包名New→
Servlet
-
Step2:填写类名→OK即可
-
创建成功后模板如下:
-
-
以上模板中还需自己写入this.doGet方法,所以可对该模板进行更改,步骤如下:
-
File→Editor→File and Code Templates→Files中找到Servlet,然后在该模板中加上
this.doGet(request,response)
即可 -
此时利用IDEA模板创建的Servlet即是完整的,如图所示
-
-
注意:右键包名创建Servlet时,若没有Servlet选项,处理方法如下:
-
File→Editor→File and Code Templates→Other→Web→复制Servlet Annotated Class.java的代码
-
从Other切换到Files,剩下操作如图所示
-
此时右键New中既有Servlet,如图所示
-
Request请求参数中文乱码处理
- 解释:请求参数中若存在中文数据则会乱码
Post请求参数中文乱码
-
Post请求参数中文乱码代码示例
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--规定当提交表单时向何处发送表单数据,该属性值就是URL.--> <!--注意:数据都要提交到服务端,所以该属性需要书写服务端的URL--> <!--书写时本地可省略主机:端口号--> <!--我的url为http://localhost:8080/WebDemo/req5--> <!--所以action属性值为:/WebDemo/req5--> <!--method设置表单的提交方式,此处设置为post--> <form action="/WebDemo/req5" method="post"> <input type="text" name="username"> <input type="password" name="password"> <input type="checkbox" name="hobby" value="1">游泳 <input type="checkbox" name="hobby" value="2">爬山 <br> <input type="submit"> </form> </body> </html>
java
package at.guigu.webb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/req5") public class RequestFive extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取请求参数 String username = request.getParameter("username"); System.out.println(username); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-
Post方式出现乱码原因
- POST的请求参数底层是通过request的
getReader()
方法来获取流中的数据,而TOMCAT在获取流的时候采用的编码是ISO-8859-1
,ISO-8859-1
编码是不支持中文的,所以会出现乱码
- POST的请求参数底层是通过request的
-
解决方式: 利用request中的方法
void setCharacterEncoding("编码格式")
来指定编码,如下所示- 注意
- 必须先设置编码在获取数据
void setCharacterEncoding("编码格式")
可写在doPost
方法中
package at.guigu.webb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/req5") public class RequestFive extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //解决Post方式中文乱码 request.setCharacterEncoding("UTF-8"); //获取请求参数 String username = request.getParameter("username"); System.out.println(username); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
- 注意
Get请求参数中文乱码
-
Get请求参数中文乱码代码示例
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--规定当提交表单时向何处发送表单数据,该属性值就是URL.--> <!--注意:数据都要提交到服务端,所以该属性需要书写服务端的URL--> <!--书写时本地可省略主机:端口号--> <!--我的url为http://localhost:8080/WebDemo/req5--> <!--所以action属性值为:/WebDemo/req5--> <!--method设置表单的提交方式,此处设置为get--> <form action="/WebDemo/req5" method="get"> <input type="text" name="username"> <input type="password" name="password"> <input type="checkbox" name="hobby" value="1">游泳 <input type="checkbox" name="hobby" value="2">爬山 <br> <input type="submit"> </form> </body> </html>
java代码与Post请求参数中文乱码解决方案中java代码一样,运行截图如下
-
Get请求方式产生中文乱码的原因
浏览器通过Http协议发送请求和数据给服务端,浏览器在发送Http协议的过程中会对中文数据进行URL编码 (URL编码会采用html文件中
<meta>
标签指定的UTF-8
的方式进行编码),而后台服务器接收到请求参数后Tomcat会对其进行处理并按照ISO-8859-1
进行URL解码 ,由于前后进行编码和解码的格式不同,所以产生乱码问题 -
问题:
-
GET请求的中文乱码问题的解决是否可以用Post请求的中文乱码问题解决方案
GET请求获取请求参数的方式是
request.getQueryString()
,POST请求获取请求参数的方式是request.getReader()
,request.setCharacterEncoding("utf-8")
是设置request处理流的编码,getQueryString方法并没有通过流的方式获取数据,所以GET请求不能用设置编码的方式来解决中文乱码问题 -
若把
html
文件的<meta>
标签的charset属性改成ISO-8859-1
,后台不做操作,能解决中文乱码问题么?不能,因为
ISO-8859-1
本身是不支持中文展示的,所以改了<meta>
标签的charset属性后,会导致页面上的中文内容都无法正常展示。
-
URL编码
-
定义
- 将字符串按照编码方式转为二进制,然后将每个字节转为2个16进制数并在前面加上
%
- 将字符串按照编码方式转为二进制,然后将每个字节转为2个16进制数并在前面加上
-
解释
-
(以张三为例):一个汉字有3个字节,共有两个汉字,所以一共有6个字节,每个字节用8个二进制位表示,所以一共有48个二进制位,如图所示
-
一个字节有8个二进制位(其中四个二进制位可组成一个16进制数,也就是说一个字节可以变成两个16进制数),将每个字节转为两个16进制数(两个为一体),然后在这两个十六进制数前面加上
%
,如图所示
-
Get中文乱码解决方式
-
注意
将请求参数张三传递到服务端后虽然产生了乱码,但是请求参数张三和乱码的底层本质是一样的(即字节是一样的,只是说用不同的编码格式进行了编码和解码,所以造成了乱码),所以我们可以利用字节来进行乱码的处理
-
步骤
-
Step1:将乱码转换为字节数据
用到String类中的
byte[] getBytes(Charset charset)
方法,作用:使用指定的编码格式将字符串编码为指定的字节序列并将结果存储在字节数组中 -
Step2:将字节数组转换为字符串
用到String类中的构造器
String(byte[] bytes, Charset charset)
将字节数组按照指定编码格式转换为字符串 -
代码示例
package at.guigu.webb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/req5") public class RequestFive extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取请求参数 String username = request.getParameter("username"); //解决Get方式中文乱码 //Step1:将请求参数转换为字节数据 byte[] bytes = username.getBytes("ISO-8859-1"); //Step2:将字节数组转换为指定编码格式的请求参数 username = new String(bytes, "UTF-8"); System.out.println(username); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //解决Post方式中文乱码 request.setCharacterEncoding("UTF-8"); this.doGet(request, response); } }
-
-
注意
-
Tomcat8.0之后已将Get请求方式的中文乱码问题解决,设置了默认的编码方式为UTF-8
-
Get中文乱码的解决方式对Post中文乱码同样适用,即代码变为如下
package at.guigu.webb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/req5") public class RequestFive extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取请求参数 String username = request.getParameter("username"); //解决Get方式中文乱码---也可解决Post方式中文乱码 //Step1:将请求参数转换为字节数据 byte[] bytes = username.getBytes("ISO-8859-1"); //Step2:将字节数组转换为指定编码格式的请求参数 username = new String(bytes, "UTF-8"); System.out.println(username); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-
Request请求转发(forward)
-
转发(forward)定义
-
转发是一种在服务器内部的资源跳转方式
-
解释
假设现在服务端有资源A和资源B,现在浏览器请求服务端的资源A,资源A处理了浏览器的一部分请求之后会跳转到资源B,由资源B继续处理浏览器的请求,资源B处理完之后会向浏览器发出响应,其中资源A跳转到资源B我们称之为转发(forward)
-
-
转发用到的request方法
方法 解释 RequestDispatcher getRequestDispatcher(String path)
将请求转发到另一个资源(如 Servlet、JSP、HTML 文件等)或包含另一个资源的内容。 path
:表示请求转发的目标路径,该路径可以是相对路径或绝对路径。void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException
将请求从一个 Servlet 转发到服务器上的另一个资源,而不通过客户端(浏览器) -
转发实现方式
利用
request.getRequestDispatcher("资源B路径").forward(request, response);
方法进行实现,且该方法要写在资源A中 -
代码示例
-
资源A
package at.guigu.webb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; /** * 请求转发 :由RequestSix转发到RequestSeven */ @WebServlet("/req6") public class RequestSix extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("现在是RequestSix..."); //请求转发 request.getRequestDispatcher("/req7").forward(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-
资源B
package at.guigu.webb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/req7") public class RequestSeven extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("已转发到Request7..."); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-
-
注意
-
由于资源A处理了一部分请求后会跳转到资源B由资源B继续处理,所以资源A和B共享浏览器发送的请求参数的数据资源
-
请求转发资源间共享数据用到的方法
Request
方法解释 void setAttribute(String name,Object o)
存储数据到request域中 Object getAttribute(String name)
获取request域中对应属性的值(即根据key获取值) void removeAttribute(String name)
删除request域中的数据(即根据key删除该键值对) 注意
String name
:要存储的属性名
Object o
:要存储的属性值 -
示例
资源A
package at.guigu.webb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; /** * 请求转发 :由RequestSix转发到RequestSeven */ @WebServlet("/req6") public class RequestSix extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("现在是RequestSix...并已进行数据存储"); //存储数据 request.setAttribute("message", "zhang"); //请求转发 request.getRequestDispatcher("/req7").forward(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
资源B
package at.guigu.webb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/req7") public class RequestSeven extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("已forward到Request7...且共享的数据为"); //获取数据 String message = (String) request.getAttribute("message"); System.out.println(message); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-
-
请求转发的特点
- 浏览器地址栏路径不会发生变化
- 虽然后台从
/req6
转发到/req7
,但是浏览器的地址一直是/req5
,未发生变化
- 虽然后台从
- 只能转发到当前服务器的内部资源
- 不能从一个服务器通过转发访问另一台服务器
- 请求转发只有一次请求和一次响应,所以可以在转发的资源间使用request共享数据
- 虽然后台从
/req3
转发到`/req67,但是这个只有一次请求
- 虽然后台从
- 浏览器地址栏路径不会发生变化
Response
- Response作用: 使用response对象设置响应数据
- Response解释: 服务端对浏览器的请求数据处理完成之后,服务端的Tomcat会向浏览器发送响应数据(即也是一些字符串),浏览器接收到响应数据后就会显示出响应的页面了。
- Tomcat会借助
response
对象来将响应数据发送到客户端,因为response对象中存放已经设置好并要发送的响应数据,所以Tomcat在做出响应之前会先把response中的数据取出来并拼成字符串传给浏览器。
- Tomcat会借助
Response
是一个接口,它的实现类ResponseFacade
是由Tomcat提供的- 在学Servlet接口时,里面有个
void service(ServletRequest request, ServletResponse response)
方法,它的两个参数其实就是Request
对象和Response
对象。
Response继承体系
- Response作用:使用response对象设置响应数据
-
Response是也是三层继承体系
ServletResponse
:Java提供的请求对象根接口HttpServletResponse
:Java提供的对Http协议封装的请求对象ResponseFacade
:由Tomcat定义的实现类
-
注意
-
使用response对象时只需查阅JavaEE API文档的
HttpServletResponse
接口即可,因为它是其实现类 -
HttpServletResponse
是HttpServlet实现类中所用到的接口,ServletResponse
是Servlet接口中所用到的接口,Java不提供ServletResponse
和HttpServletResponse
的实现类,他们的实现类ResponseFacade
由Tomcat提供 -
我们学习
Servlet
时,如果我们实现的是Servlet
接口的话,那么service
方法中的参数是ServletResponse
接口,如图一所示;但是我们是通过继承HttpServlet
来写Servlet代码的话则doGet
和doPost
方法使用的是HttpServletResponse
接口,如图二所示。
-
Response设置响应数据功能介绍
-
响应数据分为三部分
- 响应行:响应数据的第一行。
- HTTP/1.1标识协议版本
- 200标识响应状态码
- OK标识状态码的描述
- 响应头:从响应数据的第二行开始,格式为
key:value
键值对形式 - 响应体:最后一部分存放响应数据。上图中
<html>...</html>
这部分内容就是响应体
- 响应行:响应数据的第一行。
-
用到response对象的方法
响应行用到的方法 解释 void setStatus(int sc)
设置响应状态码 响应头用到的方法 解释 void setHeader(String name,String value)
设置响应头键值对 response.setHeader("content-type", "text/html")
该示例是告诉浏览器带html标签的字符串是html语句,浏览器需按照html文件进行解析并显示在页面上,详见Response响应字符数据 响应体用到的方法 解释 PrintWriter getWriter() throws IOException
获取字符输出流,可以向客户端(通常是浏览器)发送字符文本数据。 ServletOutputStream getOutputStream()
获取字节输出流
Response完成重定向(Redirect)
-
重定向(Redirect)定义
-
一种资源跳转方式
-
解释1
假设现在服务端有资源A和资源B,现在浏览器请求服务端的资源A,但是服务端的资源A处理不了浏览器的请求,但是资源A知道资源B能够处理浏览器的请求,此时资源A会向浏览器发送响应数据来告诉浏览器处理不了,并告诉他能够处理它的请求的资源B的位置,然后浏览器会重新向服务端B发送请求参数,最终由资源B处理浏览器的请求,这个过程就称之为重定向(Redirect)
- 响应数据内容包括:给浏览器响应一个302的状态码和location的一个访问资源B的路径
-
解释2
-
浏览器发送请求给服务器,服务器中对应的资源A接收到请求
-
资源A现在无法处理该请求,就会给浏览器响应一个302的状态码+location的一个访问资源B的路径
-
浏览器接收到响应状态码为302就会 自动重新发送请求 到location对应的访问地址去访问资源B
-
资源B接收到请求后进行处理并最终给浏览器响应结果,这整个过程就叫重定向
-
注意:302含义:提示所请求的资源已移动到由Location响应头给定的URL,浏览器会自动重新访问到这个页面
-
-
-
重定向用到的response方法
方法 解释 void setStatus(int sc)
设置响应的状态码 void setHeader(String name, String value)
设置响应头字段。 name
: 表示响应头名称;value
: 表示响应头对应的值 -
重定向的实现方式
- Step1:
resp.setStatus(302);
- Step2:
resp.setHeader("location","资源B的访问路径");
- Step1:
-
代码示例
- 资源A
package at.guigu.webbb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/resp1") public class ResponseOne extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("respOne get..."); //重定向实现 //Step1:设置响应状态码 response.setStatus(302); //Step2:设置响应头location以及对应的值 response.setHeader("location", "/resp2"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
- 资源B
package at.guigu.webbb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/resp2") public class ResponseTwo extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("respTwo get..."); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-
重定向提供了一种简化方式
-
用到的方法如下:
方法 解释 void sendRedirect(String location) throws IOException
向客户端发送一个重定向响应,使客户端(通常是浏览器)向一个新的 URL 重新发起请求。 注意:
location
:表示要重定向的目标 URL。这个 URL 可以是相对路径或绝对路径。 相对路径: 相对于当前请求的 URL。
绝对路径: 通常包括协议(如
http://
或https://
)和完整的域名。 -
此时资源A的代码可改为
package at.guigu.webbb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/resp1") public class ResponseOne extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("respOne get..."); //重定向实现 response.sendRedirect("/WebDemo/resp2"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-
-
重定向特点
- 浏览器地址栏路径会发送变化
- 当进行重定向访问的时候,由于是由浏览器发送的两次请求,所以地址会发生变化
- 可以重定向到任何位置的资源(服务内容、外部均可)
- 因为第一次响应结果中包含了浏览器下次要跳转的路径,所以这个路径是可以任意位置资源。
- 两次请求,不能在多个资源使用request共享数据
- 因为浏览器发送了两次请求,是两个不同的request对象,就无法通过request对象进行共享数据
- 浏览器地址栏路径会发送变化
Response资源路径问题
-
在进行请求转发时加上了虚拟目录(如图一所示),而在进行重定向时并没有加虚拟目录(如图二所示),那么问题来了:什么时候加虚拟目录什么时候不加?
-
路径问题解决
- 若浏览器使用路径:则需要加虚拟目录(即项目访问路径),比如Response重定向
- 若服务器使用路径:则不需要加虚拟目录(即项目访问路径),比如Request请求转发
-
虚拟目录配置
-
静态配置: 在Web项目的pom.xml文件中配置,在Tomcat插件的
<plugin>
标签体内添加如下代码<plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <!--以下为静态配置路径--> <configuration> <path>写入自己配置的路径</path> </configuration> </plugin>
- 注意:静态配置属于高耦合,即模块之间存在大量的依赖关系。因为静态配置虚拟目录后需要使Servlet接口中所有使用到资源路径都必须统一使用自己配置的路径,否则报错
-
动态配置路径(推荐): 可降低耦合性
-
用到的request对象的方法
String getContextPath()
,作用:动态获取虚拟目录(即项目访问路径),如下所示package at.guigu.webbb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/resp1") public class ResponseOne extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("respOne get..."); //动态获取虚拟目录 String contextPath = request.getContextPath(); //重定向实现 //Step1:设置响应状态码 response.setStatus(302); //Step2:设置响应头location以及对应的值 response.setHeader("location", contextPath + "/resp2"); /* //重定向实现 简化版 response.sendRedirect(contextPath + "/resp2"); */ } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-
-
Response响应字符数据
-
用到的方法
Response方法 解释 PrintWriter getWriter() throws IOException
获取字符输出流,可以向客户端(通常是浏览器)发送字符文本数据。 void setContentType(String type)
设置响应的内容类型,告知客户端如何处理响应数据。例如,设置为 text/html
表示响应数据是HTML格式,浏览器会将其作为网页内容进行渲染。void setHeader(String name,String value)
设置响应头键值对 PrintWriter方法 解释 void write(int c)
写入单个字符 void write(char[] buf)
写入字符数组 void write(char[] buf, int off, int len)
写入字符数组的一部分 void write(String s)
写入字符串 void write(String s, int off, int len)
写入字符串的一部分 注意:
void setContentType(String type)
方法可在指定内容类型后并指定编码格式,中间用;
隔开,详见代码示例3-
响应的内容类型如下
内容类型 解释 text/html
HTML格式,浏览器会将其作为网页进行渲染。 text/plain
纯文本格式,浏览器会将其作为普通文本显示。默认情况下为该纯文本格式 application/json
SON格式,用于发送JSON数据,客户端(例如Ajax请求)可以解析为JSON对象 application/xml
XML格式,用于发送XML数据 image/png
PNG图片格式,用于发送图片数据 text/css
CSS格式,用于发送样式表 text/javascript
JavaScript格式,用于发送脚本
-
-
步骤
- Step1:通过Response对象的
PrintWriter getWriter()
获取字符输出流 - Step2:利用
PrintWriter
对象的writer
方法写入字符数据
- Step1:通过Response对象的
-
代码示例1
package at.guigu.webbb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; import java.io.PrintWriter; /** * 设置字符响应数据:设置字符数据的响应体 */ @WebServlet("/resp3") public class ResponseThree extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.write("this is output string!!!"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-
代码示例2:除了单纯响应字符数据外,还可以写入html数据,但是要进行特殊处理才能被浏览器当作html解析并显示在页面上
package at.guigu.webbb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; import java.io.PrintWriter; /** * 设置字符响应数据:设置字符数据的响应体 */ @WebServlet("/resp3") public class ResponseThree extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.write("this is output string!!!"); response.setHeader("content-type", "text/html"); out.println("<h1>this is title</h1>"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-
代码示例3:当我们输出中文数据时会产生乱码问题 ,因为response对象所调用出来的流会自动使用
ISO-8859-1
进行编码,所以此时我们可以调用response对象的void setContentType()
方法来更改编码格式,代码如下所示- 注意:该方法在
response.setHeader()
方法的参数为"content-type"
,"text/html"
时可代替它 void setContentType()
好处- 用来专门设置
content-type
响应头的信息 - 可指定编码格式
- 用来专门设置
package at.guigu.webbb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; import java.io.PrintWriter; /** * 设置响应数据 */ @WebServlet("/resp3") public class ResponseThree extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /*告诉浏览器带html标签的字符串需要按照html文件进行解析 并按照utf-8的编码格式对中文字符进行编码输出使其能 在页面上正常显示 */ response.setContentType("text/html;charset=utf-8"); PrintWriter out = response.getWriter(); out.write("this is output string!!!"); out.println("<h1>this is title</h1>"); out.println("<h2>这就是更改编码后可正常输出的中文</h2>"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
- 注意:该方法在
-
注意
- 在Response响应字符数据中我们不需要手动关闭
PrintWriter
输出流,因为它是response对象调用出来的,当响应结束后,response对象会被自动销毁,从而使输出流被自动释放。(也就是说该流会被服务器关闭)
- 在Response响应字符数据中我们不需要手动关闭
Response响应字节数据
-
用到的方法
方法 解释 ServletOutputStream getOutputStream()
获取字节输出流 void setContentType(String type)
设置响应的内容类型,告知客户端如何处理响应数据。例如,设置为 text/html
表示响应数据是HTML格式,浏览器会将其作为网页内容进行渲染。void setHeader(String name,String value)
设置响应头键值对 ServletOutputStream方法 解释 write(int b)
写入单个字节 write(byte[] b)
写入字节数组 write(byte[] b, int off, int len)
写入字节数组的一部分 - 响应的内容类型如下
内容类型 解释 text/html
HTML格式,浏览器会将其作为网页进行渲染。 text/plain
纯文本格式,浏览器会将其作为普通文本显示。默认情况下为该纯文本格式 application/json
SON格式,用于发送JSON数据,客户端(例如Ajax请求)可以解析为JSON对象 application/xml
XML格式,用于发送XML数据 image/png
PNG图片格式,用于发送图片数据 text/css
CSS格式,用于发送样式表 text/javascript
JavaScript格式,用于发送脚本 -
步骤
- Step1:通过字节输入流输入本地字节数据(可详见javase基础day16)
- 若要向客户端传输图片、视频、音频等文件时有Step1,反之则不需要
- Step2:通过
Response
对象的ServletOutputStream getOutputStream()
获取字节输出流 - Step3:利用
ServletOutputStream
对象的writer
方法写入字节数据
- Step1:通过字节输入流输入本地字节数据(可详见javase基础day16)
-
代码示例1
package at.guigu.webbb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/resp4") public class ResponseFour extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletOutputStream out = response.getOutputStream(); out.write(98); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-
代码示例2
package at.guigu.webbb; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; @WebServlet("/resp4") public class ResponseFour extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("image/png"); //Step1:将服务端的图片、视频、音频等文件读取到内存中 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:/大数据/大数据.png")); //Step2:获取字节输出流 ServletOutputStream out = response.getOutputStream(); //Step3:写入响应数据 byte[] bytes = new byte[1024]; int len; while ((len = bis.read(bytes)) != -1) { out.write(bytes, 0, len); } out.flush(); //释放资源 bis.close(); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-
在向客户端传输响应数据时,可以不使用
javase基础day16
所讲的内容,因为我们可以在Web项目的pom.xml文件中的<dependencies>
标签体内导入一个commons-io
依赖,如下所示。它是apache提供的一种IO工具类可简化输入输出流<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.13.0</version> </dependency>
-
简化后的代码如下所示
package at.guigu.webbb; import org.apache.commons.io.IOUtils; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; @WebServlet("/resp4") public class ResponseFour extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("image/png"); //Step1:将服务端的图片、视频、音频等文件读取到内存中 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:/大数据/大数据.png")); //Step2:获取字节输出流 ServletOutputStream out = response.getOutputStream(); /* //Step3:写入响应数据 byte[] bytes = new byte[1024]; int len; while ((len = bis.read(bytes)) != -1) { out.write(bytes, 0, len); } out.flush(); */ //Step3:写入响应数据简化版 IOUtils.copy(bis, out);//将图片、视频、音频等文件直接写入输出流 //释放资源 bis.close(); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-