HttpServletResponse的字符字节输出流、编码、文件下载、Captcha图片与HttpServletRequest获取request数据表单非表单数据、重定向与转发
2016-12-11 11:04
941 查看
1、HttpServletResponse。
——我们要输出东西,也就是设置响应的response,在下面我们利用字符输出流来向浏览器输出东西。这里利用了response的getWriter方法,这个getWriter的返回值是一个PrintWriter对象。
2
3
4
5
6
——但是,上面的结果在浏览器里是部分显示“?”,即中文部分。原因是服务器默认的字符编码是“ISO-8859-1”,它是不支持中文的。
——所以,我们在输出东西之前把服务器的编码设置一下:
2
3
4
——结果如下。原因是虽然我们服务器知道用UTF-8去编码,但是生成的文件送到浏览器的时候,浏览器不知道用什么编码来解析文档,所以一般情况下我们的浏览器都是GBK的编码,结果当然就是出现乱码了。
——解决办法,就是通过上面的语句告诉服务器用什么编码之后,再告诉浏览器用什么编码来解析文档。
2
3
4
——这样,一切输出就正常了。但是我们有一种简便的写法,就是只用一句代码就能代替上面的两行代码分别设置服务器和告诉浏览器的编码。
2
3
2、字节流输出。
——write方法的参数是字节数组,所以需要对字符串进行转换。
——这一次在浏览器中输出结果是正常的。原因在getBytes()方法,这个方法是将字符串以平台默认的编码进行字节转换。我们电脑一般都是GBK编码(中文的),浏览器默认也是GBK的,所以当然没问题。getBytes()可以接受参数比如“UTF-8”,如果这样的话,浏览器是不知道的会出现乱码,这个时候,我们在前面设置个
2
3
3、文件的下载。
——我们新建了一个Servlet,把一张图片放在了
2
3
4
5
6
7
8
9
10
11
12
13
14
——我们输入“http://localhost:8080/Day01_RequestResponse/servlet/HttpServletResponseDemo2”成功在浏览器上输出了图片。成功个毛啊?不是说好是下载图片的么?
——那我们就设置一下response的头文件,content-disposition和content-type两个属性。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
——这下,一打开网址,文件自动下载(win10,chrome)。但是文件名是
——然后,就真的下载了一个叫做
4、小技巧:
——MyEclipse里的代码提示功能:
——选择Debug Server启动服务器,这样我们修改了一点代码之后就不需要重新部署重启服务器了,直接刷新浏览器就能够看到变化了。
5、Captcha,也就是实验一下图像相关的对象。
——创建一个Servlet,在里面写如下代码,每一步的意义都写在代码里了。无非就是先获取一个宽高指定图片对象、获取画笔、设置颜色、画线画框填充背景画文字、利用ImageIO的write方法把这个图片以字节流的方式输出到浏览器中。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
——我们创建一个login.html文件,把这个验证码图片集成到登录表单中。我们直接访问“http://localhost:8080/Day01_RequestResponse/login.html”即可,关于直接访问静态资源的url是被default
servlet处理了,这个问题我在JavaWeb能够直接访问html文件的原理是什么?里问过,记得其原理。
——直接刷新浏览器,相当于新的请求,验证码图片会变。但是我们点击图片或者换一张的时候,如果请求依然是“/Day01_RequestResponse/servlet/Captcha”的话,我们会发现图片不会变化,因为浏览器的缓存原因。也就是说浏览器发现你这个请求的url路径没变,所以把上一次在缓存中的东西拿出来,导致我们根本没有去服务器请求新的图片。所以我们在请求路径后面增加一个日期变量。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
——我们利用前人的轮子来写Captcha就不需要自己手动写那么多代码了。我们先把自己写的代码抽取方法到test1里面去。然后引入一个用来生成Captcha的jar(ValidateCode.jar)放入WebRoot/WEB-INF/lib/下,最后在doGet里面写2行代码即可,只要2行,即创建、输出。
注意:引入新的jar包需要重启服务器。
2
3
4
5
——可以在网上搜索不同风格的Captcha的jar包。
——我们在浏览器中刷新网址的时候,验证码图片是变化的,但有时候浏览器缓存搞鬼的话,我们可以禁止缓存,可以达到同样的目的:
2
3
4
5
6
7
8
9
10
11
12
13
6、自动刷新和自动跳转的例子。
——我们创建了一个RefreshDemo1的Servlet类,在里面写了如下代码。我们打开“http://localhost:8080/Day01_RequestResponse/servlet/RefreshDemo1”,这个页面就会每2秒钟刷新一下,你就会看到不同的随机数。
2
3
4
5
6
7
8
9
10
——常用的例子,就是过几秒钟跳转。也就是说刷新可以和页面跳转联系在一起。本质上,这里说的页面跳转,其实也是发一个url路径去一个新的Servlet而已。我们在RefreshDemo1的Servlet中写:
2
3
4
5
6
7
8
9
——然后在RefreshDemo2的Servlet中写:
2
3
4
5
6
7
——接下来我们访问“http://localhost:8080/Day01_RequestResponse/servlet/RefreshDemo1”就出现提示信息,然后过3秒就跳转了。
7、下面是请求方面的内容了,先来一个重定向。
——重定向需要注意的就是和请求转发的区别。重定向的意思就是,我发一个请求给A,A说我不处理但我告诉你去找B吧(注意此时A把响应返回回来了,也就是说我们在访问B之前,是完成的接受了A的响应的,而不是立即去B那里),然后我从A那得到B的地址,我自己再访问B。也就是我自己访问了2次。而转发的意思是,我请求A,A处理不了但它自动帮我转发给B处理去了,然后B处理的结果给A,A再把结果给我,我只访问了1次。这就是区别。下面看例子:
——我们创建一个Servlet类,叫RedirectDemo01,里面的代码主要的意思就是,我们得在响应里面告诉浏览器,这是一个重定向(302),然后再告诉浏览器重定向的地址(url)。
2
3
4
5
6
7
——我们创建一个Servlet类,叫RedirectDemo02,里面简单一个输出,用来看看我们重定向的请求走向的。
2
3
4
——我们在浏览器上输入“http://localhost:8080/Day01_RequestResponse/servlet/RedirectDemo01”后,发现浏览器的url地址变成了“http://localhost:8080/Day01_RequestResponse/servlet/RedirectDemo02”。然后我们看我们Console中有如下输出信息。可见我们在RedirectDemo01里面是完整的执行完了所有程序,之后在去重新请求RedirectDemo02的。如果是转发的话,遇到转发的那行代码它就跑去其他Servlet类了,执行完再返回本Servlet类来继续向下执行,这就是区别。
2
3
——当然,这个重定向是可以优化的。上面的两行核心代码,一个设置302状态码一个设置url,可以合并成一句代码。
8、插播一些小知识。
——在同一个Servlet里面不能同时写getOutputStream和getWriter,这两个方法是冲突的,只能写一个,否则报错。
——我们在service里面(可以看做是在doGet等方法里面)创建的输入输出流,不用我们自己关闭,服务器在执行完这个方法后会检查是否有未关闭的,如果有,服务器会帮我们关闭。
9、与请求消息行有关的方法,就是获取消息行里的信息的。我们在一个Servlet里写:
2
3
4
5
6
7
在浏览器输入“http://localhost:8080/Day01_RequestResponse/servlet/RequestDemo1”,输出结果是,分别是完整URL路径、资源URI路径、应用虚拟目录、查询参数(url里没有所以是null,如果我们的url是”http://localhost:8080/Day01_RequestResponse/servlet/RequestDemo1?name=eric“,那么它的值就是name=eric):
2
3
4
10、与请求消息头有关的方法,就是获得消息头里面的信息。我们在一个Servlet里写:
2
3
4
5
6
7
8
9
10
11
12
13
14
输出结果是:
2
——上面是知道一个消息头的name然后获取对应的value,如果我们想一下子全部输出的话,就获取全部的names,它是一个枚举,然后while循环输出,输出结果不列举了,就是全部的消息头。还有一个getHeaders方法,就是防止有同名的name,获得是同样是枚举类型,使用方法同下面的。
2
3
4
5
6
7
8
11、获取正文-表单数据相关。
——为了做这个实验,得需要一个login.html页面,上面写个表单,然后提交到我们的Servlet去处理,我们在Servlet中获得表单里面的数据。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
输出结果是:
2
3
4
——一下子获得所有表单名names,然后对这个枚举里的name一个个获得对应的值,如下代码:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Console输出结果是:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
——重点来了。我们获取表单里面的内容主要是为了写入数据库的,也就是说赋值给我们的一个类,比如这里我们创建一个User类,把上面提交过来的信息赋值给这个User类。
——我们创建了一个User类,放在src下的com.hello.entity下。这个User类的几个属性需要和form的name一样。并实现getter、setter和toString方法(用于输出对象的信息)。
——我们在Servlet中做了处理,用getParameterMap方法获得所有表单的数据,并把这个数据放到User对象中。这里有个新知识点是PropertyDescriptor 需要了解。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
输出结果:
2
——当然,你说本来整个getParameterMap方法可以一下子获得表单的全部数据并得到一个Map类型值,但这提取数据赋值的过程也太复杂了。其实,我们这个方法主要是配合框架使用的。什么意思?就是框架里面已经帮我们写好了这些赋值操作,我们只需要用getParameterMap方法得到一个Map值即可。
——把这两个jar包放在lib中,主要是beanutils包,logging包是日志作用。
——然后使用BeanUtils.populate方法,第一个参数是接受数据的对象,第二个数据就是Map值,我们通过getParameterMap方法拿到即可。就这么简单。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
输出结果是:
2
12、字节流形式获得表单数据。主要方法是getInputStream。
2
3
4
5
6
7
8
9
输出结果是(字符编码问题暂未解决):
13、请求转发和获取非表单数据的方法。
——获取非表单数据,其实就是利用了request的setAttribute和getAttribute方法。所以区别在于非表单数据都是通过getAttribute获取的,表单数据都是通过getParameter*获取的。
——在RequestDispatcherDemo1里:
2
3
4
5
6
7
8
——在RequestDispatcherDemo2里:
2
3
4
5
——访问“http://localhost:8080/Day01_RequestResponse/servlet/RequestDispatcherDemo1”时,Console有输出结果,注意转发和重定向的不同,转发的url是不会跳转至“http://localhost:8080/Day01_RequestResponse/servlet/RequestDispatcherDemo2”的,因为它就是1次访问,另外一个是Servlet帮我们转发的,不需要浏览器发起请求。
2
3
4
——总结一下重要知识点:
重定向是response下面的方法,转发是request下面的方法;
转发不可以跳转到其他领用,重定向可以跳转至其他应用,如下。因为转发最终的转发路径是
重定向在浏览器中的URL是变化的,而转发是不变的。
重定向当然也可以利用request的setAttribute设置数据,但是没有用,因为重定向的第二次请求是一个全新的request,根本和第一次携带数据的request不是同一个,所以根本拿不到数据。
14、请求包含。其实本质上就是把另一个文件包含到当前文件里来,最显著的特征就是它们可以利用request传递数据了,因为都包含到同一个地方来了,相当于同一个文件了,request也是同一个,所以可以传递数据。
——在RequestDispatcherDemo1中:
2
3
4
5
6
7
——在RequestDispatcherDemo2中:
2
3
4
5
输出结果是:
2
3
4
15、另外一个技巧,request.setCharacterEncoding方法只能解决post请求的编码问题,不能解决get请求,比如我们把form表单的方法改成get的话,request.setCharacterEncoding就不起作用了。那么解决办法就是,获取每个表单值之后,进行手动转码,比如:
2
3
——我们要输出东西,也就是设置响应的response,在下面我们利用字符输出流来向浏览器输出东西。这里利用了response的getWriter方法,这个getWriter的返回值是一个PrintWriter对象。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 得到一个字符输出流 PrintWriter out=response.getWriter(); out.print("你好Hello!"); }1
2
3
4
5
6
——但是,上面的结果在浏览器里是部分显示“?”,即中文部分。原因是服务器默认的字符编码是“ISO-8859-1”,它是不支持中文的。
??Hello!1
——所以,我们在输出东西之前把服务器的编码设置一下:
response.setCharacterEncoding("UTF-8"); // 得到一个字符输出流 PrintWriter out=response.getWriter(); out.print("你好Hello!");1
2
3
4
——结果如下。原因是虽然我们服务器知道用UTF-8去编码,但是生成的文件送到浏览器的时候,浏览器不知道用什么编码来解析文档,所以一般情况下我们的浏览器都是GBK的编码,结果当然就是出现乱码了。
浣犲ソHello!1
——解决办法,就是通过上面的语句告诉服务器用什么编码之后,再告诉浏览器用什么编码来解析文档。
response.setCharacterEncoding("UTF-8"); response.setHeader("content-type", "text/html;charset=UTF-8"); PrintWriter out=response.getWriter(); out.print("你好Hello!");1
2
3
4
——这样,一切输出就正常了。但是我们有一种简便的写法,就是只用一句代码就能代替上面的两行代码分别设置服务器和告诉浏览器的编码。
response.setContentType("text/html;charset=UTF-8"); PrintWriter out=response.getWriter(); out.print("你好Hello!");1
2
3
2、字节流输出。
——write方法的参数是字节数组,所以需要对字符串进行转换。
——这一次在浏览器中输出结果是正常的。原因在getBytes()方法,这个方法是将字符串以平台默认的编码进行字节转换。我们电脑一般都是GBK编码(中文的),浏览器默认也是GBK的,所以当然没问题。getBytes()可以接受参数比如“UTF-8”,如果这样的话,浏览器是不知道的会出现乱码,这个时候,我们在前面设置个
response.setContentType("text/html;charset=UTF-8")可以解决乱码问题。
//字节流输出 ServletOutputStream sos=response.getOutputStream(); sos.write("你好Hello!".getBytes());1
2
3
3、文件的下载。
——我们新建了一个Servlet,把一张图片放在了
WEB-INF/images/下面,这个图片名称里面有中文。然后我们写了下面的代码,意思是先获取这个图片的路径、用这个路径生成一个文件输入流(FileInputStream)、利用response生成一个字节输出流、最后利用一个while循环把这个字节流的图片输出出去。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String path=this.getServletContext().getRealPath("/images/img_头像.jpg"); // 获取输入流 FileInputStream fis=new FileInputStream(path); // 输出流 ServletOutputStream sos=response.getOutputStream(); // 输出 int len=1; byte[] b=new byte[1024]; while((len=fis.read(b))!=-1){ sos.write(b, 0, len); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
——我们输入“http://localhost:8080/Day01_RequestResponse/servlet/HttpServletResponseDemo2”成功在浏览器上输出了图片。成功个毛啊?不是说好是下载图片的么?
——那我们就设置一下response的头文件,content-disposition和content-type两个属性。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String path=this.getServletContext().getRealPath("/images/img_头像.jpg"); // 获取输入流 FileInputStream fis=new FileInputStream(path); // 输出流 ServletOutputStream sos=response.getOutputStream(); // 告诉浏览器是一个图片,并且以附件形式下载 String filename=path.substring(path.lastIndexOf("\\")+1); response.setHeader("content-disposition", "attachment;filename="+filename); response.setHeader("content-type", "image/jpeg"); // 输出 int len=1; byte[] b=new byte[1024]; while((len=fis.read(b))!=-1){ sos.write(b, 0, len); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
——这下,一打开网址,文件自动下载(win10,chrome)。但是文件名是
img_ .jpg。我们发现文件名原来是
img_头像.jpg,中文变成2个空格,这是因为文件名出问题了。文件名中出现中文的时候需要进行URLEncoder编码。继续配置,其实就是一行代码,把我们的filename编码一下再使用。
filename=URLEncoder.encode(filename, "UTF-8");1
——然后,就真的下载了一个叫做
img_头像.jpg的图片。这里补充一下,之前获取filename的时候index位置弄错了,写成下面这个样子,少往后移1位,所以文件名里面有自带了一个“/”,这斜杠如果不经过URLEncoder编码的话也不会正常显示,它显示
-,而经过编码之后显示的是
%5c,所以如果你看到文件名里有这个,说明文件名获取有问题。
filename=path.substring(path.lastIndexOf("\\"));1
4、小技巧:
——MyEclipse里的代码提示功能:
Alt+/。
——选择Debug Server启动服务器,这样我们修改了一点代码之后就不需要重新部署重启服务器了,直接刷新浏览器就能够看到变化了。
5、Captcha,也就是实验一下图像相关的对象。
——创建一个Servlet,在里面写如下代码,每一步的意义都写在代码里了。无非就是先获取一个宽高指定图片对象、获取画笔、设置颜色、画线画框填充背景画文字、利用ImageIO的write方法把这个图片以字节流的方式输出到浏览器中。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int width=115; int height=24; // 获取一个图片对象,默认是黑色的 BufferedImage bi=new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); // 获取画笔 Graphics g=bi.getGraphics(); // 设置图片背景颜色,第一步设置颜色,第二部设置填充 g.setColor(Color.PINK); g.fillRect(1, 1, width-2, height-2); // 设置边框,也是两个步骤 g.setColor(Color.DARK_GRAY); g.drawRect(0, 0, width-1, height-1); // 设置一些线条 Random rand=new Random(); g.setColor(Color.RED); for(int i=0;i<6;i++){ g.drawLine(rand.nextInt(width), rand.nextInt(height), rand.nextInt(width), rand.nextInt(height)); } // 写一些文字 g.setColor(Color.BLACK); g.setFont(new Font("宋体", Font.BOLD|Font.ITALIC, 15)); int position=20; for(int i=0;i<4;i++){ g.drawString(String.valueOf(rand.nextInt(10)), position*(i+1), 16); } // 将图片以流的方式输出到浏览器上 ImageIO.write(bi, "jpg", response.getOutputStream()); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
——我们创建一个login.html文件,把这个验证码图片集成到登录表单中。我们直接访问“http://localhost:8080/Day01_RequestResponse/login.html”即可,关于直接访问静态资源的url是被default
servlet处理了,这个问题我在JavaWeb能够直接访问html文件的原理是什么?里问过,记得其原理。
——直接刷新浏览器,相当于新的请求,验证码图片会变。但是我们点击图片或者换一张的时候,如果请求依然是“/Day01_RequestResponse/servlet/Captcha”的话,我们会发现图片不会变化,因为浏览器的缓存原因。也就是说浏览器发现你这个请求的url路径没变,所以把上一次在缓存中的东西拿出来,导致我们根本没有去服务器请求新的图片。所以我们在请求路径后面增加一个日期变量。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> <script type="text/javascript"> function captchaRefresh(){ var img=document.getElementsByTagName("img")[0]; img.src="/Day01_RequestResponse/servlet/Captcha?time="+new Date().getTime(); } </script> </head> <body> <form action="" method="post"> <input type="text" name="username" placeholder="用户名" /><br> <input type="text" name="password" placeholder="密码" /><br> <input type="text" name="captcha" placeholder="验证码" /><img src="/Day01_RequestResponse/servlet/Captcha" onclick="captchaRefresh()" /><a href="javascript:captchaRefresh()">看不清换一张</a><br> <input type="submit" value="提交"> </form> </body> </html>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
——我们利用前人的轮子来写Captcha就不需要自己手动写那么多代码了。我们先把自己写的代码抽取方法到test1里面去。然后引入一个用来生成Captcha的jar(ValidateCode.jar)放入WebRoot/WEB-INF/lib/下,最后在doGet里面写2行代码即可,只要2行,即创建、输出。
注意:引入新的jar包需要重启服务器。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ValidateCode vc = new ValidateCode(115, 24, 4, 6); vc.write(response.getOutputStream()); }1
2
3
4
5
——可以在网上搜索不同风格的Captcha的jar包。
——我们在浏览器中刷新网址的时候,验证码图片是变化的,但有时候浏览器缓存搞鬼的话,我们可以禁止缓存,可以达到同样的目的:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 以下3种方式是针对不同浏览器的禁止缓存的方法 response.setHeader("pragma", "no-cache"); response.setHeader("cache-control", "no-cache"); // 以下3行代码是同一个效果,都是通过设置缓存过期时间来达到不缓存的目的 response.setHeader("expires", "0"); response.setIntHeader("expires", 0); response.setDateHeader("expires", 0); ValidateCode vc = new ValidateCode(115, 24, 4, 6); vc.write(response.getOutputStream()); }1
2
3
4
5
6
7
8
9
10
11
12
13
6、自动刷新和自动跳转的例子。
——我们创建了一个RefreshDemo1的Servlet类,在里面写了如下代码。我们打开“http://localhost:8080/Day01_RequestResponse/servlet/RefreshDemo1”,这个页面就会每2秒钟刷新一下,你就会看到不同的随机数。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置刷新,下面两行代码是等价的设置 // response.setHeader("refresh", "2"); response.setIntHeader("refresh", 2); // 测试刷新 Random rand=new Random(); response.getWriter().write(String.valueOf(rand.nextInt(100))); }1
2
3
4
5
6
7
8
9
10
——常用的例子,就是过几秒钟跳转。也就是说刷新可以和页面跳转联系在一起。本质上,这里说的页面跳转,其实也是发一个url路径去一个新的Servlet而已。我们在RefreshDemo1的Servlet中写:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 不加这一句代码,页面上的中文都是问号? response.setContentType("text/html;charset=UTF-8"); // 输出一句提醒 response.getWriter().write("注册成功!3秒后自动跳转至主页!"); // 核心代码,也就是在刷新秒数后面加了一个url response.setHeader("refresh", "3;url=/Day01_RequestResponse/servlet/RefreshDemo2"); }1
2
3
4
5
6
7
8
9
——然后在RefreshDemo2的Servlet中写:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 不加这一句代码,页面上的中文都是问号? response.setContentType("text/html;charset=UTF-8"); response.getWriter().write("我是主页!"); }1
2
3
4
5
6
7
——接下来我们访问“http://localhost:8080/Day01_RequestResponse/servlet/RefreshDemo1”就出现提示信息,然后过3秒就跳转了。
7、下面是请求方面的内容了,先来一个重定向。
——重定向需要注意的就是和请求转发的区别。重定向的意思就是,我发一个请求给A,A说我不处理但我告诉你去找B吧(注意此时A把响应返回回来了,也就是说我们在访问B之前,是完成的接受了A的响应的,而不是立即去B那里),然后我从A那得到B的地址,我自己再访问B。也就是我自己访问了2次。而转发的意思是,我请求A,A处理不了但它自动帮我转发给B处理去了,然后B处理的结果给A,A再把结果给我,我只访问了1次。这就是区别。下面看例子:
——我们创建一个Servlet类,叫RedirectDemo01,里面的代码主要的意思就是,我们得在响应里面告诉浏览器,这是一个重定向(302),然后再告诉浏览器重定向的地址(url)。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("RedirectDemo01:我这里没有你要的东西,我告诉你去哪里找吧!"); response.setStatus(302); response.setHeader("location", "/Day01_RequestResponse/servlet/RedirectDemo02"); System.out.println("RedirectDemo01:302和地址都设置好了!"); }1
2
3
4
5
6
7
——我们创建一个Servlet类,叫RedirectDemo02,里面简单一个输出,用来看看我们重定向的请求走向的。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("RedirectDemo02:我有,你拿去吧!"); }1
2
3
4
——我们在浏览器上输入“http://localhost:8080/Day01_RequestResponse/servlet/RedirectDemo01”后,发现浏览器的url地址变成了“http://localhost:8080/Day01_RequestResponse/servlet/RedirectDemo02”。然后我们看我们Console中有如下输出信息。可见我们在RedirectDemo01里面是完整的执行完了所有程序,之后在去重新请求RedirectDemo02的。如果是转发的话,遇到转发的那行代码它就跑去其他Servlet类了,执行完再返回本Servlet类来继续向下执行,这就是区别。
RedirectDemo01:我这里没有你要的东西,我告诉你去哪里找吧! RedirectDemo01:302和地址都设置好了! RedirectDemo02:我有,你拿去吧!1
2
3
——当然,这个重定向是可以优化的。上面的两行核心代码,一个设置302状态码一个设置url,可以合并成一句代码。
response.sendRedirect("/Day01_RequestResponse/servlet/RedirectDemo02");1
8、插播一些小知识。
——在同一个Servlet里面不能同时写getOutputStream和getWriter,这两个方法是冲突的,只能写一个,否则报错。
——我们在service里面(可以看做是在doGet等方法里面)创建的输入输出流,不用我们自己关闭,服务器在执行完这个方法后会检查是否有未关闭的,如果有,服务器会帮我们关闭。
9、与请求消息行有关的方法,就是获取消息行里的信息的。我们在一个Servlet里写:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println(request.getRequestURL()); System.out.println(request.getRequestURI()); System.out.println(request.getContextPath()); System.out.println(request.getQueryString()); }1
2
3
4
5
6
7
在浏览器输入“http://localhost:8080/Day01_RequestResponse/servlet/RequestDemo1”,输出结果是,分别是完整URL路径、资源URI路径、应用虚拟目录、查询参数(url里没有所以是null,如果我们的url是”http://localhost:8080/Day01_RequestResponse/servlet/RequestDemo1?name=eric“,那么它的值就是name=eric):
http://localhost:8080/Day01_RequestResponse/servlet/RequestDemo1 /Day01_RequestResponse/servlet/RequestDemo1 /Day01_RequestResponse null1
2
3
4
10、与请求消息头有关的方法,就是获得消息头里面的信息。我们在一个Servlet里写:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String ua=request.getHeader("User-Agent"); System.out.println(ua); if(ua.toLowerCase().contains("msie")){ System.out.println("您使用的是IE浏览器!"); }else if(ua.toLowerCase().contains("firefox")){ System.out.println("您使用的是火狐浏览器!"); }else if(ua.toLowerCase().contains("chrome")){ System.out.println("您使用的是谷歌浏览器!"); }else{ System.out.println("您使用的是火星浏览器!"); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
输出结果是:
Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 您使用的是谷歌浏览器!1
2
——上面是知道一个消息头的name然后获取对应的value,如果我们想一下子全部输出的话,就获取全部的names,它是一个枚举,然后while循环输出,输出结果不列举了,就是全部的消息头。还有一个getHeaders方法,就是防止有同名的name,获得是同样是枚举类型,使用方法同下面的。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Enumeration names=request.getHeaderNames(); while(names.hasMoreElements()){ String e=(String) names.nextElement(); System.out.println(e+":"+request.getHeader(e)); } }1
2
3
4
5
6
7
8
11、获取正文-表单数据相关。
——为了做这个实验,得需要一个login.html页面,上面写个表单,然后提交到我们的Servlet去处理,我们在Servlet中获得表单里面的数据。
<form action="/Day01_RequestResponse/servlet/RequestDemo1" method="post"> 用户名:<input type="text" name="username" /><br/> 性别:<input type="radio" name="gender" value="male" />男 <input type="radio" name="gender" value="female" />女<br/> 爱好:<input type="checkbox" name="hobby" value="sing" />唱歌 <input type="checkbox" name="hobby" value="dance" />跳舞 <input type="checkbox" name="hobby" value="sleep" />睡觉<br/> 城市:<select name="city"> <option>---请选择---</option> <option value="bj">北京</option> <option value="sh">上海</option> <option value="cq">重庆</option> </select><br/> <input type="submit" value="提交" /> </form>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class RequestDemo1 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 网页使用的什么编码,右击查看源代码即可知道。网页一般默认是UTF-8,,而我们服务器默认的编码是ISO-8859-1,所以遇到中文的话,我们在Console中就变成问号?了 // 所以我们要请求告诉服务器用UTF-8编码 request.setCharacterEncoding("UTF-8"); System.out.println(request.getParameter("username")); System.out.println(request.getParameter("gender")); // 爱好可能是0个,也可以是多个 String[] hobbys=request.getParameterValues("hobby"); for(int i=0;(hobbys!=null)&&i<hobbys.length;i++){ System.out.print(hobbys[i]+"\t"); } System.out.println(); System.out.println(request.getParameter("city")); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
输出结果是:
张三 male dance sleep sh1
2
3
4
——一下子获得所有表单名names,然后对这个枚举里的name一个个获得对应的值,如下代码:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置字符 request.setCharacterEncoding("UTF-8"); // 获取所有表单的names,然后while遍历 Enumeration names=request.getParameterNames(); while(names.hasMoreElements()){ String name=(String) names.nextElement(); // 为了兼容,统一用数组来接收,只有一个值的就遍历1次 System.out.println("表单name是:"); System.out.println(name); System.out.println("对应的值是:"); String[] values=request.getParameterValues(name); for(int i=0;values!=null&&i<values.length;i++){ System.out.println(values[i]); } } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Console输出结果是:
表单name是: username 对应的值是: 李四 表单name是: gender 对应的值是: female 表单name是: hobby 对应的值是: sing dance 表单name是: city 对应的值是: sh1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
——重点来了。我们获取表单里面的内容主要是为了写入数据库的,也就是说赋值给我们的一个类,比如这里我们创建一个User类,把上面提交过来的信息赋值给这个User类。
——我们创建了一个User类,放在src下的com.hello.entity下。这个User类的几个属性需要和form的name一样。并实现getter、setter和toString方法(用于输出对象的信息)。
——我们在Servlet中做了处理,用getParameterMap方法获得所有表单的数据,并把这个数据放到User对象中。这里有个新知识点是PropertyDescriptor 需要了解。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { // 设置字符 request.setCharacterEncoding("UTF-8"); // 获得表单全部数据 Map<String,String[]> map=request.getParameterMap(); // 下面的额方法主要是为了取得里面的数据并赋值给User类的对象 User u=new User(); System.out.println("赋值前的u:"+u); // for(Map.Entry<String, String[]> m:map.entrySet()){ String name=m.getKey(); String[] values=m.getValue(); // 创建一个属性描述器 PropertyDescriptor pd=new PropertyDescriptor(name, User.class); // 自动生成左边变量的快捷键是先按Ctrl+2,松开后按L即可 Method setter = pd.getWriteMethod(); if(values.length==1){ setter.invoke(u, values[0]); }else{ // 强制转换成Object是防止values被拆分,我们需要它作为一个整体赋值过去 setter.invoke(u, (Object)values); } } System.out.println("赋值后的u:"+u); } catch (Exception e) { e.printStackTrace(); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
输出结果:
赋值前的u:User [username=null, gender=null, hobby=null, city=null] 赋值后的u:User [username=王五, gender=female, hobby=[sing, dance], city=sh]1
2
——当然,你说本来整个getParameterMap方法可以一下子获得表单的全部数据并得到一个Map类型值,但这提取数据赋值的过程也太复杂了。其实,我们这个方法主要是配合框架使用的。什么意思?就是框架里面已经帮我们写好了这些赋值操作,我们只需要用getParameterMap方法得到一个Map值即可。
——把这两个jar包放在lib中,主要是beanutils包,logging包是日志作用。
——然后使用BeanUtils.populate方法,第一个参数是接受数据的对象,第二个数据就是Map值,我们通过getParameterMap方法拿到即可。就这么简单。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { // 设置字符 request.setCharacterEncoding("UTF-8"); // 接收数据的类和对象 User u=new User(); System.out.println("赋值前的u:"+u); // 配合框架 BeanUtils.populate(u, request.getParameterMap()); System.out.println("赋值后的u:"+u); } catch (Exception e) { e.printStackTrace(); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
输出结果是:
赋值前的u:User [username=null, gender=null, hobby=null, city=null] 赋值后的u:User [username=小鬼, gender=male, hobby=[sing, dance, sleep], city=cq]1
2
12、字节流形式获得表单数据。主要方法是getInputStream。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletInputStream sis = request.getInputStream(); byte[] b=new byte[1024]; int len=0; while((len=sis.read(b))!=-1){ System.out.println(new String(b, 0, len)); } }1
2
3
4
5
6
7
8
9
输出结果是(字符编码问题暂未解决):
username=%E5%93%88%E5%93%88&gender=female&hobby=dance&hobby=sleep&city=bj1
13、请求转发和获取非表单数据的方法。
——获取非表单数据,其实就是利用了request的setAttribute和getAttribute方法。所以区别在于非表单数据都是通过getAttribute获取的,表单数据都是通过getParameter*获取的。
——在RequestDispatcherDemo1里:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("A:我办不了,帮你转发吧!"); request.setAttribute("name", "eric"); // 这里的"/"表示应用根目录,所以不需要写应用名了,直接写servlet/***即可 request.getRequestDispatcher("/servlet/RequestDispatcherDemo2").forward(request, response); System.out.println("A:办好了吧,B刚刚返回给我了!"); }1
2
3
4
5
6
7
8
——在RequestDispatcherDemo2里:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println(request.getAttribute("name")); System.out.println("B:,我办好了!"); }1
2
3
4
5
——访问“http://localhost:8080/Day01_RequestResponse/servlet/RequestDispatcherDemo1”时,Console有输出结果,注意转发和重定向的不同,转发的url是不会跳转至“http://localhost:8080/Day01_RequestResponse/servlet/RequestDispatcherDemo2”的,因为它就是1次访问,另外一个是Servlet帮我们转发的,不需要浏览器发起请求。
A:我办不了,帮你转发吧! eric B:,我办好了! A:办好了吧,B刚刚返回给我了!1
2
3
4
——总结一下重要知识点:
重定向是response下面的方法,转发是request下面的方法;
转发不可以跳转到其他领用,重定向可以跳转至其他应用,如下。因为转发最终的转发路径是
应用名/**,所以如果你直接写”http://www.baidu.com“的话,它的转发路径就是
应用名/servlet/http://www.baidu.com,所以出现404。我们转发写“/servlet/RequestDispatcherDemo2”,所以最终转发路径是“/应用名/servlet/RequestDispatcherDemo2”。所以可见转发只能在同一个应用下进行转发。
response.sendRedirect("http://www.baidu.com");1
重定向在浏览器中的URL是变化的,而转发是不变的。
重定向当然也可以利用request的setAttribute设置数据,但是没有用,因为重定向的第二次请求是一个全新的request,根本和第一次携带数据的request不是同一个,所以根本拿不到数据。
14、请求包含。其实本质上就是把另一个文件包含到当前文件里来,最显著的特征就是它们可以利用request传递数据了,因为都包含到同一个地方来了,相当于同一个文件了,request也是同一个,所以可以传递数据。
——在RequestDispatcherDemo1中:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("A:我办不了,帮你转发吧!"); request.setAttribute("name", "eric"); request.getRequestDispatcher("/servlet/RequestDispatcherDemo2").include(request, response); System.out.println("A:办好了吧,B刚刚返回给我了!"); }1
2
3
4
5
6
7
——在RequestDispatcherDemo2中:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println(request.getAttribute("name")); System.out.println("B:,我办好了!"); }1
2
3
4
5
输出结果是:
A:我办不了,帮你转发吧! eric B:,我办好了! A:办好了吧,B刚刚返回给我了!1
2
3
4
15、另外一个技巧,request.setCharacterEncoding方法只能解决post请求的编码问题,不能解决get请求,比如我们把form表单的方法改成get的话,request.setCharacterEncoding就不起作用了。那么解决办法就是,获取每个表单值之后,进行手动转码,比如:
String username=request.getParameter("username"); // 先用服务器默认处理的方式原路获得数据,然后再转化成UTF-8 username=new String(username.getBytes("ISO-8859-1"),"UTF-8");1
2
3
相关文章推荐
- 【JavaWeb-6】HttpServletResponse的字符字节输出流、编码、文件下载、Captcha图片与HttpServletRequest获取request数据表单非表单数据、重定向与转发
- Response对象、HttpServletResponse简介、处理字节编码问题、实现动态文件下载
- javaweb-day06-1 (Servlet - Response - 输出中文、输出1、中文名字的文件下载、字符流读图片会导致数据丢失的剖析)
- Response对象、HttpServletResponse简介、处理字节编码问题、实现动态文件下载
- HttpServletResponse.sendRedirect方法实现的请求重定向与RequestDispatcher.forward方法实现的请求转发的总结比较
- JAVAWEB开发之HttpServletResponse和HttpServletRequest详解(上)(各种乱码、验证码、重定向和转发)
- servlet获取表单提交的数据、请求转发、请求包含、请求重定向
- JAVAWEB开发之HttpServletResponse和HttpServletRequest详解(上)(各种乱码、验证码、重定向和转发)
- HttpServletResponse response(中文乱码、文件下载、定时刷新、控制缓存、重定向、注意事项)
- Servlet页面跳转技术--重定向httpServletResponse.sendRedirect和转发RequestDispatche
- 请求重定向与请求转发的比较(HttpServletResponse.sendRedirect方法和RequestDispatcher.forward方法)
- Java文件下载:如何编码文件名称以及如何设置HttpServletResponse
- JAVAWEB开发之HttpServletResponse和HttpServletRequest详解(下)(各种乱码、验证码、重定向和转发)
- 南哥带你学 Java 之 JavaEE 设置全局配置、请求转发、请求重定向、HttpServletResponse 和 HttpServletRequest
- HttpServletResponse response(中文乱码、文件下载、定时刷新、控制缓存、重定向、注意事项)
- 转发 重定向HttpServletRequest & HttpServletResponse
- 请求重定向与请求转发的比较(HttpServletResponse.sendRedirect方法和RequestDispatcher.forward方法)
- Servlet 3 HttpServletRequest HttpServletResponse 验证码图片 form表单
- 文件重定向,getline()获取一样,屏幕输出流,格式控制符dec,oct,hex,精度控制setprecision(int num),设置填充,cout.width和file(字符),进制输入
- 在Weblogic Web Service中获取 HttpServletRequest/Response 对象