Servlet
1.http协议
1.1 http协议概述
HTTP 协议(Hypertext Transfer Protocol,超文本传输协议),是一个客户端请求和响应的标准协议,这个协议详细规定了浏览器和万维网服务器之间互相通信的规则。用户输入地址和端口号之后就可以从服务器上取得所需要的网页信息。
通信规则规定了客户端发送给服务器的内容格式,也规定了服务器发送给客户端的内容格式。客户端发送给服务器的格式叫“请求协议";服务器发送给客户端的格式叫“响应协议"
1.2 书写规则
HTTP:// 127.0.011 8080 myweb /servlet01 name=zhangsan
1.3 特点
1.支持客户/服务器模式。
2.简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有 GET、POST。每种方法规定了客户与服务器联系的类型不同。由于 HTTP 协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
3.灵活:HTTP 允许传输任意类型的数据对象。传输的类型由Content-Type加以标记
4.无连接:无连接是表示每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。HTTP1.1 版本后支持可持续连接。通过这种连接,就有可能在建立一个 TCP 连接后,发送请求并得到回应,然后发送更多的请求并得到更多的回应,通过把建立和释放 TCP 连接的开销分摊到多个请求上,则对于每个请求而言,由于 TCP 而造成的相对开销被大大地降低了。而且,还可以发送流水线请求,也就是说在发送请求1之后的回应到来之前就可以发送请求 2.也可以认为,一次连接发送多个请求,由客户机确认是否关闭连接,而服务器会认为这些请求分别来自不同的客户端。
5.无状态:HTTP 协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送 的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
1.4 URL
HTTP(超文本传输协议)是一个基于请求与响应模式的、应用层的协议,常基于 TCP 的连接方式,绝大多数的 Web 开发,都是构建在 HTTP 协议之上的 Web 应用。
HTTP URL(URL 是一种特殊类型的 URI,包含了用于查找某个资源的足够的信息)的格式 如下
http:// host[:port]/[abc_path]
http: //IP(主机名/域名):端口/访问的资源路径
- http 表示要通过 HTTP 协议来定位网络资源;
- host 表示合法的 Internet 主机域名或 者IP 地址;
- port 指定一个端口号,为空则使用缺省端口 80;
- abs_path 指定请求资源的 UR!;如果 URL中没有给出abs_path,那么当它作为请求 URI时,必须以"/"的形式给出,通常 这个工作浏览器自动帮我们完成。
1.5HTTP请求
HTTP请求由三部分组成,分别是:请求行、请求头、请求正文。通过chrome浏览器,F12—> Network查看。
GET 是从服务器上获取数据, POST 是向服务器传送数据
GET 请求参数显示, 都显示在浏览器网址上, HTTP 服务器根据该请求所包含 URL中 的 参 数 来 产 生 响 应 内 容 , 即 “Get” 请 求 的 参 数 是 URL 的 一 部 分
POST 请求参数在请求体当中, 消息长度没有限制而且以隐式的方式进行发送, 通常用来向 HTTP 服务器提交量比较大的数据(比如请求中包含许多参数或者文件上传操作等) ,请求的参数包含在“Content-Type”消息头里, 指明该消息体的媒体类型和编码,
注意: 避免使用 Get 方式提交表单, 因为有可能会导致安全问题。 比如说在登陆表单中用 Get 方式, 用户输入的用户名和密码将在地址栏中暴露无遗。
1.请求
请求行、 请求头部、 空行、 请求数据
常用的请求报头:
-
Host (主机和端口号)
Host: 对应网址 URL 中的 Web 名称和端口号, 用于指定被请求资源的 Internet 主机和端口号, 通常属于 URL 的一部分。
-
Connection (链接类型)
Connection: 表示客户端与服务连接类型
Client 发起一个包含 Connection:keep-alive 的请求, HTTP/1.1 使用 keep-alive 为默认值。
Server 收到请求后:
如果 Server 支持 keep-alive(长连接), 回复一个包含 Connection:keep-alive 的响应, 不关闭连接;如果 Server 不支持 keep-alive, 回复一个包含 Connection:close 的响应, 关闭连接。如果 client 收到包含 Connection:keep-alive 的响应, 向同一个连接发送下一个请求, 直到一方主动关闭连接。
keep-alive 在很多情况下能够重用连接, 减少资源消耗, 缩短响应时间, 比如当浏览器需要多个文件时(比如一个 HTML 文件和相关的图形文件), 不需要每次都去请求建立连接。
-
Upgrade-Insecure-Requests (升级为 HTTPS 请求)
Upgrade-Insecure-Requests: 升级不安全的请求, 意思是会在加载 http 资源时自动替换成 https 请求, 让浏览器不再显示 https 页面中的 http 请求警报。
HTTPS 是以安全为目标的 HTTP 通道, 所以在 HTTPS 承载的页面上不允许出现
HTTP 请求, 一旦出现就是提示或报错。
-
User-Agent (浏览器名称)
User-Agent: 是客户浏览器的详细信息,服务器是通过这条信息来判断来访的用户是否为真实用户。
-
Accept (传输文件类型)
Accept: 指浏览器或其他客户端可以接受的 MIME(Multipurpose Internet Mail Extensions(多用途互联网邮件扩展)文件类型, 服务器可以根据它判断并返回适当的文件格式。
举例:
Accept: /: 表示什么都可以接收。
Accept: image/gif: 表明客户端希望接受 GIF 图像格式的资源;
Accept: text/html: 表明客户端希望接受 html 文本。
Accept: text/html, application/xhtml+xml;q=0.9, image/*;q=0.8: 表示浏览器支持的 MIME 类型分别是 html 文本、 xhtml 和 xml 文档、 所有的图像格式资源。
q 是权重系数, 范围 0 =< q <= 1, q 值越大, 请求越倾向于获得其“;”之前的类型表示的内容。 若没有指定 q 值, 则默认为 1, 按从左到右排序顺序; 若被赋值为 0, 则用于表示浏览器不接受此内容类型。
Text: 用于标准化地表示的文本信息, 文本消息可以是多种字符集和或者多种格式的;Application: 用于传输应用程序数据或者二进制数据。
-
Referer (页面跳转处)
Referer: 表明产生请求的网页来自于哪个 URL, 用户是从该 Referer 页面访问到当前请求的页面。 这个属性可以用来跟踪 Web 求来自哪个页面, 是从什么网站来的等。
有时候遇到下载某网站图片, 需要对应的 referer, 否则无法下载图片, 那是因为人家做了防盗链, 原理就是根据 referer 去判断是否是本网站的地址, 如果不是, 则拒绝, 如果是,就可以下载;
-
Accept-Encoding(文件编解码格式)
Accept-Encoding: 指出浏览器可以接受的编码方式。 编码方式不同于文件格式, 它是为了压缩文件并加速文件传递速度。 浏览器在接收到 Web 响应之后先解码, 然后再检查文件格式, 许多情形下这可以减少大量的下载时间。
举例: Accept-Encoding:gzip;q=1.0, identity; q=0.5, *;q=0
如果有多个 Encoding 同时匹配, 按照 q 值顺序排列, 本例中按顺序支持 gzip, identity压缩编码, 支持 gzip 的浏览器会返回经过 gzip 编码的 HTML 页面。 如果请求消息中没有设置这个域服务器假定客户端对各种内容编码都可以接受。
-
Accept-Language(语言种类)
Accept-Langeuage:指出浏览器可以接受的语言种类,如 en 或 en-us 指英语,zh 或者 zh-cn指中文, 当服务器能够提供一种以上的语言版本时要用到。
-
Accept-Charset(字符编码)
Accept-Charset: 指出浏览器可以接受的字符编码。
举例: Accept-Charset:iso-8859-1,gb2312,utf-8
ISO8859-1: 通常叫做 Latin-1。 Latin-1 包括了书写所有西方欧洲语言不可缺少的附加字符, 英文浏览器的默认值是 ISO-8859-1.
gb2312: 标准简体中文字符集;
utf-8: UNICODE 的一种变长字符编码, 可以解决多种语言文本显示问题, 从而实现应用国际化和本地化。
如果在请求消息中没有设置这个域,缺省是任何字符集都可以接受。
-
Cookie (Cookie)
Cookie: 浏览器用这个属性向服务器发送 Cookie。 Cookie 是在浏览器中寄存的小型数据体, 它可以记载和服务器相关的用户信息, 也可以用来实现会话功能。
-
Content-Type (POST 数据类型)
Content-Type: POST 请求里用来表示的内容类型。
举例: Content-Type = Text/XML; charset=gb2312:
指明该请求的消息体中包含的是纯文本的 XML 类型的数据, 字符编码采用“gb2312”。
2.响应
状态行、 消息报头、 空行、 响应正文
-
Cache-Control: must-revalidate, no-cache, private
这个值告诉客户端, 服务端不希望客户端缓存资源, 在下次请求资源时, 必须要从新请求服务器, 不能从缓存副本中获取资源。
Cache-Control 是 响 应 头 中 很 重 要 的 信 息 , 当 客 户 端 请 求 头 中 包 含
Cache-Control:max-age=0 请求, 明确表示不会缓存服务器资源时,Cache-Control 作为作为回应信息, 通常会返回 no-cache, 意思就是说, “那就不缓存呗”。
当客户端在请求头中没有包含 Cache-Control 时, 服务端往往会定,不同的资源不同的缓存策略, 比如说 oschina 在缓存图片资源的策略就是 Cache-Control: max-age=86400,这个意思是, 从当前时间开始, 在 86400 秒的时间内, 客户端可以直接从缓存副本中读取资源, 而不需要向服务器请求。
-
Connection: keep-alive
这个字段作为回应客户端的 Connection: keep-alive, 告诉客户端服务器的 tcp 连接也是一个长连接, 客户端可以继续使用这个 tcp 连接发送 http 请求。
-
Content-Encoding:gzip
告诉客户端, 服务端发送的资源是采用 gzip 编码的, 客户端看到这个信息后, 应该采用 gzip 对资源进行解码。
-
Content-Type: text/html;charset=UTF-8
告诉客户端, 资源文件的类型, 还有字符编码, 客户端通过 utf-8 对资源进行解码, 然后对资源进行 html 解析。 通常我们会看到有些网站是乱码的, 往往就是服务器端没有返回正确的编码。
-
Date: Sun, 21 Sep 2016 06:18:21 GMT
这个是服务端发送资源时的服务器时间, GMT 是格林尼治所在地的标准时间。 http 协议中发送的时间都是 GMT 的, 这主要是解决在互联网上, 不同时区在相互请求资源的时候,时间混乱问题。
-
Expires:Sun, 1 Jan 2000 01:00:00 GMT
这个响应头也是跟缓存有关的, 告诉客户端在这个时间前, 可以直接访问缓存副本, 很显然这个值会存在问题, 因为客户端和服务器的时间不一定会都是相同的, 如果时间不同就会导致问题。 所以这个响应头是没有 Cache-Control: max-age=*这个响应头准确的, 因为max-age=date 中的 date 是个相对时间, 不仅更好理解, 也更准确。
-
Pragma:no-cache
这个含义与 Cache-Control 等同。
-
Server: Tengine/1.4.6
这个是服务器和相对应的版本, 只是告诉客户端服务器的信息。
-
Transfer-Encoding: chunked
这个响应头告诉客户端, 服务器发送的资源的方式是分块发送的。 一般分块发送的资源都是服务器动态生成的, 在发送时还不知道发送资源的大小, 所以采用分块发送, 每一块都是独立的, 独立的块都能标示自己的长度, 最后一块是 0 长度的, 当客户端读到这个 0 长度的块时, 就可以确定资源已经传输完了。
-
Vary: Accept-Encoding
告诉缓存服务器, 缓存压缩文件和非压缩文件两个版本, 现在这个字段用处并不大, 因为现在的浏览器都是支持压缩的。
3.响应状态码
- 响应状态代码有三位数字组成, 第一个数字定义了响应的类别, 且有五种可能取值。
- 常见状态码:
- 100~199: 表示服务器成功接收部分请求, 要求客户端继续提交其余请求才能完成整个处理过程。
- 200~299: 表示服务器成功接收请求并已完成整个处理过程。 常用 200(OK 请求成功)。
- 300~399: 为完成请求, 客户需进一步细化请求。 例如: 请求的资源已经移动一个新地址、 常用 302(所请求的页面已经临时转移至新的 url) 、 307 和 304(使用缓存资源) 。
- 400~499: 客户端的请求有错误, 常用 404(服务器无法找到被请求的页面) 、 403(服务器拒绝访问, 权限不够) 。
- 500~599: 服务器端出现错误, 常用 500(请求未完成。 服务器遇到不可预知的情况) 。
4.Cookie 和 Session:
- 服务器和客户端的交互仅限于请求/响应过程, 结束之后便断开, 在下一次请求时, 服务器会认为新的客户端。为了维护他们之间的链接, 让服务器知道这是前一个用户发送的请求, 必须在一个地方保存客户端的信息。
- Cookie: 通过在 客户端 记录的信息确定用户的身份。
- Session: 通过在 服务器端 记录的信息确定用户的身份。
2.Tomcat
Tomcat是一个符合JavaEE WEB 标准的最小的WEB容器,所有的JSP程序一定要有WEB容器的支持才能运行,而且在给定的WEB容器里面都会支持事务处理操作。
2.1 目录文件
- bin:启动和关闭 tomcat 的 bat 文件
- conf:配置文件server.xml 该文件用于配置 server 相关的信息,比如 tomcat 启动的端口号,配置主机(Host);web.xml文件配置与web应用(web 应用相当于一个 web站点);tomcat-user.xml 配置用户名密码和相关权限
- lib:该目录放置运行 tomcat 运行需要的 jar 包
- logs:存放日志,当我们需要查看日志的时候,可以查询信息
- webapps:放置我们的 web 应用
- work 工作目录:该目录用于存放 jsp 被访问后生成对应的 server 文件和.class 文件
3.Servlet的实现
Servlet是Server与 Applet 的缩写,是服务端小程序的意思。使用Java 语言编写的服务器端程序,可以像生成动态的 WEB 页,Servlet 主要运行在服务器端,并由服务器调用执行,是一种按照 Servlet 标准来开发的类。是 SUN 公司提供的一门用于开发动态 Web 资源的技术。(言外之意:要实现 web 开发,需要实现 Servlet 标准)
Servlet 本质上也是Java 类,但要遵循 Servlet规范进行编写,没有 main()方法,它的创建、使用、销毁都由Servlet 容器进行管理(如 Tomcat)。(言外之意:写自己的类,不用写 main 方法,别人自动调用)
Servlet 是和 HTTP 协议是紧密联系的,其可以处理 HTTP 协议相关的所有内容。这也是 Servlet 应用广泛的原因之一。
提供了 Servlet 功能的服务器,叫做 Servlet 容器,其常见容器有很多,如 Tomcat,Jetty,WebLogic Server,WebSphere,JBoss 等等。
3.1 servlet的实现
实现Servlet
- 1.创建普通Java类
- 2.实现Servlet的规范 继承HttpServlet类
- 3.重写service方法 用来处理请求
//@WebServlet("/ser01")
//@WebServlet(name = "ServletFirst",value = {"/se1","/se2"})
//@WebServlet(name = "ServletFirst",urlPatterns = "/se1")
@WebServlet(name = "ServletFirst",urlPatterns = {"/se1","/se2"})//Patterns模式
public class ServletFirst extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//打印在控制台
System.out.println("hello servlet!");
//通过流输出数据到浏览器
resp.getWriter().write("hello servlet!");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
//互调
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
//互调
doGet(req, resp);
}
}
3.2继承GenericServlet
public class Servlet2 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {}
}
3.3实现Servlet接口
public class Servlet3 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {}
@Override
public ServletConfig getServletConfig() {return null;}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {}
@Override
public String getServletInfo() {return null;}
@Override
public void destroy() {}
}
3.4 servlet的生命周期
Servlet没有 main()方法,不能独立运行,它的运行完全由 Servlet 引擎来控制和调度。 所谓生命周期,指的是 servlet 容器何时创建 servlet 实例、何时调用其方法进行请求的处理、 何时并销毁其实例的整个过程。
3.4.1实例和初始化时机
当请求到达容器时,容器查找该 servlet 对象是否存在,如果不存在,则会创建实例并进行初始化。
3.4.2就绪/调用/服务阶段
有请求到达容器,容器调用 servlet 对象的 service()方法,处理请求的方法在整个生命周期中可以被多次调用:HttpServlet 的 service()方法,会依据请求方式来调用 doGet()或者 doPost()方法。但是,这两个 do 方法默认情况下,会抛出异常,需要子类去 override。
3.4.3销毁时机
当容器关闭时(应用程序停止时),会将程序中的Servet实例进行销毁。上述的生命周期可以通过 Servlet 中的生命周期方法来观察。在 Servlet 中有三个生命周 期方法,不由用户手动调用,而是在特定的时机有容器自动调用,观察这三个生命周期方法 即可观察到 Servlet 的生命周期。
init初始化
//初始化方法,系统自动调用
//当请求到达Servlet容器时,Servlet容器会判断该Servlet对象是否存在,如果不存在则创建对象并初始化,调用该方法
@Override
public void init() throws ServletException {
System.out.println("Servlet创建");
}
service就绪 / 服务
//就绪/服务方法(处理请求的方法)
//当有请求到达Servlet时,会调用该方法
//可以被多次调用
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//打印在控制台
System.out.println("hello servlet!");
//通过流输出数据到浏览器
resp.getWriter().write("hello servlet!");
}
destroy销毁
//销毁方法,系统自动调用
//当服务器关闭或应用程序停止时,调用该方法
@Override
public void destroy() {
System.out.println("Servlet销毁");
}
如何工作的
- Web Client向Servlet容器(Tomcat)发出Http请求
- Servlet容器接收Web Client的请求
- Servlet容器创建一个HttpServletRequest对象,将Web Client请求的信息封装到这个对象中
- Servlet容器创建一个HttpServletResponse对象
- Servlet容器调HttpServlet对象service方法,把Request与Response作为参数,传给HttpServlet
- HttpServlet调用HttpServletRequest对象的有关方法,获取Http请求信息
4.HttpServletRequest对象
- HttpServletRequest 对象:主要作用是用来接收客户端发送过来的请求信息,例如:请求的参数,发送的头信息等都属于客户端发来的信息,service()方法中形参接收的是 HttpServletRequest 接口的实例化对象,表示该对象主要应用在 HTTP 协议上,该对象是由 Tomcat 封装好传递过来。
- HttpServletRequest是ServletRequest 的子接口,ServletRequest 只有一个子接口,就是HttpServletRequest。既然只有一个子接口为什么不将两个接口合并为一个?
- 从长远上讲:现在主要用的协议是 HTTP协议,但以后可能出现更多新的协议。若以后想要支持这种新协议,只需要直接继承 ServletRequest 接口就行了。
- 在 HttpServletRequest接口中,定义的方法很多,但都是围绕接收客户端参数的。但是怎么拿到该对象呢?
- 不需要,直接在 Service 方法中由容器传入过来,而我们需要做的就是取出对象中的数据,进行分析、处理。
作用
接收并封装客户端发送过来的请求源信息,由Tomcat封装好传参过来
常用方法
方法 | 作用 |
---|---|
getRequestURL() | 获取客户端发出请求时的完整URL |
getRequestURI() | 获取请求行中的资源名称部分(项目名称开始) |
getQueryString() | 获取请求行中的参数部分 |
getMethod() | 获取客户端请求方式 |
getProtocol() | 获取 HTTP 版本号 |
getContextPath() | 获取 webapp 名字 |
获取参数
方法 | 作用 |
---|---|
getParameter() | 获取指定名称的参数 |
getParameterValues() | 获取指定名称参数的所有值(复选框传值) |
代码块
@WebServlet("/m")
//http://localhost/m?uname=kun&pwd=123&hobbys=唱&hobbys=跳&hobbys=rap
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("hello");
System.out.println("客户端发出请求时的完整URL "+req.getRequestURL());
System.out.println("请求行中的资源名称部分(项目名称开始) "+req.getRequestURI());
System.out.println("请求行中的参数部分 "+req.getQueryString());
System.out.println("客户端请求方式 "+req.getMethod());
System.out.println("HTTP版本号 "+req.getProtocol());
System.out.println("webapp名字 "+req.getContextPath());
System.out.println("用户名是 "+req.getParameter("uname"));
String[] hobbys = req.getParameterValues("hobbys");
for (String hobby : hobbys) {
System.out.println("爱好是 "+hobby);
}
}
}
乱码问题
在解析文字时,默认的编码规范是ISO-8859-1(此编码不支持中文)(Tomcat8及以上版本,Get请求不会乱码但是Post请求会),有两种解决办法
1.设置HttpServletRequest的编码规范(针对Post请求)
req.setCharacterEncoding("UTF-8");
2.用String方法(通用)
String uname = new String(req.getParameter("uname").getBytes("ISO-8859-1"),"UTF-8");
请求转发
请求转发,是一种服务器的行为,当客户端请求到达后,服务器进行转发,此时会将请求对象进行保存,地址栏中的 URL 地址不会改变,得到响应后,服务器端再将响应发送给客户端,从始至终只有一个请求发出。实现方式如下,达到多个资源协同响应的效果
//req.getRequestDispatcher("url").forward(req, resp);
@WebServlet("/s03")
public class Servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uname = req.getParameter("uname");
System.out.println("Servlet03 uname: " + uname);
//请求跳转
//req.getRequestDispatcher("s04").forward(req, resp);
//req.getRequestDispatcher("login.jsp").forward(req, resp);
req.getRequestDispatcher("login.html").forward(req, resp);
}
}
@WebServlet("/s04")
public class Servlet04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uname = req.getParameter("uname");
System.out.println("Servlet04 uname: " + uname);
}
}
Request作用域
通过该对象可以在一个请求中传递数据,作用范围:再一次请求中有效(请求转发跳转时有效)
方法 | 作用 |
---|---|
setAttribute(String,Object) | 设置域对象内容 |
setAttribute(String) | 获取域对象内容 |
removeAttribute(String) | 删除域对象内容 |
<%-- 如果要在jsp中写Java代码需要写在脚本段中--%>
<%
String name = (String) request.getAttribute("name");
System.out.println(name);
int age = (int) request.getAttribute("age");
List<String> list = (List<String>) request.getAttribute("list");
for (String s : list) {
System.out.println(s);
}
%>
5.HttpServletResponse对象
- Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request 对象和代表响应的 response 对象。
- request 和response 对象代表请求和响应:获取客户端数据,需要通过 request 对象;向客户端输出数据需要通过 response 对象。
- HttpServletResponse 的主要功能用于服务器对客户端的请求进行响应,将 Web 服务器处理后的结果返回给客户端。service()方法中形参接收的是 HttpServletResponse 接口的实例化对象,这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。
响应数据
@WebServlet("/s01")
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//字符流
//PrintWriter writer = resp.getWriter();
//writer.write("hello");
/*两种流不能同时使用 同时使用就会报错 */
//字节流
ServletOutputStream outputStream = resp.getOutputStream();
outputStream.write("hi".getBytes());
}
}
乱码问题
在解析文字时,默认的编码规范是ISO-8859-1(此编码不支持中文)
字符流解决方法
@WebServlet("/s02")
public class Servlet02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置服务端编码格式
resp.setCharacterEncoding("UTF-8");
//设置客户端编码格式
resp.setHeader("content-type","text/html;chaset=UTF-8");
//总结设置客户端和服务端的编码格式都支持中文,且保持一致
//同时设置客户端和服务端的编码格式
resp.setContentType("text/html;chaset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("<h2>你好</h2>");
}
}
字节流解决方法
@WebServlet("/s03")
public class Servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletOutputStream outputStream = resp.getOutputStream();
resp.setCharacterEncoding("UTF-8");
resp.setHeader("content-type","text/html;chaset=UTF-8");
outputStream.write("<h2>你好</h2>".getBytes("UTF-8"));
}
}
重定向
- 重定向是一种服务器指导,客户端的行为。客户端发出第一个请求,被服务器接收处理后,服务器会进行响应,在响应的同时,服务器会给客户端一个新的地址(下次请求的地址response.sendRedirect(url)😉,当客户端接收到响应后,会立刻、马上、自动根据服务器给的新地址发起第二个请求,服务器接收请求并作出响应,重定向完成。
从描述中可以看出重定向当中有两个请求存在,并且属于客户端行为。
// 重定向跳转到index.jsp
response.sendRedirect(“index.jsp”);
- 通过观察浏览器我们发现第一次请求获得的响应码为 302,并且含有一个location 头信息。并且地址栏最终看到的地址是和第一次请求地址不同的,地址栏已经发生了变化。
@WebServlet("/s04")
public class Servlet04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet04");
//地址栏的数据重定向后的网页拿不到数据
resp.sendRedirect("s05");
}
}
请求转发和重定向的区别
请求转发(req.getRequestDispatcher().forward()) | 重定向(resp.sendRedirect()) |
---|---|
一次请求,数据在request域中共享 | 两次请求,request域中数据不共享 |
服务器端行为 | 客户端行为 |
地址栏不发生变化 | 地址栏发生变化 |
绝对地址定位到站点后 | 绝对地址可写到http:// |
只能是当前站点下的资源 | 可以是任意资源 |
6.Cookie
- Cookie是浏览器提供的一种技术,通过服务器的程序能将一些只须保存在客户端,或者在客户端进行处理的数据,放在本地的计算机上,不需要通过网络传输,因而提高网页处理的效率,并且能够减少服务器的负载,但是由于 Cookie 是服务器端保存在客户端的信息,所以其安全性也是很差的。例如常见的记住密码则可以通过Cookie 来实现。
- 有一个专门操作Cookie的类javax.servlet.http.cookie。随着服务器端的响应发送给客户端,保存在浏览器。当下次再访问服务器时把Cookie苒带回服务器。
- Cookie 的格式:键值对用“=“链接,多个键值对间通过“;“隔开。
注意
- Cookie只在当前浏览器中有效(不跨浏览器和电脑)
- Cookie不能存中文,如果一定要存中文,则需要通过URLEncoder.encode()方法进行编码,获取时通过URLDecoder.decode()方法进行解码
- 如果出现同名的Cookie对象,则会覆盖
- Cookie的存储数量是有上限的,不同浏览器的上限不同。Cookie存储的大小是有效的,在4kb左右
6.1Cookie的创建和获取
@WebServlet(“/cook01”)
public class Cookie01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//cookie的创建
Cookie cookie = new Cookie(“name”, “zhangsan”);
resp.addCookie(cookie);
@WebServlet("/cook01")
public class Cookie01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//cookie的创建
Cookie cookie = new Cookie("name", "zhangsan");
resp.addCookie(cookie);
//cookie的获取
Cookie[] cookies = req.getCookies();
if (cookies.length > 0 && cookies != null) {
for (Cookie cookie1 : cookies) {
System.out.println(cookie1.getName());
System.out.println(cookie1.getValue());
}
}
}
}
6.2Cookie设置到期时间
除了 Cookie 的名称和内容外,我们还需要关心一个信息,到期时间,到期时间用来指定该 cookie 何时失效。默认为当前浏览器关闭即失效。我们可以手动设定cookie的有效时间(通过到期时间计算),通过setMaxAge(int time);方法设定 cookie 的最大有效时间,以秒为单位。
-
到期时间的取值
负整数
若为负数,表示不存储该 cookie。
cookie 的 maxAge 属性的默认值就是-1,表示只在浏览器内存中存活,一旦关闭浏览器窗口,那么 cookie 就会消失。
正整数
若大于 0的整数,表示存储的秒数。
表示 cookie 对象可存活指定的秒数。当生命大于0时,浏览器会把 Cookie 保存到硬盘上,就算关闭浏览器,就算重启客户端电脑,cookie 也会存活相应的时间。
零
若为 0,表示删除该 cookie。
cookie 生命等于0是一个特殊的值,它表示 cookie 被作废!也就是说,如果原来浏览器已经保存了这个Cookie,那么可以通过 Cookie 的 setMaxAge(0)来删除这个Cookie。无论是在浏览器内存中,还是在客户端硬盘上都会删除这个 Cookie。
到期时间
//到期时间:负整数(默认值-1,表示只在浏览器中存活,关闭浏览器失效)
Cookie cookie1 = new Cookie("name", "zhangs");
cookie1.setMaxAge(-1);
resp.addCookie(cookie1);
//到期时间:正整数(表示存活指定秒数,数据存储在磁盘中)
Cookie cookie2 = new Cookie("name", "lisi");
cookie2.setMaxAge(30);
resp.addCookie(cookie2);
到期时间:零(表示删除cookie)
Cookie cookie3 = new Cookie("name", "wangw");
cookie3.setMaxAge(0);
resp.addCookie(cookie3);
6.3Cookie的路径问题
@WebServlet("/s01")
public class Cookie02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie = new Cookie("name", "lisi");
//当前服务器下任意任意项目的任意资源都可以获取Cookie对象
//设置路径为/
cookie.setPath("/");
//当前项目下的资源可以获取Cookie对象
//不设置path或设置路径为当前站点名
cookie.setPath("/s01");
//指定项目下的资源可以获取Cookie对象
//设置path为指定站点名
cookie.setPath("/s02");
//指定目录下的资源可以获取Cookie对象
//设置path为指定路径
cookie.setPath("/s01/cookie");
}
}
7.HttpSession对象
HttpSession对象是 javax.servlet.http.HttpSession 的实例,该接口并不像 HttpServletRequest 或HttpServletResponse 还存在一个父接口,该接口只是一个纯粹的接口。这因为 session 本身就属于 HTTP 协议的范畴。
对于服务器而言,每一个连接到它的客户端都是一个session,servlet 容器使用此接口创建 HTTP 客户端和HTTP 服务器之间的会话。会话将保留指定的时间段,跨多个连接或来自用户的页面请求。一个会话通常对应于一个用户,该用户可能多次访问一个站点。可以通过此接口查看和操作有关某个会话的信息,比如会话标识符、创建时间和最后一次访问时间。在整个 session 中,最重要的就是属性的操作。
session 无论客户端还是服务器端都可以感知到,若重新打开一个新的浏览器,则无法取得之前设置的session,因为每一个 session 只保存在当前的浏览器当中,并在相关的页面取得。
Session 的作用就是为了标识一次会话,或者说确认一个用户;并且在一次会话(一个用户的多次请求)期间共享数据。我们可以通过 request.getSession()方法,来获取当前会话的 session 对象。
@WebServlet("/s01")
public class Se01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session对象
HttpSession session = req.getSession();
//获取session会话标识符
//JSESSIONID为名的Cookie
String id = session.getId();
//获取session创建时间
long creationTime = session.getCreationTime();
//获取最后一次访问时间
long lastAccessedTime = session.getLastAccessedTime();
//判断是否是新的session对象
boolean aNew = session.isNew();
}
}
Session作用域
通过该对象可以在一个请求中传递数据,作用范围:再一次请求中有效(请求转发跳转时有效)
方法 | 作用 |
---|---|
setAttribute(String,Object) | 设置域对象内容 |
setAttribute(String) | 获取域对象内容 |
removeAttribute(String) | 删除域对象内容 |
Session对象的销毁
1.默认Tomcat-conf-web.xml
<session-config>
<!--默认30min后销毁-->
<session-timeout>30</session-timeout>
</session-config>
2.Session对象的方法
HttpSession session = req.getSession();
//15分钟后销毁
session.setMaxInactiveInterval(15);
8.ServletContext对象
每一个 web 应用都有且仅有一个ServletContext 对象,又称 Application 对象,从名称中可知,该对象是与应用程序相关的。在 WEB 容器启动的时候,会为每一个WEB 应用程序创 建一个对应的 ServletContext 对象。
该对象有两大作用
第一、作为域对象用来共享数据,此时数据在整个应用程序中共享;
第二、该对象中保存了当前应用程序相关信息。例如可以通过getServerinfo()方法获取当前 服务器信息,getRealPath(Stringpath)获取资源的真实路径
获取对象和方法
@WebServlet("/se02")
public class context01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*获取对象*/
//通过request对象获取
ServletContext servletContext1 = req.getServletContext();
//通过session对象获取
ServletContext servletContext2 = req.getSession().getServletContext();
//通过ServletConfig对象获取
ServletContext servletContext3 = getServletConfig().getServletContext();
//直接获取
ServletContext servletContext4 = getServletContext();
/*常用方法*/
//1.获取当前服务器版本信息
String serverInfo = req.getServletContext().getServerInfo();
//2.获取项目真实路径
String realPath = req.getServletContext().getRealPath("/");
}
}
Servlet的三大域对象
- request域对象:在一次请求中有效,请求转发有效,重定向失效
- session域对象:在一次会话中有效,请求转发和重定向都有效,session销毁后失效
- servletContext域对象:在整个应用程序中有效,服务器关闭后失效
方法 | 作用 |
---|---|
setAttribute(String,Object) | 设置域对象内容 |
setAttribute(String) | 获取域对象内容 |
removeAttribute(String) | 删除域对象内容 |
9.文件上传和下载
在上网的时候我们常常遇到文件上传的情况,例如上传头像、上传资料等;当然除了上传,遇见下载的情况也很多,接下来看看我们 servlet 中怎么实现文件的上传和下载。
文件上传涉及到前台页面的编写和后台服务器端代码的编写,前台发送文件,后台接收并保存文件,这才是个完整的文件上传。
前台页面
在做文件上传的时候,会有一个上传文件的界面,首先我们需要一个表单,并且表单的请求方式为 POST;其次我们的 form 表单的 enctype 必须设为"multipart/form-data",即 enctype=“multipart/form-data”,意思是设置表单的类型为文件上传表单。默认情况下这个表单类型是"application/x-www-form-urlencoded",不能用于文件上传。只有使用了multipart/form-data 才能完整地传递文件数据。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--
文件上传
1.准备表单
2.设置表单提交类型为 method="post"
3.设置表单类型为文件上传表单 enctype="multpart/from-data"
4.设置文件提交地址
5.准备表单元素
普通项 type="text"
文件项 type="file"
6.设置表单元素的name属性
-->
<form method="post" enctype="multipart/form-data" action="uploderServlet">
name:<input type="text" name="uname"><br>
file:<input type="text" name="myfile"><br>
<!--button默认是提交类型 type="submit"-->
<button>提交</button>
</form>
</body>
</html>
后台代码
/*
* 文件上传
* 使用注解@MultipartConfig 将一个servlet标识为支持文件上传
* */
@WebServlet("/uploadservlet")
@MultipartConfig
public class UploadServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("文件上传...");
//设置请求编码格式
req.setCharacterEncoding("UTF-8");
//获取普通表单项目
String uname = req.getParameter("uname");
System.out.println("uname " + uname);
//获取上传的文件
//获取Part对象
Part part = req.getPart("myfile");
//通过Part对象得到上传的文件名
String fileName = part.getSubmittedFileName();
System.out.println("上传的文件名 " + fileName);
//得到文件存放的路径
String filePath = req.getServletContext().getRealPath("/");
System.out.println("文件存放路径 " + filePath);
//上传文件到指定目录
part.write(filePath + "/" + fileName);
}
}
下载
文件下载,即将服务器上的资源下载(拷贝)到本地,我们可以通过两种方式下载。第一种是通过超链接本身的特性来下载;第二种是通过代码下载。
当我们在 HTML 或 ISP 页面中使用a标签时,原意是希望能够进行跳转,但当超链接遇到浏览器不识别的资源时会自动下载;当遇见浏览器能够直接显示的资源,浏览器就会默认显示出来,比如txt、png、jpg等。当然我们也可以通过 download 属性规定浏览器进行下载。但有些浏览器并不支持
超链接下载
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--
在Tomcat部署时添加外部源
超链接下载
当使用超链接标签时,如果遇到能够识别的资源会直接显示,如果不能识别则会下载
可以通过download属性下载可以识别的资源,download属性的值为空会使用默认的文件名,设置了值则用设置的值作为文件名
-->
<a href="download/..." download>文本文件</a>
<a href="download/..." download="xx.png">图片文件</a>
<a href="download/...">压缩文件</a>
</body>
</html>
后台代码下载
- 需要通过response.setContentType 方法设置Content-type头字段的值,为浏览器无法使用某种方式或激活某个程序来处理的MIME类型,例如"application/octetstream"或"application/x-msdownload"等
- 需要通过 response.setHeader 方法设置Content-Disposition头的值为"attachment;filename=文件名”
- 读取下载文件,调用response.getOutputStream方法向客户端写入附件内容
@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("文件下载");
//设置编码格式
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
//获取参数(得到要下载的文件名)
String fileName = req.getParameter("fileName");
//参数的非空判断 .trim()去除字符串前后空格
if (fileName == null || "".equals(fileName.trim())) {
resp.getWriter().write("请输入要下载的文件名!");
resp.getWriter().close();
return;
}
//得到路径存放路径
String path = req.getServletContext().getRealPath("/download/");
//通过路径得到file对象
File file = new File(path + fileName);
//判断文件对象是否存在且是一个标准文件
if (file.exists() && file.isFile()) {
//设置响应类型
resp.setContentType("application/octetstream");
//设置响应头
resp.setHeader("Content-Disposition", "attachment;filename=" + fileName);
//得到file输入流
InputStream in = new FileInputStream(file);
//得到字节输出流
ServletOutputStream out = resp.getOutputStream();
//定义byte数组
byte[] bytes = new byte[1024];
//记录数量
int len = 0;
//循环输出
while ((len = in.read(bytes)) != -1) {
out.write(bytes, 0, len);
}
//关闭资源
out.close();
in.close();
} else {
resp.getWriter().write("文件不存在!");
resp.getWriter().close();
}
}
}