JavaWeb学习篇之----容器Request详解
前篇说到了Response容器对象,这篇我们就来看一下Request容器对象,之前也说过了,这个两个容器对象是相对应的,每次用户请求服务器的时候web容器就会给创建这对容器对象,他们是共存亡的,当然Request除了有一个容器对象的角色,他还有一个角色就是Request域,我们之前在讲解Servlet的时候,说到一个ServletContext域,这个域的范围是整个web应用,这里的Request域的范围就小了,他只是一次用户的请求内,即用户发送一个请求的时候,Request创建,当用户关闭这次请求的时候Request就会消亡的。
下面就来看一下Request的相关方法:
getContextPath():这个方法返回的是web应用映射的虚拟目录地址:如ServletDemo应用的虚拟目录是:/ServletDemo
getCookies():这个方法返回的是一个Cookies[],我们在response容器那篇文章中看一个方法是向response容器中存入一个cookie的,这个方法是从Request容器中拿取多个cookies,因为用户在请求的时候会携带很多的cookie,关于Cookie的相关知识,我们会在后面的文章中进行详解
getHeader(String name)/getIntHeader(String name)/getDateHeader(String name):这些方法是获取请求头信息的,只是针对不同的类型的,有字符串类型的,时间类型,数值类型的
getHeaderNames():这个方法是获取所有请求头的字段名称
getHeaders(String name):这个方法是获取一个请求头字段的所有值,因为有时候可能会有相同请求头字段信息,不会覆盖的
getMethod():这个方法是获取客户机的请求方法
getQueryString():这个方法是获取用户请求时的查询参数的,即url后面携带的参数,如:http://localhost:8080/ServletDemo/ServletRequest?username=jiangwei&password=123456,那么getQueryString()方法返回来的值就是username=jiangwei&password=123456
getRequestSessionId():这个方法是获取客户机在请求的时候携带的sessionid值,有关session的相关知识,后面会详解
getRequestURL():这个方法是获取客户机请求的url
getServletPath():这个方法返回的是请求的Servlet的映射路径,比如:ServletRequest映射的是是/ServletRequest
getServerName()/getServerPort():这两个方法是获取服务器的名称和端口号,比如localhost,8080
getSession()/getSession(boolean mode):这两个方法是获取一个session对象,相关之后在session篇会说到
getAttribute(String name):这个方法是从Request域中获取值
getAttributeNames():这个方法是获取Request域中获取所有的字段名称
getParameter(String name):这个方法是获取用户使用get/post方式携带的参数值
getParameterNames():这个方法是获取用户请求时携带的所有参数名称
getParameterMap():这个方法是获取用户请求时携带的参数名称和参数值,并将其组装成一个Map对象
getParameterValues():这个方法是获取用户请求携带的参数值,因为有时候一个参数名称可能对应多个值
setAttribute(String name,Object value):这个方法是设置Request域中的属性值
removeAttribute(String name):这个方法是删除Request域中的属性值
getInputStream()/getReader():这个方法是获取用户请求的时候上传的输入流,比如我们在处理用户上传文件的时候。需要用到这个输入流
setCharacterEncoding(String name):这个方法是设置Request容器的编码
getRemoteAddr()/getRemoteHost():获取客户机的IP地址和主机名
getProctocol():获取协议名称
getRequestDispatcher(String path):获取一个转发对象RequestDispatcher,进行转发操作
下面就通过实例来看一下上面方法的使用:
public void test1(HttpServletRequest request) throws Exception{
//有时候可能有多个name
String[] nameAry = request.getParameterValues("username");
//在获取用户的请求数据的时候先要进行判断数据的有效性,然后再去使用,提高应用的健壮性
if(nameAry != null){
System.out.println("getParameterValues方法");
System.out.println("---------------------");
System.out.println("参数名称:username");
for(int i=0;i<nameAry.length;i++){
System.out.println(nameAry[i]+",");
}
System.out.println();
} System.out.println("getParameterMap方法");
System.out.println("------------------");
Map<String,String[]> map = request.getParameterMap();
if(map != null){
Set<Entry<String,String[]>> set = map.entrySet();
Iterator<Entry<String,String[]>> iterator = set.iterator();
while(iterator.hasNext()){
Entry<String,String[]> entry = iterator.next();
System.out.println("参数名:"+entry.getKey());
String[] values = entry.getValue();
if(values != null){
for(int i=0;i<values.length;i++){
System.out.print(values[i]+",");
}
System.out.println();
}
}
}
System.out.println(); System.out.println("getParameterNames()方法");
System.out.println("----------------------");
Enumeration names = request.getParameterNames();
if(names != null){
while(names.hasMoreElements()){
String name = (String) names.nextElement();
System.out.println("参数名:"+name);
System.out.println("参数值:"+request.getParameter(name));
}
} }
这里我还需要设计一个demo.jsp,在里面设计一个表单进行数据的上传:
<form action="/ServletDemo/ServletRequest" method="post">
用户名1:<input type="text" name="username"/><br/>
用户名2:<input type="text" name="username"/><br/>
密码:<input type="text" name="password"/><br/>
<input type="submit" value="提交"/>
</form>
我们传递了两个参数名称为:username的字段,我们在浏览器中输入:http://localhost:8080/ServletDemo/demo.jsp,然后打印结果:
getParameterValues方法
---------------------
参数名称:username
aaa,
bbb,
getParameterMap方法
------------------
参数名:username
aaa,bbb,
参数名:password
123,
getParameterNames()方法
----------------------
参数名:username
参数值:aaa
参数名:password
参数值:123
下面我们再来看一下request的乱码问题:
我们还是直接使用demo.jsp中的方式传递参数,当我们在页面文本框中输入"中国"
在控制台中打印获取到的username的值,显示的是??,这个是因为Request域中的采用的是ISO8859-1码表的,而我们的demo.jsp使用的是utf-8编码的,所以当我们点击提交的时候,浏览器会将"中国"使用utf-8码表编码,然后web容器创建一对request/response容器对象,数据传入到request容器中,因为request容器使用的是iso8859-1编码的,所以当我们在Servlet中从request容器中读取数据,使用的是iso8859-1进行解码的,所以会出现乱码了,所以我们只需要将request的容器码表设置成和我们页面显示的码表一样就可以了。这样我们在getParameter的时候得到正确的解码(utf-8)数据
request.setCharacterEncoding("utf-8");
String name = request.getParameter("username");
System.out.println("username:"+name);
这时候就可以了,就能够正常显示了。
但是问题还没有结束,以上说到的乱码问题是在使用post方式传递的数据,下面我们在来看一下使用get方式传递数据的乱码问题,
昨天突然发现request.setCharacterEncoding("UTF-8")这句代码失效,前后台编码统一都是UTF-8,但通过request.getParameter("name")接收到的表单数据依然乱码,后来发现原因是表单的提交方式没有设置,也就是采用了默认的GET方式提交。
那为什么GET方式会出现问题?难道request.setCharacterEncoding("UTF-8")这句代码只对POST方式提交数据才有效?
做了一些测试之后总结出了一点规律:
1、web浏览器对页面上通过GET方式提交的数据会进行URL编码,采用的编码方式通常由html页面上
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
这个标签内的charset所指定的编码方式决定(前提是:没有自定义浏览器发送数据的编码设置)。比如现在charset="UTF-8",那么当采用get方式提交表单的时候,web浏览器默认会采用UTF-8的编码方式对数据进行编码。
所以,当请求的URL如下时:
http://localhost:8080/test/TestServlet?name=中国
其实真实内容是这样:
http://localhost:8080/test/TestServlet?name=%E4%B8%AD%E5%9B%BD
其中%E4%B8%AD%E5%9B%BD是对‘中国’二字采用了UTF-8的URL编码之后产生的字符串。
2、所以,当数据被发送到Web服务器上的时候(测试使用tomcat6),服务器要做的一件事就是解码%E4%B8%AD%E5%9B%BD这个字符串。那么如何解码这个字符串?
3、在JDK的java.net包下面有一个类叫做URLDecoder,该类即可对URL编码之后的字符串进行解码,如:
URLDecoder.decode("%E4%B8%AD%E5%9B%BD","UTF-8");
返回解码之后的字符串,第二个参数是指采用何种字符集解码"%E4%B8%AD%E5%9B%BD"这个字符串。打印以上代码成功显示“中国”二字,说明解码正确!
4、前面已经说过了:web服务器会自行解码%E4%B8%AD%E5%9B%BD这个字符串,但是我们通过request.getParameter("name")得到的却是乱码,所以问题一定出在web服务器在解码E4%B8%AD%E5%9B%BD这个字符串的时候采用的字符集不对。
5、经过测试发现,web服务器(只测试了tomcat6)对GET方式的数据提交采用的解码字符集是"ISO-8859-1",所以web服务器其实是这样解码的:
URLDecoder.decode("%E4%B8%AD%E5%9B%BD","ISO-8859-1");
因此:明显服务器的解码方式是不对的,因为编码采用的是UTF-8,而解码却用的ISO-8859-1。
6、所以,request.getParameter("name")返回的是用ISO-8859-1解码的字符串,那么必然是乱码了!
那么如何获得正确编码的字符串?可以采用以下的方式:
String a = new String(request.getParameter("name").getBytes("ISO-8859-1"),"UTF-8");
总结:因为GET方式提交数据会被浏览器进行URL编码,而tomcat服务器会采用了错误的解码方式进行解码,所以得的是乱码。而POST方式提交的数据不会被浏览器进行URL编码,所以服务器直接按照request.setCharacterEncoding("UTF-8")
所指定的编码方式解析字符串,因此在POST方式下request.setCharacterEncoding("UTF-8")是好用的!
这里的解决方式有两种,
一种:是通过String类的getBytes方法进行编码转换,具体java代码是:
new String(request.getParameter(“name”).getBytes(“iso-8859-1”),“客户端编码方式”)
这里的客户端编码方式就是页面的编码,比如demo.jsp中使用的是utf-8
第二种:在服务器server.xml代码中改配置信息:
<Connector port="8080"protocol="HTTP/1.1" maxThreads="150" connectionTimeout="20000"
redirectPort="8443"URIEncoding="客户端编码"/>
这样我们就修改了Tomcat中的编码和解码字符集了。当tomcat获取客户机使用get方式带来的数据使用URL解码的字符集
当然我们之后修改server.xml之后需要重启服务器的,所以第二种方式是不赞同使用的。
上面我们就讲述了如何解决request的乱码问题
下面我们再来看一下请求转发的问题:
对于重定向和转发我们这里就不做太多的介绍了,之前不知道说了好多遍了,我们之前说过ServeltContext也是可以得到一个转发对象RequestDispatch的,其实Request也是可以得到一个RequestDispatch对象的,我们还是来做个例子,通过一个servlet转发到另一个servlet:
Servlet1代码:
public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
//在request域中存入一个属性值,然后转发到Servlet2中进行读取
request.setAttribute("data", "Hello Servlet2");
request.getRequestDispatcher("/Servlet2").forward(request, response);
}
我们在request域中存入data属性,然后在Servlet2中读取出来进行显示:
Servlet2代码:
public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
//获取属性值
String data = (String) request.getAttribute("data");
//打印
response.getWriter().write(data);
}
运行结果:
我们取出来了属性data的值,显示成功了,但是我们记得在之前说的ServletContext域中存入了data属性值,那个也是可以读取的,但是那时候我们说过那种方式是不可靠的,因为ServletContext是全局的,整个web应用都是有效的,所以可能发生数据错乱的情况,比如现在一个人去请求servlet1,就在要进行转发到servlet2的时候,这时候又来一个人去请求servlet1,这时候他也去设置data属性值,因为ServletContext是全局的,所以第一个人在servlet2中读取的可能是第一个人在servlet1中存入的属性值,那样数据就乱了,所以我们只能使用request域了,而且转发都是在一个request域中的,当多个用户来访问servlet1的时候,是有多个request域的,所以互相是不会干扰的,这样数据也是不会乱的,所以说在使用转发技术的时候使用request域存数据是可靠的,而不是用ServletContext域。
下面在来看一下使用转发技术的时候我们需要注意的问题:
当我们在使用转发的时候不能将response流关闭了,不然会报错的,即在request.getRequestDispatcher("index.jsp").forward(request,response);这行代码前不能将response.getOutputStream流关闭。
//关闭流
response.getOutputStream().close();
//进行转发
RequestDispatcher rd = request.getRequestDispatcher("/demo.jsp");//不是使用ServletContext的
rd.forward(request, response);
异常:
这个关闭流操作是很明显的,下面我们在看一下一个不明显的,也是最容易犯错的:
request.getRequestDispatcher("/demo.jsp").forward(request, response);
//转发完之后再去转发
request.getRequestDispatcher("/index.jsp").forward(request, response);
从代码中我们可以看到是在一次转发之后,在通过一次转发,这个报错的原因和上面的是一样的,因为当一个转发之后,response就是想浏览器输出数据了,当输出完数据之后,response就会自动的关闭流,所以会报和上面的错误,这种错误是经常犯的,因为我们有时候会在不同的页面或者servlet中进行转发,这样就可能造成这种错了,所以我们解决这种问题就是:
在每次调用转发代码之后一定要记得添加一句代码:return;
这样后续的代码就不会执行了,所以就不会有这种错误了,同样的前面说到的重定向也是这样的问题,为了我们在重定向之后,后续的代码不在执行,所以必须添加一句代码:return;
关于转发还有一个问题就是,在使用转发技术的时候,会冲掉response中已写入的数据:实例代码如下:
//在调用转发之前向response中写入的数据,会被冲掉
String data = "aaaaaa";
response.getWriter().write(data);
request.getRequestDispatcher("/demo.jsp").forward(request, response);
这样我们只能看到demo.jsp页面,而看不到"aaaaaa"数据了,因为他被冲掉了。
最后我们在看一下在web应用中怎么书写各种路径:
规则:
写任何地址都是使用斜杠开头:如果是写给服务器用的这个"/"就代表当前web应用,如果是写给浏览器用的:这个"/"是指当前网站
浏览器用的:客户机需要这个地址去请求服务器
服务器用的:服务器本身用的
实例:
//1:写给服务器用的
request.getRequestDispatcher("/form1.html").forward(request, response); //2:写给浏览器的
response.sendRedirect("/ServletDemo/form1.html"); //3:写给服务器用的
this.getServletContext().getRealPath("/WEB-INF/form1.html"); //4:写给服务器用的
this.getServletContext().getResourceAsStream("/form1.html"); //5:写给浏览器用的
/**
* <a href="/ServletDemo/form1.html">点点</a>
*/ //6:写给浏览器用的
/**
* <form action="/ServletDemo/form1.html">
* </form>
*/
JavaWeb学习篇之----容器Request详解的更多相关文章
- JavaWeb学习篇之----容器Response详解
今天在来看一下Response容器的相关知识,其实这篇blog早就应该编写了,只是最近有点忙,所以被中断了.下面我们就来看一下Response容器的相关知识吧.Response和我们即将在后面说到的R ...
- JavaWeb学习篇之----HTTP协议详解
简介: HTTP是hypertexttransfer protocol(超文本传输协议)的简写,它是TCP/IP协议的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的过程. HTTP协 ...
- JavaWeb学习篇之----EL表达式详解
我们之前的几篇文章中都提到了一个EL表达式,那么这个EL表达式到底是什么东东呢?为什么用处那么大,下面我们就来看看EL表达式的相关内容 EL表达式简介: EL 全名为Expression Langua ...
- JavaWeb学习----JSP内置对象详解
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...
- [转载]JavaEE学习篇之——JQuery技术详解
原文链接:http://blog.csdn.net/jiangwei0910410003/article/details/32102187 1.简介2.工具3.jQuery对象 1.DOM对象转化成j ...
- J2EE学习篇之--JQuery技术详解
前面我们讲解了的J2EE的技术都是服务端的技术,下面我们来看一下前端的一些开发技术,这一篇我们来看一下jQuery技术 简介: jQuery由美国人John Resig创建,至今已吸引了来自世界各地的 ...
- J2EE学习篇之--Spring技术详解
今天我们来看一下Spring的相关知识,我们知道Spring也是一个开源的框架,这个框架并不像是Struts一样,这个框架是可以用在Java的所有地方,所以,其实开发Android的时候我们也是可以使 ...
- J2EE学习篇之--Struts2技术详解
前面说到了Struts1的相关知识,下面来说一下Struts2的相关知识,我们知道现在Struts2使用的比Struts1多,Struts2已经替代Struts1成为主流的框架了... 摘要 Stru ...
- JavaWeb学习篇之----自定义标签&&JSTL标签库详解
今天来看一下自定义标签的内容,自定义标签是JavaWeb的一部分非常重要的核心功能,我们之前就说过,JSP规范说的很清楚,就是Jsp页面中禁止编写一行Java代码,就是最好不要有Java脚本片段,下面 ...
随机推荐
- 八、结构模式之组合(Composite)模式
组合模式属于对象的结构模式,有时又叫做部分-整体模式,组合模式将对象组织到树结构中,可以用来描述整体与部分的联系.其可以使客户端将单纯元素和组合元素同等对待. 当需求中是体现部分与整体层次的结构时,以 ...
- Android——谷歌官方下拉刷新控件SwipeRefreshLayout(转)
转自:http://blog.csdn.net/zouzhigang96/article/details/50476402 版权声明:本文为博主原创文章,未经博主允许不得转载. 前言: 如今谷歌推出了 ...
- 利用Process类创建多个子进程对象执行任务,主进程负责调度
import time from multiprocessing import Process def run1(): for i in range(5): print("sunck is ...
- Qt【Could not parse stylesheet of object 0x7f7990 】
查找自己所写的 setstylesheet(); 然后看里面的括号标点什么的有没有多余的,删除即可解决.
- Scala(一)基础
OOP 面向对象编程 AOP 面向切面编程 FP 函数式编程 编程语言都要定义变量,一些代码是用来注释的,变量和变量之间有一些关系,要做一些运算,运算离不开流程控制,进行运算的数据往往来自数据结构,最 ...
- CodeFirst命令
CodeFirst get-help entityFramework NuGet命令 Add-Migration Adds a new mig ...
- bootstrap相关使用
如果想做成响应式效果,使用栅格系统产生 小于768手机端 : col-xs- 大于768小于992平板 : col-sm- 大于992小于1200 : col-md- 大于1200 : col-lg- ...
- git ,报403错误,完美解决方案
首先命令行操作结果如下: root@zhiren-PowerEdge-T110-II:/zrun# git clone https://git.coding.net/xxxxxxxx/xxxx.git ...
- Flink Kafka Connector 与 Exactly Once 剖析
Flink Kafka Connector 是 Flink 内置的 Kafka 连接器,它包含了从 Kafka Topic 读入数据的 Flink Kafka Consumer 以及向 Kafka T ...
- xStream.jar踩坑指南
前言 第一次接触Xstream,是在做一个socket通信的项目,由于是二次重新开发,所以有部分代码沿用了原来的代码(改造前用的webservice),其中xml字符串转换为对象,以及对象转换为xml ...