java.servlet
HTTP协议
HTTP请求
抓包
请求行
- 请求行
POST /myApp/success.html?username=zs&password=123456 HTTP/1.1
GET /myApp/success.html HTTP/1.1
-
请求方式(8种,put,delete等)
GET:明文传输, 不安全,参数跟在请求路径后面,对请求参数大小有限制,
POST: 暗文传输,安全一些,请求参数在请求体里,对请求参数大小没有有限制,
-
URI:统一资源标识符(即:去掉协议和IP地址部分)
-
协议版本:HTTP/1.1
请求头
请求头,以键值对的形式存在,但存在一个key对应多个值的请求头.
作用:浏览器告诉服务器自己相关的设置
-
Accept:浏览器可接受的MIME类型 ,告诉服务器客户端能接收什么样类型的文件。
-
User-Agent:浏览器信息.(浏览器类型, 浏览器的版本....)
-
Accept-Charset: 浏览器通过这个头告诉服务器,它支持哪种字符集
-
Content-Length:表示请求参数的长度
-
Host:初始URL中的主机和端口
-
Referer:从哪里里来的(之前是哪个资源)----------防盗链
-
Content-Type:内容类型,告诉服务器,浏览器传输数据的MIME类型,文件传输的类型,application/x-www-form-urlencoded .
-
Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip
-
Connection:表示是否需要持久连接。如果服务器看到这里的值为“Keep -Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接 )
-
Cookie:这是最重要的请求头信息之一(会话技术, 后面会有专门的时间来讲的)
-
Date:Date: Mon, 22Aug 2011 01:55:39 GMT请求时间GMT
-
get方式请求
【请求行】
GET /myApp/success.html?username=zs&password=123456 HTTP/1.1
【请求头】
Accept: text/html, application/xhtml+xml, */*
X-HttpWatch-RID: 41723-10011
Referer: http://localhost:8080/myApp/login.html
Accept-Language: zh-Hans-CN,zh-Hans;q=0.5
User-Agent: Mozilla/5.0 (MSIE 9.0; qdesk 2.4.1266.203; Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: localhost:8080
Connection: Keep-Alive
Cookie: Idea-b77ddca6=4bc282fe-febf-4fd1-b6c9-72e9e0f381e8
- post请求
【请求行】
POST /myApp/success.html HTTP/1.1
【请求头】
Accept: text/html, application/xhtml+xml, */*
X-HttpWatch-RID: 37569-10012
Referer: http://localhost:8080/myApp/login.html
Accept-Language: zh-Hans-CN,zh-Hans;q=0.5
User-Agent: Mozilla/5.0 (MSIE 9.0; qdesk 2.4.1266.203; Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: localhost:8080
Content-Length: 27
Connection: Keep-Alive
Cache-Control: no-cache
【请求体】
username=zs&password=123456
请求体
get方式没有请求体,只有post请求才有请求体. 即post请求时,请求参数(提交的数据)所在的位置
HTTP响应
抓包
-
响应部分
【响应行】 HTTP/1.1 200 【响应头】 Accept-Ranges: bytes ETag: W/"143-1557909081579" Last-Modified: Wed, 15 May 2019 08:31:21 GMT Content-Type: text/html Content-Length: 143 Date: Sun, 08 Dec 2019 02:20:04 GMT 【响应体】 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> Success </body> </html>
响应行
HTTP/1.1 200
-
协议/版本
-
响应状态码
200:正常,成功
302:重定向
304:表示客户机缓存的版本是最新的,客户机可以继续使用它,无需到服务器请求. 读取缓存
404:客户端错误(一般是路径写错了,没有这个资源)
500:服务器内部错误
响应头
响应头以key:vaue存在, 可能多个value情况. 服务器指示浏览器去干什么,去配置什么.
-
Location: http://www.it315.org/index.jsp指示新的资源的位置,通常和状态,码302一起使用,完成请求重定向
-
Content-Type: text/html; charset=UTF-8; 设置服务器发送的内容的MIME类型,文件下载时候
-
Refresh: 5;url=http://www.baidu.com 指示客户端刷新频率。单位是秒 eg: 告诉浏览器5s之后跳转到百度
-
Content-Disposition: attachment; filename=a.jpg 指示客户端(浏览器)下载文件
-
Content-Length:80 告诉浏览器正文的长度
-
Server:apachetomcat 服务器的类型
-
Content-Encoding: gzip服务器发送的数据采用的编码类型
-
Set-Cookie:SS=Q0=5Lb_nQ;path=/search服务器端发送的Cookie
-
Cache-Control: no-cache (1.1)
-
Pragma: no-cache (1.0) 表示告诉客户端不要使用缓存
-
Connection:close/Keep-Alive
-
Date:Tue, 11 Jul 2000 18:23:51 GMT
响应体
页面展示内容, 和网页右键查看的源码一样
general
Request URL: http://localhost:8080/Servlet_war_exploded/ 请求地址
Request Method: GET 请求方法
Status Code: 200 状态码
Remote Address: [::1]:8080 远程IP地址
Referrer Policy: strict-origin-when-cross-origin 上一个链接转过来的地址
Response Headers
Connection: keep-alive 客户端与服务器保持连接 Connection: close表示关闭连接
Content-Length: 606 返回的内容长度
Content-Type: text/html;charset=UTF-8 返回的内容类型,这里是html
Date: Mon, 25 Oct 2021 10:24:14 GMT 东八区时间,北京时间-8小时
Keep-Alive: timeout=20
Set-Cookie: JSESSIONID=4B7A9A0CD40520648860788AC48F751F; Path=/Servlet_war_exploded; HttpOnly
Request Headers
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 浏览器能够接收的类型
Accept-Encoding: gzip, deflate, br 浏览器支持的压缩编码类型
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 浏览器支持的语言
Cache-Control: no-cache 通过首部指令操作缓存
Connection: keep-alive 保持连接
Cookie: JSESSIONID=83A0C7616581A88BFC549EAB425072DE; Idea-717fcd95=c68c8a9c-8c64-42f7-9586-99850f2b9b07; Pycharm-f2f043dd=526d43bc-6c5a-4c00-a7c2-8b72be16e308; Idea-e5a52c40=b26af877-cd61-46ac-9d33-af5a2522b4fd 发送请求时把保存在该请求下的所有cookie值一起发给浏览器
Host: localhost:8080 指定请求的服务器的域名和编号
Pragma: no-cache
信息安全 sec-ch-ua: "Google Chrome";v="93", " Not;A Brand";v="99", "Chromium";v="93"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1 升级不安全的请求 http - https
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36 服务器信息
Servlet的实现
部署Tomcat项目
* 部署项目的方式:
1. 直接将项目放到webapps目录下即可。
* /hello:项目的访问路径-->虚拟目录
* 简化部署:将项目打成一个war包,再将war包放置到webapps目录下。
* war包会自动解压缩
2. 配置conf/server.xml文件
在<Host>标签体中配置
<Context docBase="D:\hello" path="/hehe" />
* docBase:项目存放的路径
* path:虚拟目录
3. 在conf\Catalina\localhost创建任意名称的xml文件。在文件中编写
<Context docBase="D:\hello" />
* 虚拟目录:xml文件的名称
servlet的生命周期(重点)
先看与Servlet生命周期有关的三个方法:init(), service(), destroy(). Servlet生命周期可被定义为从创建
直到毁灭的整个过程。以下是三个方法分别对应的Servlet过程:
init():Servlet进行初始化;
service():Servlet处理客户端的请求;
destroy():Servlet结束,释放资源;
在调用destroy()方法后,Servlet由JVM的垃圾回首器进行垃圾回收。
init()方法
Servlet被装载后,Servlet容器创建一个Servlet实例并且调用Servlet的init()方法进行初始化在Servlet生命周期中init()方法只被调用一次。
当用户调用一个Servlet时,Servlet容器就会创建一个Servlet实例,每一个用户请求都会产生一个新的线程,init()方法简单的创建或加载一些数据,这些数据将会被用在Servlet的整个生命周期。
init()方法的定义如下:
public void init() throws ServletException {
// 初始化代码...
}
service()方法
service()方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service()方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。
每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service()方法检查HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用doGet()、doPost()等方法。
service()的定义如下:
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException{
// service()代码...
}
destroy()方法
destroy()方法只会被调用一次,在Servlet生命周期结束时被调用。destroy()方法可以让Servlet关闭数据库连接、停止后台、把cookie列表或点击计数器写入到磁盘,并执行其他类似的清理活动。
在调用destroy()方法之后,Servlet对象被标记为垃圾回收。
destroy()方法的定义如下所示:
public void destroy() {
// 终止化代码...
}
总结:
- 在首次访问某个Servlet时,init()方法会被执行,而且也会执行service()方法。
- 再次访问时,只会执行service()方法,不再执行init()方法。
- 在关闭Web容器时会调用destroy()方法。
创建WEB项目
实现Servlet
配置servlet的两种方式
1.通过web.xml文件配置servlet
<servlet>
<servlet-name>servlet01</servlet-name>
<servlet-class>com.ccl.servlet.servlet01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet01</servlet-name>
<url-pattern>/ser.do</url-pattern>
</servlet-mapping>
2.通过注解配置servlet
servlet的匹配规则
精确匹配
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/user.html</url-pattern>
<url-pattern>/user.do</url-pattern>
</servlet-mapping>
在浏览器中输入如下几种url时,都会被匹配到该servlet
http://localhost:4444/servlet01/user.html
http://localhost:4444/servlet01/user.do
路径匹配 ,以"/"开头,并以"/*"结尾的字符串用于路径匹配
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/user/*</url-pattern>
</servlet-mapping>
路径以/user/开始,后面的路径可以任意。比如下面的url都会被匹配
扩展名匹配,以"*"结尾的字符串用于扩展名匹配
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
则任何扩展名为jsp或action的url请求都会匹配,比如下面的url都会被匹配
缺省匹配
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
以任意字符开头,/结尾该servlet被匹配
匹配顺序
- 精准匹配
- 路径匹配,优先最长路径匹配
- 扩展名匹配
注意:两种配置方法不能同时使用
创建servlet的三种方式
1.实现javax.servlet.Servlet接口,这个接口定义了servlet的生命周期,所有的方法都要实现
@WebServlet("/HelloServlet")
public class UserServlet 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 {
servletResponse.getWriter().print("<h1>hello servlet</h1>");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
2.继承javax.servlet.GenericServlet类
public class GenServlet extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
servletResponse.getWriter().print("<h1>hello GenericServlet</h1>");
}
}
3.继承javax.servlet.http.HttpServlet类,会根据请求的类型进行特殊的调用
public class HServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().print("<h1>hello HttpServlet</h1>");
}
}
请求和响应
RERUEST
概述
request是Servlet.service()方法的一个参数,类型为javax.servlet.http.HttpServletRequest。在客户端发出每个请求时,服务器都会创建一个request对象,并把请求数据封装到request中,然后在调用Servlet.service()方法时传递给service()方法,这说明在service()方法中可以通过request对象来获取请求数据。
request的功能可以分为以下几种:
- 封装了请求头数据;
- 封装了请求正文数据,如果是GET请求,那么就没有正文;
- request是一个域对象,可以把它当成Map来添加获取数据;
- request提供了请求转发和请求包含功能。(以后学习)
操作请求行和请求头方法
方法名 | 描述 |
---|---|
getRequestURL() | 获取客户端发出请求时的完整URL |
getRequestURI() | 获取请求行中的资源名称部分(项目名称开始) |
getQueryString() | 获取请求行中的参数部分 |
getMethod() | 获取客户端请求方式 |
getProtocol() | 获取HTTP版本号 |
getContextPath() | 获取webapp名字 |
getRemoteAddr() | 获取客户机的IP地址(知道是谁请求的) |
getServerPort | 获得服务端的端口 |
// 获取客户端请求的完整URL (从http开始,到?前面结束)
String url = req.getRequestURL().toString();
System.out.println("获取客户端请求的完整URL:" + url);
// 获取客户端请求的部分URL (从站点名开始,到?前面结束)
String uri = req.getRequestURI();
System.out.println("获取客户端请求的部分URL:" + uri);
// 获取请求行中的参数部分 (从?后面开始,到最后)
String queryString = req.getQueryString();
System.out.println("获取请求行中的参数部分:" + queryString);
// 获取客户端的请求方式
String method = req.getMethod();
System.out.println("获取客户端的请求方式:" + method);
// 获取HTTP版本号
String protocol = req.getProtocol();
System.out.println("获取HTTP版本号:" + protocol);
// 获取webapp名字(站点名)
String webapp = req.getContextPath();
System.out.println("获取webapp:" + webapp);
操作请求体方法
方法名 | 描述 |
---|---|
getParameter(name) | 获取指定名称的参数 |
getParamerterValues(String name) | 获取指定名称参数的所有值,返回数组 |
Map<String , String[]> getParamterMap() | 获得所有的请求参数。key为参数名,value为key对应的值 |
setCharcterEncoding("UTF-8") | 针对POST乱码问题的处理方式 |
// 针对POST乱码问题的处理方式
req.setCharacterEncoding("UTF-8");
// 获取指定名称的参数,返回字符串
String uname = req.getParameter("uname");
System.out.println("uname的参数:" + uname);
// 获取指定名称参数的所有参数值,返回数组
String[] hobbys = req.getParameterValues("hobby");
System.out.println("获取指定名称参数的所有参数值:" + Arrays.toString(hobbys));
请求转发
特点:
- 请求路径不会变化
- 请求转发只有1次请求
- 路径写相对路径,不要写绝对路径
- 转发只能转发到当前项目的资源,不能转发到外部资源
- 可以转发到WEB-INF里面的资源
- 请求转发的路径只能写相对路径,不能写绝对路径
- 请求转发地址栏不会变
请求转发表示由 多个Servlet共同来处理一个请求 。例如Servlet1来处理请求,然后Servlet1又转发给Servlet2来继续处理这个请求。
public class AServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("AServlet");
RequestDispatcher rd = request.getRequestDispatcher("/BServlet");
rd.forward(request, response);
}
}
public class BServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("BServlet");
}
}
request域方法
方法名 | 描述 |
---|---|
void setAttribute(String name, Object value) | 用来存储一个对象,也可以称之为存储一个域属性, |
Object getAttribute(String name) | 用来获取request中的数据,当前在获取之前需要先去存储才行例如:String value = (String)request.getAttribute(“xxx”);,获取名为xxx的域属性; |
void removeAttribute(String name) | 用来移除request中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做; |
Enumeration getAttributeNames() | 获取所有域属性的名称; |
一个请求会建一个request对象,如果在一个请求中经历了多个Servlet,那么多个Servlet就可以使用request来共享数据。
域方法通常在进行重定向时使用,多个servlet共享数据。
@WebServlet("/ris")
public class RegionServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("RegionServlet");
req.setAttribute("akey", "aaa");
//一次请求转发
req.getRequestDispatcher("/trs").forward(req, resp);
}
}
@WebServlet(name = "TheRequestServlet", urlPatterns = "/trs")
public class TheRequestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//取值
System.out.println("akey的值:" + req.getAttribute("akey"));
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
}
RESPONSE
概述
response是Servlet.service方法的一个参数,类型为javax.servlet.http.HttpServletResponse。在客户端发出每个请求时,服务器都会创建一个response对象,并传入给Servlet.service()方法。response对象是用来对客户端进行响应的,这说明在service()方法中使用response对象可以完成对客户端的响应工作。
response对象的功能分为以下四种:
- 设置响应头信息;
- 发送状态码;
- 设置响应正文;
- 重定向;
操作响应行
方法名 | 描述 |
---|---|
void setStatus(int sc) | 设置状态码 |
HTTP/1.1 200
常用的状态码:(必须记忆)
200:成功
302:重定向
304:访问缓存
404:客户端错误
500:服务器错误
操作响应头
方法名 | 描述 |
---|---|
void setDateHeader(String name,long date) | |
void setHeader(String name,String value)(重点) | |
void setHeader(String name,int value) | |
void addDateHeader(String name,long date) | |
void addHeader(String name,String value) | |
void addHeader(String name,int value) |
常用的响应头
Refresh:定时跳转 (eg:服务器告诉浏览器5s之后跳转到百度)
Location:重定向地址(eg: 服务器告诉浏览器重定向到xxx)
Content-Disposition: 告诉浏览器下载
Content-Type:设置响应内容的MIME类型(服务器告诉浏览器内容的类型)
响应乱码问题
* 字符流
* getWriter()
* 一定会乱码,应为服务器默认的解析编码时ISO-8869-1,该编码不支持中文
*
* 字节流
* getOutputStream()
* 可能乱码,当前服务器的编码与客户端的编码不一致时,会出现乱码
*
* 解决方案
* 1.设置服务端的编码格式
* 2.设置客户端的编码格式
* 总结:
* 设置客户端和服务端的编码格式保持一致,且支持中文
* 响应json格式的数据时,设置响应类型为application/json
public class Servlet02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//方法一
//同时设置客户端和服务端的编码(推荐)
resp.setContentType("text/html;charset=UTF-8");
//方法二
//设置服务端的编码格式
resp.setCharacterEncoding("UTF-8");
//设置客户端的编码格式
resp.setHeader("content-type","text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("<h2>你好</h2>");
writer.flush();
writer.close();
}
}
定时刷新
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ServletRefresh...");
resp.setHeader("Content-type", "text/html;charset=utf-8");
resp.getWriter().write("<h1>刷新</h1>");
//定时刷新当前页面,循环
// resp.setHeader("Refresh", "3");
//定时刷新指定页面,刷新一次
resp.setHeader("Refresh", "3;https://www.baidu.com");
//定时刷新本地页面
// resp.setHeader("Refresh", "3;trs");
}
重定向
方法名 | 描述 |
---|---|
void sendRedirect(String location) | 重定向 |
- 重定向两次请求
- 重定向的地址栏路径改变
- 重定向的路径写绝对路径(带域名/ip地址的, 如果是同一个项目里面的,域名/ip地址可以省略)
- 重定向的路径可以是项目内部的,也可以是项目以外的(eg:百度)
- 重定向不能重定向到WEB-INF下的资源
- 把数据存到request里面, 重定向不可用
设置状态码重定向
响应码为200表示响应成功,而响应码为302表示重定向。所以完成重定向的第一步就是设置响应码为302。
因为重定向是通知浏览器再第二个请求,所以浏览器需要知道第二个请求的URL,所以完成重定向的第二步是设置Location头,指定第二个请求的URL地址。
public class Servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
///设置状态码重定向
resp.setStatus(302);
resp.setHeader("Location","http://www.baidu.com");
}
}
便捷重定向
public class Servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//便捷重定向
resp.sendRedirect("http://www.baidu.com");
//如果要重定向的URL是在同一个服务器内,那么可以使用相对路径,例如:
resp.sendRedirect("/serspn/ser04");
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 重定向的路径: 相对路径
//resp.sendRedirect("index.html");
// 重定向的路径: 绝对路径
//resp.sendRedirect("http://localhost:8080/day24/index.html");
// 使用绝对路径访问内部项目中的资源可以省略http,ip地址,端口号
//resp.sendRedirect("/day24/index.html");// 绝对路径
// 重定向到WEB-INF下的资源
//resp.sendRedirect("http://localhost:8080/day24/WEB-INF/b.html");// 报错,不可以
}
重定向小结
- 重定向是两次请求,请求转发是一次
- 重定向的URL可以是其他应用,不局限于当前应用;
- 重定向的响应头为302,并且必须要有Location响应头;
- 重定向就不要再使用response.getWriter()或response.getOutputStream()输出数据,不然可能会出现异常;
请求转发与重定向的区别
- 转发是一次请求, 重定向是二次请求
- 转发的路径不会改变,重定向的路径会改变
- 转发只能转发到项目的内部资源,重定向可以重定向到项目的内部资源, 也可以是项目外部资源(eg:百度)
- 转发可以转发到WEB-INF下面的资源, 重定向不可以重定向到WEB-INF下面的资源
- 把数据存到request里面,转发有效, 重定向无效
- 转发的路径写相对的(不带http,不带ip,不带项目名), 重定向的路径写绝对的(带http,带ip,带项目名),重定向到内部项目,可以省略http,ip地址,端口号
操作响应体
方法名 | 描述 |
---|---|
ServletOutputStream getOutputStream() | |
PrintWriter getWriter() |
响应正文
response是响应对象,向客户端输出响应正文(响应体)可以使用response的响应流,repsonse一共提供了两个响应流对象:
- PrintWriter out = response.getWriter():获取字符流,处理字符;
- ServletOutputStream out = response.getOutputStream():获取字节流,处理文件;
在一个请求中,不能同时使用这两个流!也就是说,要么你使用repsonse.getWriter(),要么使用response.getOutputStream(),但不能同时使用这两个流。不然会抛出IllegalStateException异常。
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/**
* 字符输出流
*/
PrintWriter writer = resp.getWriter();
writer.write("<h2>你好</h2>");
writer.flush();
writer.close();
/**
* 字节输出流
*/
ServletOutputStream out = resp.getOutputStream();
out.write("你好".getBytes());
out.write("<h2>你好</h2>".getBytes());
out.flush();
out.close();
}
}
文件下载案例
Cookie对象
Cookie的创建和发送
通过new Cookie("key" , "value");来创建一个Cookie对象,要想将Cookie随响应发送到客户端,需要先添加到response对象中,response.addCookie(cookie);此时该Cookie对象随着响应发送到客户端,在浏览器上可以看见
public class scookie01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建Cookie
Cookie cookie = new Cookie("name","zhangsan");
//发送cookie
resp.addCookie(cookie);
}
}
Cookie的获取
在服务器端只提供了一个getCookie()方法用来获取客户端回传的所有cookie组成的一个数组,如果需要获取单个cookie则需要通过遍历,getName()获取Cookie的名称getValue()获取Cookie值
public class SCookie02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie[] cookies = req.getCookies();
if (cookies != null & cookies.length > 0){
for (Cookie cookie : cookies){
System.out.println(cookie.getName());
System.out.println(cookie.getValue());
}
}
}
}
Cookie设置到期时间
默认当前浏览器关闭即失效。我们可以手动设定cookie的有效时间(通过到期时间计算),通过setMaxAge(int time);方法设定cookie的最大有效时间,以秒为单位
负整数
cookie的maxAge属性的默认值就是-1,表示只在浏览器内存中存活,一旦关闭浏览器窗口,那么cookie就会消失
正整数
表示cookie对象可存活指定的秒数,当生命大于0时,浏览器会把Cookie保存到硬盘上,就算关闭浏览器,就算重启客户端电脑,cookie也会存活相应的时间
零
表示cookie作废,如果原来浏览器已经保存了这个Cookie,那么可以通过setMaxAge(0)来删除这个Cookie
public class SCookie03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie = new Cookie("aa","AAA");
// 负整数,表示浏览器关闭即失效
//cookie.setMaxAge(-1);
// 正整数,表示存活指定秒数
//cookie.setMaxAge(15);
// 零,表示删除cookie
cookie.setMaxAge(0);
//响应cookie
resp.addCookie(cookie);
// 删除已有cookie
Cookie cookie1 = new Cookie("name", null);
cookie1.setMaxAge(0);
resp.addCookie(cookie1);
}
}
Cookie的注意点
输入中文
public class SCookie04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = "姓名";
String value = "李青";
// 通过URLEncoder.encode()进行编码
name = URLEncoder.encode(name);
value = URLEncoder.encode(value);
// 创建Cookie对象
Cookie cookie = new Cookie(name, value);
// 发送Cookie对象
resp.addCookie(cookie);
}
}
同名问题
如果浏览器发送重复的Cookie会覆盖原有的Cookie
浏览器存放Cookie的数量
不同的浏览器对Cookie有限定,Cookie的存储是有上限的。Cookie是存储在客户端(浏览器)的,而且一般是由服务器创建和设定。后期结合Session来实现回话跟踪。
Cookie的路径
public class SCookie06 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//当前项目下的资源可获取Cookie对象(默认不设置Cookie的path)
Cookie cookie = new Cookie("a1", "A1");
resp.addCookie(cookie);
// 当前服务器中,任意资源都可以访问
Cookie cookie1 = new Cookie("a2", "A2");
cookie1.setPath("/");
resp.addCookie(cookie1);
//指定目录下的资源可以获取Cookie对象
Cookie cookie2 = new Cookie("a3", "A3");
cookie2.setPath("/serspn/test/scok02");
resp.addCookie(cookie2);
//指定项目下的资源可获取Cookie对象(指定站点名)
Cookie cookie3 = new Cookie("a4", "A4");
cookie3.setPath("/ser");
resp.addCookie(cookie3);
}
}
HttpSession对 象
基本使用
public class Session01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取Session对象(如果session对象不存在,则新建session对象;如果session存在,则获取session对象)
HttpSession session = req.getSession();
//获取session的标识符
System.out.println(session.getId());
//获取第一次访问时间
System.out.println(session.getCreationTime());
//获取最后一次访问时间
System.out.println(session.getLastAccessedTime());
//是否是新的session对象
System.out.println(session.isNew());
//设置session域对象(一次对话有效)
session.setAttribute("uname","admin");
//获取指定名称的session域对象
String uname = (String) req.getAttribute("uname");
//移除指定名称的session域对象
session.removeAttribute("uname");
}
}
session对象的销毁
默认时间到期
Tomcat中conf目录下的web.xml文件中进行修改
<session-config>
<session-timeout>30</session-timeout>
</session-config>
自己设定到期时间
通过session.setMaxInactiveInterval(int)来设定session的最大活动时间,单位为秒
//获取session对象
HttpSession session = req.getSession();
//设置session的最大活动时间
session.setMaxInactiveInterval(15);
//获取session的最大不活动时间
int time = session.getMaxInactiveInterval();
立刻失效
//销毁session对象
session.invalidate();
关闭浏览器
seesion的底层依赖cookie实现,cookie的有效时间为关闭浏览器,从而seesion在浏览器关闭时也会失效
关闭浏览器
关闭服务器意味着此次会话结束,数据共享结束
ServletContext对象
public class ServletContext extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过request获取对象
javax.servlet.ServletContext servletContext = req.getServletContext();
//通过Session获取
javax.servlet.ServletContext servletContext1 = req.getSession().getServletContext();
//通过ServletConfig对象获取
javax.servlet.ServletContext servletContext2 = getServletConfig().getServletContext();
//直接获取
javax.servlet.ServletContext servletContext3 = getServletContext();
//常用方法
//获取服务器版本信息
String serverInfo = servletContext.getServerInfo();
System.out.println("获取服务器的版本信息:" + serverInfo);
//获取项目的真实路径
String realPath = servletContext.getRealPath("/");
System.out.println("获取项目的真实路径:" + realPath);
}
}
Servlet三大作用域
request域对象
再一次请求中有效,请求转发有效,重定向有效
session域对象
在一次会话中有效,请求转发和重定向都有效,seesion销毁后失效
servletContext域对象
在整个应用程序中有效,服务器关闭后失效
Jsp
jsp基本语法
<% %> Service()方法内操作
request.setAttribute("key",value)
<%= %> Service()方法内输出操作
request.getAttribute("key");
<%! %> class中操作
private int a;
EL语法
作用域:resquest session application(ServletContext)
获取数据
${requestScope.get("AName")} & ${AName} 获取简单数据
${sessionScopr.get("ArrayName")[index]} & ${ArrayName[index]} 获取数组数据
${application.get("ListName").get(index)} & ${ListName[index]} 获取list数据
${map.get(key) & ${map.Key} 获取map数据
${class.getField()} & ${class.filed} 获取Bean数据
运算
${int + int}
判断
${int > int}
JSTL语法
jar包:standard.jar & jstl.jar
核心标签库:<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl -->
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-impl</artifactId>
<version>1.2.5</version>
<scope>runtime</scope>
</dependency>
if标签
<c:if test="${el判断}" [var="给定test结果一个变量名"] [scope="将变量名保存域中 默认page"]>
test=true则在代码块中执行操作
</c:if>
choose标签 (了解)
<c:choose>
<c:when test="el表达式">
test=true则在代码块中执行操作
</c:when> == if(){}
可以有多个when
<c:otherwise>
上面条件都不满足执行此代码块操作
</c:otherwise>== else{}
</c:choose>
forEach标签
<%
ArrayList<String> list = new ArrayList<>();
list.add("leesin");
list.add("zed");
list.add("king");
request.setAttribute("list", list);
%>
<%-- 迭代对象 迭代步数 起始索引 结束索引 当前对象存到page域 当前对象的迭代信息存到page域--%>
<c:forEach items="${list}" step="1" begin="0" end="3" var="e" varStatus="status">
${e}<br/>
当前循环的索引:${status.index}<br/>
当前循环的次数:${status.count}<br/>
当前迭代出来的元素是否是最后一个元素:${status.last}<br/>
当前迭代出来的元素是否是第一个元素:${status.first}<br/>
当前索引:${status.current}<br/>
迭代步长:${status.step}<br/>
迭代终位:${status.end}<br/>
迭代始位:${status.begin}<br/>
<hr/>
</c:forEach>