Servlet入门
1.概念
server applet 运行在服务器端的小程序
-
Servlet就是一个接口规范,定义了Java类被浏览器访问(tomcat所识别)的规则
-
如果我们需要自定义一个类被tomcat所识别 需要实现Servlet接口 重写其抽象方法
2.快速入门
-
创建一个JavaEE项目
-
定义一个类 实现Servlet接口
-
重写其抽象方法
-
配置Servlet
在 web.xml中配置以下信息
<!--将自定义的Servlet类配置到web.xml中以便于 tomcat运行时能够识别该类 ServletDemo01 -->
<servlet>
<servlet-name>demo1</servlet-name> <!--别名 随意取-->
<servlet-class>com.huike.web.servlet.ServletDemo01</servlet-class> <!--自定义类的全限定类名-->
</servlet>
<!--servlet的映射标签 每一个Servlet类都需要有一个映射标签 -->
<servlet-mapping>
<servlet-name>demo1</servlet-name> <!--别名 需要跟上面的一致-->
<url-pattern>/demo1</url-pattern> <!-- 虚拟路径 url中要访问该类应该写的路径-->
</servlet-mapping>
3.执行原理
- 当服务器接收到客户端浏览器的请求后 会解析对应的url路径 获取访问的Servlet的资源路径(虚拟目录/demo1)
- 查找web.xml文件,是否有对应的标签体内容与之一致.
- 如果有, 则再找到对应的的全类名
- tomcat会将字节码文件加载至内存,创建该类对象,调用该类的方法
4.Servlet的生命周期
-
被创建:执行init方法 只执行一次
-
Servlet什么时候被创建? 第一次访问时被创建且只会被创建一次
-
可以通过进行相关配置去修改Servlet创建的时间
<servlet> <servlet-name>demo1</servlet-name> <!--别名 随意取--> <servlet-class>com.huike.servlet.AnnoServlet</servlet-class> <!--自定义类的全限定类名--> <!--默认为-1 负数(默认值)代表第一次访问被创建 0或者正数代表服务器启动时创建--> <load-on-startup>1</load-on-startup> </servlet> <!--servlet的映射标签 每一个Servlet类都需要有一个映射标签 --> <servlet-mapping> <servlet-name>demo1</servlet-name> <!--别名 需要跟上面的一致--> <url-pattern>/demo1</url-pattern> <!-- 虚拟路径 url中要访问该类应该写的路径--> </servlet-mapping>
-
Servlet在内存中只存在一个对象
Servlet的init方法只执行一次 说明一个Servlet在内存中只会存在一个对象 Servlet是单例模式的
-
多个用户同时访问同一个Servlet资源的时候,会存在并发问题(线程安全问题)
解决方案:尽量不要在service方法中访问成员变量 不要修改成员变量的值
-
-
提供服务:执行service方法,执行多次
每次访问Servlet时,都会执行一次
-
被销毁:执行destory方法,只执行一次
- Servlet被销毁时执行,服务器关闭时,Servlet会被销毁
- 只有服务器正常关闭时才会执行该方法
- destory方法在Servlet销毁前执行 一般用于资源的释放
5.Servlet的优化
-
Servlet3.0及其以上版本 和 Servlet3.0以下版本的区别
- 以上版本 支持注解配置的,不需要配置文件了
-
Servlet3.0以上版本使用注解创建项目的步骤
1.创建JavaEE项目 选择3.0版本以上 不需要再创建web.xml文件了
2.定义一个类 实现Servlet接口 实现其方法
3.在类上使用@WebServlet注解即可
-
注解的优化
<servlet>
<servlet-name>demo1</servlet-name> <!--别名 随意取-->
<servlet-class>com.huike.web.servlet.ServletDemo01</servlet-class> <!--自定义类的全限定类名-->
<load-on-startup>5</load-on-startup> <!-- 修改Servlet创建的时间 -->
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name> <!--别名 需要跟上面的一致-->
<url-pattern>/demo1</url-pattern> <!-- 虚拟路径 url中药访问该类应该写的路径-->
</servlet-mapping>
<!-- @WebServlet注解: -->
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
String name() default ""; //相当于<servlet-name>标签
String[] value() default {}; //相当于<url-pattern>标签 String[] 注意是个数组
//两个作用差不多
String[] urlPatterns() default {}; //相当于<url-pattern>标签 String[] 注意是个数组
int loadOnStartup() default -1; //相当于<load-on-startup>标签
WebInitParam[] initParams() default {};
boolean asyncSupported() default false;
String smallIcon() default "";
String largeIcon() default "";
String description() default "";
String displayName() default "";
}
//元注解(描述注解的注解(注解前面的注解))复习:
@Target:描述注解能够作用的位置
ElementType取值:
TYPE:可以作用于类上
FIELD:可以作用于成员变量上
METHOD:可以作用于方法上
@Retention:描述注解被保留的阶段
SOURCE: 源代码阶段, 被编译器忽略
CLASS: 注解将会被保留在Class文件中,但在运行时并不会被JVM保留。这是默认行为,所有没有用Retention注解的注解,都会采用这种策略。
RUNTIME:保留至运行时。所以我们可以通过反射去获取注解信息。
@Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
@Documented:描述注解是否被抽取到api文档中
@Inherited:描述注解是否被子类继承
注解默认只有一个属性,且为value则可以省略
//以下三种相同
@WebServlet("/annoServlet")
@WebServlet(urlPatterns = "/annoServlet")
@WebServlet(value = "/annoServlet")
//因为 urlPatterns和value是个数组,所以可以
@WebServlet("/annoServlet1","/annoServlet2","/annoServlet3")
//Servlet访问路径
@WebServlet("/annoServlet") //1.
@WebServlet("/annoServlet/a") //2.
@WebServlet("/annoServlet/*") //3.通配符,路径为/annoServlet/XXX 什么都可以访问
@WebServlet("/*") //3.通配符,/XXX 什么都可以访问
@WebServlet("*.d") //4.后缀名匹配 /XXX.d
6.IDEA和tomcat的相关配置信息的底层原理
-
Q:IDEA会将项目部署到tomcat当中,到底采用的是哪一种方式呢?
答:IDEA会为tomcat部署的每一个项目单独建一份配置文件,文件所在的目录就是控制台上 log输出的"CATALINA_BASE"目录
-
Q:工作空间的项目 和 tomcat中部署的项目 是不是同一个项目呢?
答:不是同一个,tomcat真正访问的是"tomcat中部署的项目"
-
Q:工作空间的项目 和 tomcat中部署的项目 有什么区别呢?
答:“tomcat中部署的项目” 其实就是 "工作空间的项目"中的web目录下的所有资源
-
注意事项: WEB-INF目录下的资源是不能被浏览器所访问的
7.Servlet的体系结构
体系结构:
Servlet:接口
|实现类
GenericServlet:抽象类 不怎么常用
|子类
HttpServlet:抽象类 (最常用 重点掌握)
|
自定义servlet
servlet常用方法:
void init(ServletConfig config):初始化
void service(ServletRequest request,ServletResponse response):服务 处理业务逻辑
void destroy():销毁
ServletConfig getServletConfig():获取当前servlet的配置对象
GenericServlet常用方法:
将Servlet接口中其他的方法做了默认的空实现,只将service()方法作为抽象
空参的Init() 若我们自己想对servlet进行初始化操作,重写这个init()方法即可
将来定义Servlet类的时候,只需要继承GenericServlet 重写其中的service()方法即可
HttpServlet常用方法:对HTTP协议的一种封装 可以简化我们的操作
1. 定义一个类继承HttpServlet
2. 重写其中的doGet()/doPost()方法
service做了实现,把参数强转,调用了重载的service方法
重载的service方法获取请求的方式,根据请求方式的不同调用相应doXxx()方法
doGet和doPost方法
HTTP
1.概念:超文本传输协议
- 传输协议:客户端和服务器端通信时发送数据的格式
- 特点:
- 基于TCP/IP的高级协议
- 默认端口号:80
- 基于请求/响应模型的 发送一次请求,对应一次响应(一对一)
- 无状态的:每一次请求和响应之间互相独立的,不干扰的,所以无法进行多次请求之间的数据交互
- 发展史(历史版本):
- 1.0:每一次请求和响应都会建立一个新的连接
- 1.1:进行连接的复用 (类似于线程和线程池的关系)
2. 请求数据的数据格式 request
1.请求行
-
格式: 请求方式 请求url 请求协议/版本
- GET /login.html HTTP/1.1
-
请求方式:
-
HTTP协议中共有7种请求方式,常用的有两种
GET: 无请求体
-
请求参数在请求行中,在url的后面 多个请求参数以&连接
username=yzc123&password=123456
-
请求的url长度是有限制的
-
不太安全的
POST:有请求体
- 请求参数在请求体中
- 请求的url长度是没有限制的
- 相对比较安全
-
-
2.请求头
客户端浏览器告诉服务器一些信息
-
格式: 属性名:属性值
-
包含若干个属性,服务端据此获取客户端的信息。与缓存相关的规则信息,均包含在header中
-
常见的请求头:
- User-Agent:浏览器要告诉服务器,我访问你使用的浏览器相关的版本信息
作用: 可以在服务器的代码中通过request对象获取该头的信息,解决浏览器之间的兼容性问题
- Referer:http://localhost/05_12_servlet/login.html A页面跳转到B页面 会发送一个请求 代表A页面的url地址
含义:告诉服务器,该请求是从哪个页面跳转过来的
作用:
1. 统计数据 在百度|新浪|谷歌投放广告 我们可以通过Referer去判断该用户是从哪个地方跳转过来的,从而比较其性价比 2. 防盗链:防止盗取链接操作
3.请求空行
- 本质上就是一个空行 主要是用于分割POST请求的请求头和请求体的
4.请求体(正文)
- 封装了POST请求消息的参数的
- 将一个页面表单中的组件值通过param1=value1¶m2=value2的键值对形式编码成一个格式化串,它承载多个请求参数的数据。
request
1.request对象和response对象的执行原理
-
request对象和response对象都是由服务器创建的
-
我们可以用request对象获取请求数据信息, 用response对象设置响应数据信息
2.request对象继承体系结构
ServletRequest --接口
| 继承
HttpServletRequest – 接口
| 实现
org.apache.catalina.connector.RequestFacade 类(tomcat提供的)
3.request功能
1.获取请求消息数据
1.获取请求行数据
-
GET /login.html HTTP/1.1
-
方法:
- 获取请求方式 GET
String getMethod()
- 获取虚拟目录: /05_12_servlet 重要
String getContextPath()
- 获取Servlet路径: /servletDemo01
String getServletPath()
- 获取get方式的请求参数: username=123
String getQueryString()
- 获取请求URI: /05_12_servlet/servletDemo01
String getRequestURI() : /05_12_servlet/servletDemo01
StringBuffer getRequestURL() : http://localhost/05_12_servlet/servletDemo01
- URI:统一资源标识符
- URL:统一资源定位符
- 获取协议及其版本号: HTTP/1.1
String getProtocol()
- 获取客户机的IP地址: 如果获取本机 ipv6的地址
String getRemoteAddr()
/**
* 演示 Request对象获取请求行信息
*/
@WebServlet("/servletDemo02")
public class ServletDemo02 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取请求方式: GET
String method = request.getMethod();
System.out.println(method);
//2.获取虚拟目录: /05_12_servlet
String contextPath = request.getContextPath();
System.out.println(contextPath);
//3.获取Servlet路径: /servletDemo02
String servletPath = request.getServletPath();
System.out.println(servletPath);
//4.获取get方式的请求参数: username=123
String queryString = request.getQueryString();
System.out.println(queryString);
//5.获取请求URI: /05_12_servlet/servletDemo01
String requestURI = request.getRequestURI();
System.out.println(requestURI);
StringBuffer requestURL = request.getRequestURL();
System.out.println(requestURL);
//6.获取协议及其版本号: HTTP/1.1
String protocol = request.getProtocol();
System.out.println(protocol);
//7.获取客户机的IP地址: 如果获取本机 ipv6的地址
String remoteAddr = request.getRemoteAddr();
System.out.println(remoteAddr);
}
}
运行servletDemo02结果:
GET
/05_12_http
/servletDemo02
null //请求参数为空
/05_12_http/servletDemo02
http://localhost:8080/05_12_http/servletDemo02
HTTP/1.1
0:0:0:0:0:0:0:1
新建html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<form action="/05_12_http/servletDemo02" method="get">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" value="提交">
</form>
</body>
</html>
运行login.html结果:
GET
/05_12_http
/servletDemo02
username=yzc123&password=123456
/05_12_http/servletDemo02
http://localhost:8080/05_12_http/servletDemo02
HTTP/1.1
0:0:0:0:0:0:0:1
2.获取请求头数据
-
方法:
-
String getHeader(String name):通过请求头的名称获取请求头的值
User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。
一些网站常常通过判断 UA 来给不同的操作系统、不同的浏览器发送不同的页面
//判断使用的是哪个浏览器 @WebServlet("/requestDemo01") public class RequestDemo01 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String header = request.getHeader("user-agent"); if (header.contains("Chrome")){ //使用的谷歌浏览器 System.out.println("你使用的是谷歌浏览器"); }else if(header.contains("Firefox")){ System.out.println("你使用的是火狐浏览器"); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
//防盗链 @WebServlet("/requestDemo02") public class RequestDemo02 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String header = request.getHeader("referer"); if (header.contains("jump.html")){ //如果是正常访问,http://localhost/05_13/login.html response.setContentType("text/html;charset=utf-8"); response.getWriter().write("播放电影....."); }else{ //盗链 response.setContentType("text/html;charset=utf-8"); response.getWriter().write("想看电影吗,来爱奇艺充钱看吧....."); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
<!-- daolian.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>跳转</title> </head> <body> <form action="/05_13/requestDemo02" method="post"> <input type="text" name="username1"> <input type="submit" value="跳转"> </form> </body> </html>
<!-- login.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册</title> </head> <body> <form action="/05_13/requestDemo02" method="post"> <input type="text" name="username"> <input type="text" name="password"> <input type="submit" value="跳转"> </form> </body> </html>
-
Enumeration getHeaderNames(): 获取所有的请求头名称 返回值相当于一个iterator迭代器
Enumeration<String> headerNames = request.getHeaderNames(); //遍历 while(headerNames.hasMoreElements()){ //获取响应头名称 String name = headerNames.nextElement(); String value = request.getHeader(name); System.out.println(name+"----------->"+value); }
-
3.获取请求体数据 POST请求的请求体
-
请求体:只有POST请求才会有请求体 在请求体中封装了 POST请求的请求参数
-
步骤:
1.获取流对象
1.BufferedReader getReader():获取字符输入流 只能操作字符数据
<!-- login.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册</title> </head> <body> <form action="/05_13/requestDemo04" method="post"> <input type="text" name="username"> <input type="text" name="password"> <input type="submit" value="跳转"> </form> </body> </html>
//http://localhost:8080/05_13/login.html BufferedReader reader = request.getReader(); String line = null; while((line = reader.readLine())!=null){ System.out.println(line); }
2.ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据
2.从流对象中获取数据
2.其他功能
- 获取请求参数的通用方式 不管是POST还是GET都可使用
需求 | 方法 | 备注 |
---|---|---|
根据参数名称获取参数对应的值 | String getParameter(String name) | username–>zhangsan |
获取特定参数所对应的值的数组 | String[] getParameterValues(String name) | 通常用于处理复选框 |
获取所有请求的参数名称 | Enumeration getParameterNames() | |
获取所有参数的map集合 | Map<String,String[]> getParameterMap() |
(hobby=study&&haobby=swimming)
获取参数中文乱码问题
设置流的编码格式即可: request.setCharacterEncoding(“utf-8”);
@WebServlet("/requestDemo05")
public class RequestDemo05 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//解决中文乱码
request.setCharacterEncoding("utf-8");
// 1. String getParameter(String name):根据参数名称获取参数对应的值 username-->zhangsan
String username = request.getParameter("username");
System.out.println("username:"+username);
System.out.println("---------------------------------------->");
//2. String[] getParameterValues(String name):根据参数名称获取参数对应的值的数组 (hobby=study&&haobby=swimming)
String[] hobbies = request.getParameterValues("hobby");
System.out.println("hobby:"+ Arrays.toString(hobbies));
System.out.println("---------------------------------------->");
//3. Enumeration<String> getParameterNames():获取所有请求的参数名称
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()){
String name = parameterNames.nextElement();
String value = request.getParameter(name);
System.out.println(name+":"+value);
}
System.out.println("---------------------------------------->");
//4.Map<String,String[]> getParameterMap():获取所有参数的map集合
Map<String, String[]> parameterMap = request.getParameterMap();
Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();
for (Map.Entry<String, String[]> entry : entries) {
String key = entry.getKey();
String[] value = entry.getValue();
System.out.println(key+":"+Arrays.toString(value));
}
System.out.println("---------------------------------------->");
Set<String> keys = parameterMap.keySet();
for (String key:keys) {
System.out.println(key+":"+parameterMap.get(key)[0]);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
<!-- register.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<form action="/05_13/requestDemo05" method="get">
姓名: <input type="text" name="username"> <br>
密码: <input type="text" name="password"> <br>
<input type="checkbox" name="hobby" value="game" >游戏
<input type="checkbox" name="hobby" value="sleep" >睡觉
<input type="checkbox" name="hobby" value="eat" >吃饭 <br>
<input type="submit" value="跳转">
</form>
</body>
</html>
-
请求转发 forward:一种在服务器内部的资源跳转方式
- 后续会讲解一个 重定向 redirect
- 面试题: redirect和forward区别
1.步骤:
- 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
- 使用RequestDispatcher对象进行转发:void forward(ServletRequest request, ServletResponse response)
request.getRequestDispatcher("/DemoServlet").forward(request, response);
2.特点:
- 浏览器地址栏的路径不会发生任何改变
- 只能转发到当前服务器内部资源
- 转发是一次请求
-
数据共享
-
域对象:一个有作用范围的对象,可以在范围内共享数据
-
request域:代表一次请求的范围 一般用于请求转发的多个资源共享数据
- 方法:
-
void setAttribute(String name, Object o) :存储数据
-
Object getAttribute(String name) :获取数据
-
void removeAttribute(String name) :移除数据
-
@WebServlet("/requestDemo06")
public class RequestDemo06 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("requestDemo06..............");
//1.存储数据
request.setAttribute("msg","今天你真帅");
request.setAttribute("msg02","今天你真漂亮");
//3.删除数据
request.removeAttribute("msg");
//1. 参数为要跳转的资源路径
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/requestDemo07");
//不能转发到其他服务器资源
// RequestDispatcher requestDispatcher = request.getRequestDispatcher("http://wwww.baidu.com");
//2.
requestDispatcher.forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
//获取存储中的数据
@WebServlet("/requestDemo07")
public class RequestDemo07 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("requestDemo07..............");
//2.获取6中request存储的数据
String msg = (String) request.getAttribute("msg");
String msg02 = (String) request.getAttribute("msg02");
System.out.println("msg:"+msg);
System.out.println("msg02:"+msg02);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
response
响应消息:服务器端发送给客户端的消息
-
数据格式:
1.响应行:
-
组成:协议/版本 响应状态码 状态码的描述 HTTP/1.1 200 ok
-
响应状态码:服务器告诉客户端浏览器 本次请求和响应的一个状态
设置响应状态码:setStatus(int sc)
response.setStatus(302);
-
特点: 都是三位数字(1xx~5xx)
-
分类:
-
1xx:服务器接收客户端消息 但是服务器没有将消息接收完成. 等待一段时间后 服务器会发送1xx状态码
-
2xx:代表请求已成功被服务器接收、理解、并接受。 成功. 代表 200
-
3xx:重定向. 代表:302(重定向) 304(访问缓存)
-
4xx:客户端错误
响应状态码 意思 404 客户端路径错误 请求路径没有对应的资源 405 请求方式没有对应的doXxx方法 -
5xx:服务器端错误
代表:500(服务器内部出现异常) 一般页面出现500,该页面也会显示出现500错误的原因
-
2.响应头:
-
格式: 头名称:值
-
常见的响应头:
-
Content-Type:服务器告诉客户端本次响应体数据格式以及编码的格式
-
Content-disposition:服务器告诉客户端以什么格式打开响应数据
in-line(默认值):在当前页面打开
attachment:filename=xxx 以附件形式打开响应体 下载链接
设置响应头:setHeader(String name,String value);
功能 设置响应头 一秒刷新页面一次 response.setHeader(“refresh”,“1”); 二秒跳到其他页面 response.setHeader(“refresh”,“2;URL=otherPagename”); 没有缓存 response.setHeader(“Pragma”, “No-cache”);
response.setHeader(“Cache-Control”, “no-cache”);设置过期的时间期限 response.setDateHeader(“Expires”, System.currentTimeMillis()+自己设置的时间期限); 访问别的页面 response.setStatus(302); response.setHeader(“location”,“url”); -
3.响应空行
4.响应体: 传输的数据
-
HTTP/1.1 200 ok --->响应行
//下面是响应头
Accept-Ranges: bytes
ETag: W/"420-1589453109675"
Last-Modified: Thu, 14 May 2020 10:45:09 GMT
Content-Type: text/html
Content-Length: 420
Date: Thu, 14 May 2020 12:05:57 GMT
--->响应空行
//下面是响应体
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/05_17_response/responseDemo02" method="post">
<input type="text">
<input type="submit" value="提交">
</form>
</body>
</html>
设置响应体:
-
步骤:
-
获取输出流:
字符输出流:PrintWriter getWriter()
字节输出流:ServletOutputStream getOutputStream()
-
使用输出流
将数据输出到客户端浏览器:void write(String s)
-
-
JSP实质上会转换成一个java文件(Servlet) 里面的html元素 都是通过输出流去写进去的(原理和我们使用response设置响应体一致)
案例
1. 完成重定向
-
重定向:资源跳转的方式
-
步骤:
1.设置状态码302 response.setStatus(302);
2.设置响应头location response.setHeader(“location”,"/05_17_response/responseDemo02"); -
简单的重定向的方式:
动态获取虚拟目录
String contextPath = request.getContextPath();
//参数为要跳转的页面路径response.sendRedirect(contextPath+"/responseDemo02");
response.sendRedirect("http://www.4399.com");//可以访问其他服务器中的网站
-
Q:如果在Demo01通过request存储了一个数据,请问重定向后能不能在Demmo02中获取?
A:不行,request存储的数据的有效范围为一个请求中的多个资源 重定向为两个请求
-
redirect 和 forward的区别 (重要)
- 重定向的特点:
- 重定向地址栏路径发生了变化
- 重定向可以访问其他站点(服务器)的资源
- 重定向是两次请求,不可以使用request对象来共享数据
- 转发的特点:
- 转发地址栏路径不变
- 转发只能访问当前服务器的资源
- 转发是一次请求,可以使用request对象来共享数据
- 重定向的特点:
-
路径的写法
路径分类:
判断路径为相对或者绝对路径的方法:看是以/开头还是.开头
/(绝对) .(相对)
-
相对路径:通过相对路径不可以确定唯一资源
-
如:./index.jsp 不以/开头 以.开头
-
如何确定相对路径的准确性
1.找到当前资源和目标资源之间的相对位置关系
http://localhost/05_17_response/hello.html http://localhost/05_17_response/responseDemo02 http://localhost/05_17_response/html/haha.html http://localhost/05_17_response/responseDemo01
2.确认相对目录
- ./:当前目录
- …/上一级目录
-
-
绝对路径:通过绝对路径可以确定唯一资源
http://localhost/05_17_response/responseDemo02 /05_17_response/responseDemo02
都是绝对路径
-
如何判断路径是否需要携带虚拟目录:
判断路径给谁用的?
-
给客户端浏览器用:需要加虚拟目录的(项目访问路径)
建议虚拟目录动态获取:request.getContextPath();
例如: 重定向…都需要携带虚拟目录
-
给服务器自身使用:不需要加虚拟目录
转发路径不需要携带虚拟目录
-
-
2.服务器输出字符数据到浏览器中
步骤:
- 获取输出流:
- 字符输出流:PrintWriter getWriter()
- 字节输出流:ServletOutputStream
- 使用输出流,将数据输出到客户端浏览器
- void write(String s)
注意:
-
乱码问题:
-
方案1:
-
获取流对象之前,设置流的默认编码 将默认的ISO-8859-1 设置成GBK
response.setCharacterEncoding("GBK");
-
获取输出流:
-
使用输出流,将数据输出到客户端浏览器
-
-
方案2:
-
告诉浏览器,服务器发送的消息体数据的编码, 建议浏览器使用该编码
response.setHeader("content-type","text/html;charset=utf-8");
-
获取输出流:
-
使用输出流,将数据输出到客户端浏览器
-
-
方案3: 推荐使用
-
告诉浏览器,服务器发送的消息体数据的编码, 建议浏览器使用该编码(简写形式)
response.setContentType("text/html;charset=utf-8");
-
获取输出流:
-
使用输出流,将数据输出到客户端浏览器
-
-
3.解决文件名中文乱码问题
//解决中文乱码问题
String agent = request.getHeader("user-agent");
filename = DownLoadUtils.getFileName(agent,filename);
// 3.2:content-disposition:attachment;filename=xxx
response.setHeader("content-disposition","attachment;filename="+filename);
public class DownLoadUtils {
public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏览器
Base64.Encoder base64Encoder = Base64.getEncoder();
// filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
filename = "=?utf-8?B?"+base64Encoder.encodeToString(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}
ServletContext对象
1.概念
代表整个web应用,可以和程序的容器(服务器)进行通信
2.获取
-
通过request对象获取
ServletContext getServletContext()
获取上次调度此ServletRequest的servlet上下文。
-
通过HttpeServlet对象获取
this.getServletContext();
- 结论:无论是哪一种方式获取的ServletContext对象 都为同一个对象
//1.
ServletContext servletContext01 = request.getServletContext();
//2.
ServletContext servletContext02 = this.getServletContext();
System.out.println(servletContext01); //org.apache.catalina.core.ApplicationContextFacade@35107d21
System.out.println(servletContext02); // org.apache.catalina.core.ApplicationContextFacade@35107d21
System.out.println(servletContext01 == servletContext02); // true
3.功能
1. 获取MIME类型
-
MIME类型:在互联网通信过程中定义的一种文件数据类型:
格式: 大类型/小类型 text/html image/png
-
获取
String getMimeType(String file)
String mimeType = servletContext.getMimeType(filename);//获取文件的mime类型
response.setHeader("content-type",mimeType);
2.域对象
共享数据(作用范围?)
-
void setAttribute(String name, Object object)
-
Object getAttribute(String name)
-
void removeAttribute(String name)
- ServletContext的作用范围: 所有用户所有请求的数据
3. 获取文件的真实(服务器)路径
String getRealPath(String path)
- 分别在src目录下创建一个a.txt文件
- web目录下创建一个b.txt文件
- WEB-INF目录下创建一个c.txt文件 如何获取其真实路径
ServletContext context = this.getServletContext();
String b = context.getRealPath("/b.txt");//web目录下的资源访问
System.out.println(b);
//C:\Users\zc980807\IdeaProjects\huike\tomcat\out\artifacts\05_17_servlet_war_exploded\b.txt
String c = context.getRealPath("/WEB-INF/c.txt");//WEB-INF目录下的资源访问
System.out.println(c);
//C:\Users\zc980807\IdeaProjects\huike\tomcat\out\artifacts\05_17_servlet_war_exploded\WEB-INF\c.txt
String a = context.getRealPath("/WEB-INF/classes/a.txt");//src目录下的资源访问
System.out.println(a);
//C:\Users\zc980807\IdeaProjects\huike\tomcat\out\artifacts\05_17_servlet_war_exploded\WEB-INF\classes\a.txt
重点记忆\05_17_servlet_war_exploded\后的目录结构