03Response&Request
1、Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。
2、request和response对象即然代表请求和响应,那我们要获取客户机提交过来的数据,只需要找request对象就行了。要向客户机输出数据,只需要找response对象就行了。
一、response
ServletResponse -- 通用的response提供了一个响应应该具有最基本的属性和方法(接口)
|-HttpServletResponse -- 在ServletResponse的基础上针对于HTTP协议增加了很多强化的属性和方法
Location: http://www.it315.org/index.jsp 配合302实现请求重定向
Server:apache tomcat 服务器的基本信息
Content-Encoding: gzip 服务器发送数据时使用的压缩格式
Content-Length: 80 发送数据的大小
Content-Language: zh-cn 发送的数据使用的语言环境
Content-Type: text/html; charset=GB2312 当前所发送的数据的基本信息,(数据的类型,所使用的编码)
Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT 缓存相关的头
Refresh: 1;url=http://www.it315.org 通知浏览器进行定时刷新,此值可以是一个数字指定多长时间
以后刷新当前页面,
这个数字之后也可以接一个分号后跟一个URL地址指定多长时间后刷新到哪个URLContent-Disposition: attachment;filename=aaa.zip 与下载相关的头
Transfer-Encoding: chunked 传输类型,如果是此值是一个chunked说明当前的数据是一块一块传输的
Set-Cookie:SS=Q0=5Lb_nQ; path=/search 和cookie相关的头,后面课程单讲
ETag: W/"83794-1208174400000" 和缓存机制相关的头
Expires: -1 通知浏览器是否缓存当前资源 ,如果这个头的值是一个以毫秒为单位值就是通知浏览器
缓存资源到指定的时间点
,如果值是0或-1则是通知浏览器禁止缓存Cache-Control: no-cache 通知浏览器是否缓存资源:缓存相关的头,如果为no-cache则通知浏览器不缓存;
Pragma: no-cache 通知浏览器是否缓存资源:缓存相关的头,如果为no-cache则不缓存
以上三个头都是用来控制缓存的,是因为历史原因造成的,不同的浏览器认识不同的头,
我们通常三个一起使用保证通用性。
Connection: close/Keep-Alive 是否保持连接
Date: Tue, 11 Jul 2000 18:23:51 GMT 当前时间
HttpServletResponse对象服务器的响应。而HTTP协议规定一个HTTP响分为状态行、响应头、实体内容三个部分。于是对象中封装了向客户端发送响应状态码、响应头、实体数据的方法。(课后翻看api)。
(一).输出数据
response.setContentTye("text/html;charset=utf-8");
response.getOutputStream().write("".getBytes("utf-8"));
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("");
@WebServlet("/OutServlet")
public class OutServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//字节流;编码,getBytes()使用平台的默认字符集GBK;使用UTF-8会乱码(编码和解码不一致)
//IE默认GB2312上述语句会乱码,怎么解决乱码;Chrome无乱码
//HTTP的响应头:Content-Type: text/html; charset=GB2312
response.setHeader("Content-Type", "text/html; charset=UTF-8");
response.getOutputStream().write("你好, getOutputStream".getBytes("UTF-8"));
//最常用的是字符流,有乱码,是两个“??”;
//服务器查询ISO8859-1码表,找不到中文,返回?
//response.setCharacterEncoding("GBK");
//只使用下面第一句时候,使用UTF-8时,仍会出现乱码;明确用U8解析,可以查到,但浏览器用的是GBK
//用下面两句,第一句可以省写 原来
//response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Type", "text/html; charset=UTF-8");
response.getWriter().write("你好, getWriter");
//下面第二句这一句可以解决输出时候的所有乱码,但程序员最好协商第一句 最好
response.setCharacterEncoding("UTF-8");//给服务器设置
response.setContentType("text/html; charset=UTF-8");//给浏览器设置
response.getWriter().write("你好, getWriter");
}
(二).实现下载
Content-Disposition: attachment;filename=aaa.zip 与下载相关的头
@WebServlet("/DownloadServlet")
public class DownloadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//直接显示出图片,若想下载,使用下列响应头,提示下载
//Content-Disposition: attachment;filename=aaa.zip
//如果图片名字是中文,下载的名字有问题或不显示,所以只能包含ISO8859-1的字符
//若想正确显示中文名字如下解决,只识UTF-8(IE);
//如果不进行编码则文件名显示错误并且不可下载。
//利用URL编码方式(百分号编码)将ASCII码中有的字符表示的形式
response.setHeader("Content-Disposition", "attachment;filename="
+ URLEncoder.encode("SD娃娃.jpg", "UTF-8"));
//输入流,字节流;在传入的路径前拼接当前web应用的硬盘路径,1.jpg在WebRoot下
InputStream input = new FileInputStream(this
.getServletContext().getRealPath("1.jpg"));
//输出流
OutputStream output = response.getOutputStream();
byte[] bys = new byte[1024];
int i = 0;
while ((i = input.read(bys)) != -1) {
output.write(bys, 0, i);
}
input.close();
//output.close();//从response拿出的流不需要自己关闭
}
String s1 = "中国";
String s2 = URLEncoder.encode( s1, "UTF-8");
System.out.println(s2);//编码:%E4%B8%AD%E5%9B%BD
String s3 = URLDecoder.decode( s2, "UTF-8");
System.out.println(s3);//解码:中
(三).定时刷新页面
Refresh: 1;url=http://www.it315.org 通知浏览器进行定时刷新,此值可以是一个数字指定多长时间
以后刷新当前页面,这个数字之后也可以接一个分号后跟一个URL地址指定多长时间后刷新到哪个URL
@WebServlet("/RefreshServlet")
public class RefreshServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
//response.getWriter().write(new Date().toLocaleString());
//设置时间自动加3秒动
//response.setHeader("Refresh", "3");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("恭喜你注册成功!3秒后回到主页......");
response.setHeader("Refresh", "3;url='/Day04/index.jsp'");
//http://localhost/Day04/RefreshServlet,3秒后回到,http://localhost/Day04/index.jsp
}
<!DOCTYPE html>
<html>
<head>
<title>registOK.html</title>
<meta http-equiv= "Refresh" content="3;url=/Day04/index.jsp">
</head>
<body>
恭喜你注册成功!3秒后回到主页....... <br>
</body>
</html>
(四).控制是否缓存资源
利用response设置expires响应头为0或-1浏览器就不会缓存当前资源。(同样功能的头还有Cache-Control: no-cache、Pragma: no-cache)
控制不缓存
@WebServlet("/NoCacheServlet")
public class NoCacheServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setIntHeader("Expires", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
// 若把里面的“检查存储的页面的较新版本”设置为“从不”,
//每次获得时间不变,是第一次缓存时间。若加上上面三
//个响应头控制浏览器不要缓存,就可以每次得到新的时间
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("当前时间是:" + new Date().toLocaleString());
}
控制缓存
@WebServlet("/CacheServlet")
public class CacheServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//从1970.1.1 00:00:00到想表示的时间,显示异常的原因(超过了long的范围)
response.setDateHeader("Expires", System.currentTimeMillis()+1000L*3600*24*30);//缓存保存一个月
// 若把里面的“检查存储的页面的较新版本”设置为“每次访问网页时”,每次重新得到图片
//输入流,字节流;在传入的路径前拼接当前web应用的硬盘路径,1.jpg在WebRoot下
InputStream input = new FileInputStream(this
.getServletContext().getRealPath("1.jpg"));
//输出流
OutputStream output = response.getOutputStream();
byte[] bys = new byte[1024];
int i = 0;
while ((i = input.read(bys)) != -1) {
output.write(bys, 0, i);
}
input.close();
//output.close();
}
(五).请求重定向:地址栏地址发生改变
1、利用response设置状态码为302,并设置响应头Location为要重定向到的地址,就可以实现请求重定向操作。
2、为了方便进行请求重定向操作,response提供了response.sendRedirect("....");实现请求重定向。
3、在大部分情况下请求重定向和转发的效果是差不多的,这时候我们推荐使用转发,以减少对服务器的访问。
而在某些情况下是需要使用转发的,目的往往是为了改变浏览器地址栏里的地址(如登录成功后转到主页),和更改刷新操作(如加入商品到购物车后转到购物车页面的操作)
4、Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。
5、Serlvet的service方法结束后,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎tomcat将调用close方法关闭该输出流对象。
@WebServlet("/RedirectServlet")
public class RedirectServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//利用response设置状态码为302,
//并设置响应头Location为要重定向到的地址(直至改变)
response.setStatus(302);
response.setHeader("Location", "/Day04/index.jsp");
//上面要写两行,于是接口HttpServletResponse提供了一个
//方法sendRedirect(String location)
response.sendRedirect("/Day04/index.jsp");
}
**getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。
对于请求转发的两个页面,两个方法调用要一致。(一次请求,一次响应);但请求重定向没有问题。
**response中获取的输出流,在service方法结束后服务器会帮我们关闭,所以一般不要自己在Servlet中关闭这个流.
(五).案例: 输出验证码图片
package com.lmd.response;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 输出验证码图片,目的:防止恶意注册
*/
@WebServlet("/OutImageServlet")
public class OutImageServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//把里面的“检查存储的页面的较新版本”设置为“从不”;此时,设置为不缓存
response.setIntHeader("Expires", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
//1、在内存中构建出一张图片
int height = 30;
int width = 120;
int xOffset = 5;
int yOffset = 20;
int bang = 18;
//建立BufferedImage对象:指定图片的长度宽度和类型
BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
//2、获取图像上的画布Graphics2D是Graphics的子类
//取得画布对象,用来绘制图片
Graphics2D grap = (Graphics2D) image.getGraphics();
//3、设置背景色,默认为黑色
grap.setColor(Color.WHITE);
grap.fillRect(0, 0, width, height);
//4、设置边框
grap.setColor(Color.BLUE);
grap.drawRect(0, 0, width - 1, height - 1);
//5、画干扰线,使用随机的,随机起点和终点
for (int i = 0; i < 5; i++) {
grap.setColor(Color.red);
grap.drawLine(randNum(0, width), randNum(0, height)
, randNum(0, width), randNum(0, height));
}
// //6、写字:颜色随机生成RGB,设置字体 ,验证字也要随机,验证字旋转方向
// 准备常用汉字集
//String str = "\u7684......
\u7ec6";String str = "0123456789abcdefghjkmnopqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ";
for (int j = 0; j < 4; j++) {
grap.setColor(new Color(randNum(0, 255), randNum(0, 255), randNum(0, 255)));
grap.setFont(new Font("黑体", Font.BOLD, bang));
int r = randNum(-45, 45);
//正向角度,旋转位置
grap.rotate(1.0*r/180 * Math.PI, xOffset+(j*30) , yOffset);
//如果验证码是中文,要使用中文的字体库
//通过词库生成随机验证码内容
//"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
//汉字:\u4e00 —— \u9fa5
grap.drawString( str.charAt(randNum(0, str.length()-1))+"", xOffset+(j*30) , yOffset);
//反向角度,旋转回原位置,为下一个字旋转做准备
grap.rotate(-1.0*r/180 * Math.PI, xOffset+(j*30) , yOffset);
}
//释放此图形的上下文以及它使用的所有系统资源
grap.dispose();
//7、将图片输出到浏览器:通过ImageIO对象的write静态方法将图片输出
ImageIO.write(image, "jpg", response.getOutputStream());
}
private Random rand = new Random();
private int randNum(int begin, int end) {
return rand.nextInt(end - begin) + begin;
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv= "content-type" content="text/html;charset=UTF-8">
<script type="text/javascript">
function changeImg(img){
//添加后面的时间后,每次点击后,会改变验证码;
//地址不变,加一个时间参数,会刷新
img.src = "/Day04/OutImageServlet?time="+new Date().getTime();
}
</script>
</head>
<body>
<form action="#" method="POST">
用户名:<input type="text" name="username" style="height:25px;"/><br><br>
密 码:<input type="password" name="password" style="height:25px;"/><br>
验证码:<input type="text" name="valistr" style="height:25px;"/>
<img src="/Day04/OutImageServlet" style="cursor:pointer" onclick="changeImg(this)"/><br>
<input type="submit" value="注册"/>
</form>
</body>
</html>
二、request
ServletRequest -- 通用request,提供一个request应该具有的最基本的方法
|--HttpServletRequest -- ServletRequest的孩子,针对http协议进行了进一步的增强
HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户这些信息。
Accept: text/html,image/* 客户端可以接受的数据类型 */* 所有类型
Accept-Charset: ISO-8859-1 客户端接受数据需要使用的字符集编码
Accept-Encoding: gzip,compress 客户端可以接受的数据压缩格式
Accept-Language: en-us,zh-cn 可接受的语言环境
Host: www.it315.org:80 想要访问的虚拟主机名 localhost
If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT 这是和缓存相关的一个头,带着缓存资源的最后获取时间
Referer: http://www.it315.org/index.jsp 这个头表示当前的请求来自哪个链接,这个头和防盗链的功能相关
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0) 客户端的一些基本信息
Cookie 会在后面讲会话技术的时候单讲
Connection: close/Keep-Alive 指定是否继续保持连接
Date: Tue, 11 Jul 2000 18:23:51 GMT 当前时间
(一).获取客户机信息
getRequestURL方法返回客户端发出请求完整URL
!!getRequestURI方法返回请求行中的资源名部分
getQueryString 方法返回请求行中的参数部分
!!getRemoteAddr方法返回发出请求的客户机的IP地址
!!getMethod得到客户机请求方式
!!getContextPath 获得当前web应用虚拟目录名称
@WebServlet("/CusInfoServlet")
public class CusInfoServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//getRequestURL方法 返回客户端发出请求完整URL
String url = request.getRequestURL().toString();
System.out.println(url);// http://localhost/Day04/CusInfoServlet
//!!getRequestURI方法 返回请求行中的资源名部分
String uri = request.getRequestURI();
System.out.println(uri);// /Day04/CusInfoServlet
//getQueryString 方法 返回请求行中的参数部分
String ref = request.getQueryString();
System.out.println(ref);// name=lily&age=18
//!!getRemoteAddr方法 返回发出请求的客户机的IP地址
//得到0:0:0:0:0:0:0:1
//修改C:\Windows\System32\drivers\etc下的hosts文件
//将# 127.0.0.1 localhost里的注释“#”去掉
//或者将在地址栏输入的localhost改为ip地址
String ip = request.getRemoteAddr();
System.out.println(ip);// 127.0.0.1
//!!getMethod 得到客户机请求方式,ServletRequest下的方法
String reqMethod = request.getMethod();
System.out.println(reqMethod);// GET
//!!getContextPath 获得当前web应用虚拟目录名称
String webPath = request.getContextPath();
System.out.println(webPath);/// /Day04
//以后获取web应用名称,均使用其
response.sendRedirect(request.getContextPath()+"/index.jsp");
}
(二).获取请求头信息
/1、获得客户机请求头
getHeader(name)方法 --- String
getHeaders(String name)方法 --- Enumeration<String>
getHeaderNames方法 --- Enumeration<String>
/2、获得具体类型客户机请求头
getIntHeader(name)方法 --- int
getDateHeader(name)方法 --- long(日期对应毫秒)
@WebServlet("/GetReqHeaderServlet")
public class GetReqHeaderServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获得客户机请求头
//getHeader(name)方法 --- String
//getHeaders(String name)方法 --- Enumeration<String>
//getHeaderNames方法 --- Enumeration<String>
String reqHeader = request.getHeader("Host");
System.out.println(reqHeader);// 输出:localhost:8080
Enumeration<String> headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String name = (String) headers.nextElement();
String value = request.getHeader(name);
System.out.println(name + ":" + value);
}//结果为上述请求头内容,输出如下
//accept:text/html, application/xhtml+xml, image/jxr, */*
//accept-language:en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3
//user-agent:Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
//accept-encoding:gzip, deflate
//host:localhost:8080
//connection:Keep-Alive
//cookie:JSESSIONID=88B88ECB68795C3CF0719D3C4CDBE35E
}
*防盗链:利用下面的请求头实现
Referer: http://www.it315.org/index.jsp 这个头表示当前的请求来自哪个链接,这个头和防盗链的功能相关
1、设置正常网页.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv= "content-type" content="text/html;charset=UTF-8">
</head>
<body>
<h1>网易新闻</h1>
广告<br>
广告<br>
<a href="/Day04/HerServlet">凤姐的回忆录。。。。。。</a><br>
广告<br>
</body>
</html>
2、链接访问页面servlet文件
@WebServlet("/HerServlet")
public class HerServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
//设置防盗链:防止盗链,回到主页
String ref = request.getHeader("Referer");
if (ref == null || "".equals(ref) || !ref.startsWith("http://localhost")) {
response.sendRedirect(request.getContextPath() + "/index.html");
return;
}
response.getWriter().write("她在黑马收获了很多。。。。。");
}
3、设置盗链页面
(1)、配置F:\tomcat8\conf下的server.xml:
<Host name="www.361.com" appBase="F:\webexample\361" />
(2)、在F:\webexample下新建:
361文件夹
|--ROOT文件夹(缺省wewb应用)
|--index.html
index.html内容如下:
<h1>易网</h1>
广告广告广告<br>
<a href="http://localhost:8080/Day04/HerServlet">凤姐独家新闻</a><br>
广告广告广告<br>
(3)、在C:\Windows\System32\drivers\etc下的hosts文件中添加:
127.0.0.1 www.361.com
防止盗链,回到
(三).获取请求参数
1、方法
getParameter(name) --- String 通过name获得值
getParameterValues --- String[ ] 通过name获得多值 checkbox
getParameterNames --- Enumeration<String> 获得所有name
getParameterMap --- Map<String,String[ ]> key :name value: 多值
2、数据非空校验
3、处理中文乱码
post: setCharacterEncoding //放在getParameter前才有效
get: new String(str.getBytes(“ISO-8859-1”),”utf-8”)
设置tomcat Connector URIEncoding=“utf-8”
<!DOCTYPE html>
<html>
<!-- 测试请求参数 -->
<head>
<meta http-equiv= "content-type" content="text/html;charset=UTF-8">
</head>
<body>
<h1>POST提交</h1><hr>
<form action="/Day04/ParamServlet" method="POST">
姓名:<input type="text" name="username" style="height:25px;"/><br>
地址:<input type="text" name="address" style="height:25px;"/><br>
<input type="submit" value="提交"/>
</form>
<h1>GET提交</h1>
<form action="/Day04/ParamServlet" method="GET">
姓名:<input type="text" name="username" style="height:25px;"/><br>
地址:<input type="text" name="address" style="height:25px;"/><br>
<input type="submit" value="提交"/>
</form>
</body>
</html>
@WebServlet("/ParamServlet")
public class ParamServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
String address = request.getParameter("address");
System.out.println(username + ":" + address);//lily:China
//遍历所有的请求参数
Enumeration<String> enu = request.getParameterNames();
while (enu.hasMoreElements()) {
String name = (String) enu.nextElement();
String value = request.getParameter(name);
System.out.println(name + ":" + value);
}//换行输出 username:lily address:China
}
对于中文的输入:
GET提交:http://localhost:8080/Day04/ParamServlet?username=%E5%BC%A0%E7%8F%8A&address=%E4%B8%AD%E5%9B%BD
一个汉字在UTF-8中表示3个字节;一个字符在ISO8859-1(常用)中表示一个字节。
一个汉字,三个字节,在ISO8859-1中找3个字符。所以上面是6个?
4、乱码的问题:
(1)、浏览器以什么编码来发送请求参数? 浏览器以什么编码打开的表单页面,就用什么编码发送这个页面提交的数据。
服务器以什么编码来打开呢?如果不指定,则使用ISO8859-1,这样如果请求参数中有中文必然就乱码了。
(2)、 对于POST提交,可以设置request.setCharacterEncoding("utf-8");明确的通知服务器以浏览器发送过来的编码来打开数据就可以解决乱码;但是上面的方法只对请求中实体内容部分起作用,所以GET提交的乱码并不能解决.
对于GET提交的乱码,只能手动的进行编解码从而解决乱码问题:
String username = request.getParameter("username");
username = new String(username.getBytes("iso8859-1"),"utf-8");
(四).利用请求域传递对象
1、作用范围:整个请求链上
2、生命周期:当服务器收到一个请求,创建出代表请求的request对象,request开始。当请求结束,服务器销毁代表请求的request对象,request域结束。
3、作用:在整个请求链范围内共享数据,通常我们在Servlet中处理好的数据会存入request域后请求转发到jsp页面来进行展示
setAttribute getAttribute removeAttribute
@WebServlet("/Area2Servlet")
public class Area2Servlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//request.setAttribute("苹果", "Red and Green Apple");
//this.getServletContext().getRequestDispatcher("/Area1Servlet").forward(request, response);
String result = "Red and Green Apple";
request.setAttribute("苹果", result);
//一般不用Servlet做输出,HTML页面组织麻烦,转发到show.jsp页面上做输出
request.getRequestDispatcher("/show.jsp").forward(request, response);
}
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<font color = "red">
Apple的属性:<%=(String)request.getAttribute("苹果") %>
</font>
</body>
</html>
(五).实现请求转发和请求包含
1、请求转发:
原来:this.getServletContext().getRequestDispatcher("").forward(request,response);
现在:request.getRequestDispatcher("").forward(request,response);
@WebServlet("/Transmit1Servlet")
public class Transmit1Servlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//使用getWriter方法时,1放在输出语句之前;getOutputStream方法,前后均可
response.setContentType("text/html;charset=UTF-8"); //1 其放到第一句,无乱码
response.getWriter().write("哎,from Transmit1Servlet"); //2
//response.getWriter().flush();//刷新到浏览器中,测试:出现异常
request.getRequestDispatcher("/Transmit2Servlet").forward(request, response);
}
@WebServlet("/Transmit2Servlet")
public class Transmit2Servlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
response.getWriter().write("哈哈,from Transmit2Servlet");
}
加入response.getWriter().flush();,将输出刷新到浏览器中,测试:出现异常
若在一个页面中同时有两个请求转发,出现异常:
~请求转发是希望将请求交给另外一个资源执行,所以应该保证只有最后真正要执行的资源才能够输出数据,所以:
*请求转发时,如果已经有数据被写入到了response的缓冲区,但是这些数据还没有被发送到客户端,则请求转发时,这些数据将会被清空;但是清空的只是响应中的实体内容部分,头信息并不会被清空。
*而请求转发时已经有数据被打给了浏览器,那么再进行请求转发,不能成功,会抛出异常,原因是响应已经结束了,再转发交给其他人没意义了。
*在最终输出数据的Servlet执行完成后。response实体内容中的数据将会被设置为已提交的状态。再往里写数据也不会起作用。
-------使用以上三条,就保证了最终只有一个Servlet能够向浏览器输出数据,所以
*一个Servlet里两次请求转发也是不可以的,一次请求交给两人处理自然也是不行.
案例:
@WebServlet("/Demo1")
public class Demo1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("before Demo1 ");
response.getWriter().write("out from Demo1 bef ");
request.getRequestDispatcher("/Demo2").forward(request, response);
System.out.println("after Demo1 ");
response.getWriter().write("out from Demo1 aft ");
}
@WebServlet("/Demo2")
public class Demo2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("before Demo2 ");
response.getWriter().write("out from Demo2 bef ");
response.getWriter().write("from Demo2...... ");
System.out.println("after Demo2 ");
response.getWriter().write("out from Demo2 aft ");
}
输出结果:
请求重定向和请求转发的区别
1、RequestDispatcher.forward方法只能将请求转发给同一个WEB应用中的组件;
而HttpServletResponse.sendRedirect 方法还可以重定向到同一个站点上的其他应用程序中的资源,
甚至是使用绝对URL重定向到其他站点的资源。
2、如果传递给HttpServletResponse.sendRedirect 方法的相对URL以“/”开头,它是相对于服务器的根目录;
如果创建RequestDispatcher对象时指定的相对URL以“/”开头,它是相对于当前WEB应用程序的根目录。
3、调用HttpServletResponse.sendRedirect方法重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,
由初始的URL地址变成重定向的目标URL;
调用RequestDispatcher.forward 方法的请求转发过程结束后,浏览器地址栏保持初始的URL地址不变。
4、HttpServletResponse.sendRedirect方法对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出
对另外一个URL的访问请求;
RequestDispatcher.forward方法在服务器端内部将请求转发给另外一个资源,浏览器只知道发出了请求并得到了响应结果,
并不知道在服务器程序内部发生了转发行为。
5、RequestDispatcher.forward方法的调用者与被调用者之间共享相同的request对象和response对象,
它们属于同一个访问请求和响应过程;
而HttpServletResponse.sendRedirect方法调用者与被调用者使用各自的request对象和response对象,
它们属于两个独立的访问请求和响应过程。
2、请求包含:将两个资源的输出进行合并后输出
原来: this.getServletContext().getRequestDispatcher("").include(request,response);
现在:request.getRequestDispatcher("").include(request,response);
(1)、RequestDispatcher.include方法用于将RequestDispatcher对象封装的资源内容作为当前响应内容的一部分包含进来,从而实现可编程的服务器端包含功能.
(2)、被包含的Servlet程序不能改变响应消息的状态码和响应头,如果它里面存在这样的语句,这些语句的执行结果将被忽略。
(3)、include在程序执行上效果类似forward,但是使用forward只有一个程序可以生成响应,include可以由多个程序一同生成响应 ----- 常被用来进行页面布局。
@WebServlet("/Include1Servlet")
public class Include1Servlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getWriter().write("from Include1Servlet ");
//forward方法:浏览器只显示Include2Servlet的输出,而其被忽略
//request.getRequestDispatcher("/Include2Servlet").forward(request, response);
//此时输出结果为:from Include2Servlet
request.getRequestDispatcher("/Include2Servlet").include(request, response);
//此时输出结果为:from Include1Servlet from Include2Servlet
}
@WebServlet("/Include2Servlet")
public class Include2Servlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getWriter().write("from Include2Servlet ");
}
3、三种资源处理方式的区别
请求重定向
response.sendRedirect();
请求转发
request.getRequestDispatcher().forward();
请求包含
request.getRequestDispatcher().include();
请求重定向和请求转发的区别:
请求重定向地址栏会发生变化。 请求转发地址栏不发生变化.
请求重定向两次请求两次响应。 请求转发一次请求一次响应.
如果需要在资源跳转时利用request域传递域属性则必须使用请求转发;
如果希望资源跳转后修改用户的地址栏则使用请求重定向;
如果使用请求转发也可以重定向也可以,则优先使用请求转发,减少浏览器对服务器的访问次数减轻服务器的压力。
一.请求重定向:
1.原始方式
response.setStatus(302);
response.setHeader("Locaton","/xxxxxx");
2.快捷方式
response.sendRedirect("/xxxx");
*3.额外的方式
1. response.setHeader("refresh","0;url=/xxxx");
2. <c:redirect>
二、请求转发
1.
ServletContext.getRequestDispatcher().forward();2.
request.getRequestDispatcher().forward();3.
pageContext.forward("");- 4.<jsp:forward>
三、请求包含
1.
ServletContext.getRequestDispatcher().include()2.
request.getRequestDispatcher().include()3.
<%@ include file=""%>4.
pageContext.include("/index.jsp");5.
<jsp:include/>6.
<c:import/>
============================================================================================
常用地址的写法:
绝对路径:以/开头的路径就叫做绝对路径,绝对路径在相对于的路径上直接拼接得到最终的路径。
相对路径:不以/开头的路径就叫做相对路径,相对路径基于当前所在的路径计算的到最终的路径。
硬盘路径:以盘符开头的路径就叫做硬盘路径。是哪个路径就是哪个路径。没有相对于谁的问题。
1、虚拟路径: --写虚拟路径时都使用绝对路径
如果路径是给浏览器用的,这个路径相对于虚拟主机,所以需要写上web应用的名称;
如果路径是个服务器用的,这个路径相对于web应用,所以可以省写web应用的名称。
<a href="/Day04/....."> 浏览器
<form action="/Day04/..."> 浏览器
<img src="/Day04/...."> 浏览器
response.setHeader("Location","/Day04/...."); 浏览器 302+Locaction
response.setHeader("refresh","3;url=/Day04/..."); 浏览器
response.sendRedirect("/Day04/..."); 浏览器
request.getRequestDispathce("/index.jsp").forward(); 服务器
request.getRequestDispathce("/index.jsp").include(); 服务器
/Day04/Demo6转发到:
/Day04/index.jsp
若使用绝对路径:
request.getRequestDispathce("/Day04/index.jsp").forward();
若使用相对路径:(分析路径,麻烦,易出错)
request.getRequestDispathce("../index.jsp").forward();
2、 真实路径: --写真实路径时都使用相对路径
根据原理,具体问题具体分析
servletContext.getRealPath("config.properties");//--给一个相对于web应用目录的路径
classLoader.getResource("../../config.properties");//--给一个相对于类加载目录的路径
@WebServlet("/Demo6")
public class Demo6 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.getRequestDispatcher("/Day04/index.jsp");//绝对路径
String path = Demo6.class.getClassLoader().getResource("config.properties").toString();
System.out.println(path);
- //在src下新建config.properties,与包同路径下
/输出:file:/F:/tomcat8/webapps/Day04/WEB-INF/classes/config.properties
}
File file = new File("config.properties");//--相对于程序的启动目录,web工程下面找
new InputStream("config.properties");//--相对于程序的启动目录