一.response.
1.response.characterEncoding和response.setContentType("text/html;charset=UTF-8"),response.setCharacterEncoding是设置服务器端输出的编码,而response.setContentType(),既指定了 服务器端的编码,又指定了浏览器端的编码.
2.常见的方法:
response.setStatus(int)://设置状态码,通常需要和响应头一起结合.例如302状态码和location一起结合等
response.setHeader/response.addHeader://当添加的两个响应头的键相同时,前者会覆盖,后者保留两个响应头.
response.setHeader("Refresh","2;URL=http://www.baidu.com");//设置响应头,2s后转向指定的URL
response.setHeader("Refresh","2");//每隔两秒刷新一次..
//下面的三个方法设置禁止浏览器使用缓存
response.setDateHeader("Expires",0);
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
3.在浏览器中模拟响应头(meta标签)
<meta http-equiv="Expires" content="0">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
注意web服务器发送的响应头字段的优先级比meta标签要高
4.response.getWriter方法和getOutPutStream方法:
1.两个方法不能同时使用.
2.response.getWriter方法返回的是PrintWriter对象,而response.getOutPutStream方法返回的是ServletOutPutStream对象.
3.可以用getOutPutStream方法输出字符,但是要先转化为字节数组,比较麻烦.
几个小细节:
1.response.getWriter()在一个service方法中进行多次调用,返回的是同一个对象,而response.getOutPutStream也一样.
2.response.getWriter()返回的PrintWriter的print方法和write方法几乎一样,print方法的底层实际上调用的就是write方法,唯一的区别就是如果要写入的字符为null,write会报异常,而print方法会写出null这个字符串
二.Request
1.如果在请求体中和get请求中包含同样的键,那么get请求中的值将会把请求体的值(post请求)给覆盖.
2.request.getParameter()方法一些需要注意的地方:
1.如果传递的参数不存在对应的键值对,返回null
2.如果传递的参数存在对应的键,但是键里没有赋值,那么返回空字符串
3.如果传递的参数存在对应的键,但是该键对应多个值,那么返回第一个匹配的字符串.想要将一个键对应的两个值都获取到用request.getParameterValues(name)
3.request.getRequestURI和request.getContextPath:
有一个URL:http://localhost:8080/day15_request1/DemoServlet1?username=14
request.getRequestURI:获取请求行中的资源部分,即位于URL的主机和端口之后,参数之前的部分.对上面的例子:/day15_request1/DemoServlet1
Request.getContextPath:获取用于获取请求请求URL中属于web应用程序的路径./day15_request1
4.request.getHeader(String name):获取指定名称的请求头,如果有多个获取第一个
request.getHeaderNames():获取所有请求头的键的枚举
request.getHeaders(String name):获取指定名称的所有的请求头,返回的是枚举,记录了一个键所对应的多个值
三.请求包含和请求转发
1.请求包含
有以下几个细节需要注意:
1>.服务器不会去调整HttpServletRequest对象,因此HttpServletRequest对象依然保持的是初始的参数路径和信息.被包含者调用程序检测当前访问路径的时候,依然得到的是调用者的路径.
2>.被包含的如果是servlet程序,不能改变相应的状态码和状态头,如果它里面存在这样的语句,语句的执行结果将被忽略
3>.可以通过request.getQueryString获得整个查询参数,但是需要注意的是,如果在浏览器请求某个servlet的时候,没有携带参数,就算包含的时候携带了参数,这个方法也会返回null.但是如果在请求转发的getRequestDispatcher方法的括号内有参数,依然可以通过request.getParameter方法获取
4>.如果包含的是静态页面,Tomcat的缺省servlet会检查当前HttpServletResponse对象是否已经调用getWriter方法返回对象,如果已经调用就使用PrintWriter的write方法写出静态html的内容,如果没有调用,则会调用getOutPutStream方法的write方法直接写出到缓冲区中.因此就会存在一个问题:下面的代码会报错:
getServletContext().getRequestDispatcher("/a.html").forward(request,response);
response.getWriter().write("你在干嘛");
这是因为在缺省servlet中已经调用了getOutputStream了,而在这里又调用了getWriter方法,两个方法是冲突的.
此外输出html文件的时候可能会有意想不到的乱码问题.如果在请求转发到静态页面之前调用了getWriter方法,且没有指定content-type,那么就会用ISO8859-1对静态页面进行编码输出(这样子可能会乱码),否则直接将静态页面用流输出.
5>.RequestDispatcher接口封装了某个路径所指定的资源对象.注意:WEB-INF目录对RequestDispatcher接口是可见的.
6>.对于request.getRequestDispatcher()方法,如果请求转发的是html就读取文档发送到客户端,而如果请求转发的是jsp,就读取相应结果发送到客户端.因此,相当于缓冲区已经清空,结果已经提交给客户端,所以之后不能再调用include或者sendRedirect方法,这样会报错.
7>.RequestDispatcher对象只能包装当前web应用程序中的资源,所以forward和include方法只能实现一个web应用程序之间的转发请求和资源包含.
8>.在包含的时候,如果同时调用response.getWriter方法,实际上返回的对象是一样的,可以通过直接打印观察出来.
9>.请求包含jsp文件存在的问题:
1>>.乱码.如果不在请求的servlet处设置编码,那么jsp一定会发生乱码,因为jsp是动态资源,而且jsp中页面的内容是用response.getWriter写出的.因此为了防止jsp文件乱码,必须要在包含的servlet处设置编码!!!而html文件则不存在这种问题,因为DefaultServlet会直接将test.html文件按字节流原封不动的读取出来,再按字节流原封不动的写出.在客户端,会直接根据<meta>标签进行解析.
2>>.冲突.上面说过了JSP文件是调用response.getWriter()方法输出页面的,因此如果在包含的servlet处调用了response.getOutputStream方法,就会报错.
3>>jsp与普通静态页面的区别在于普通静态页面在请求的servlet处可以调用getOutPutStream方法,它会直接用字节流的形式进行输出(此时页面不能有getWriter方法)但是jsp只以getWriter形式输出,因此在转发jsp的时候,转发页面一定不能用getOutputStream方法.
2.请求转发.
1>.调用requestDispatcher().forward方法时,servlet容器将根据目标资源路径对当前HttpServletRequest对象中的请求路径和请求参数进行调整.例如下面的示例:
@WebServlet(name = "ForwardTestServlet", urlPatterns = "/ForwardTestServlet")
public class ForwardTestServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setDateHeader("Expires", 0);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma","no-cache");
response.setContentType("text/html;charset=UTF-8");
request.getRequestDispatcher("/ForwardTestServlet2?name=hlhdidi").forward(request, response);
}
@WebServlet(name = "ForwardTestServlet2", urlPatterns = "/ForwardTestServlet2")
public class ForwardTestServlet2 extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String uri=request.getRequestURI();
StringBuffer url=request.getRequestURL();
String query=request.getQueryString();
response.getWriter().write("uri="+uri+"<br>");
response.getWriter().write("url="+url+"<br>");
response.getWriter().write("query="+query);
}
输出结果:
如果是请求包含,将ForwardTestServlet中的forward改为Include,结果如下:
可以看出于请求包含对请求头不做出修改不同,请求转发对请求行做出了修改,也可以获取查询的参数了.
2.forward一个静态资源时候的过程:
如果在访问一个静态资源转发前,调用了getWriter方法,那么直接用getWriter的write方法写出这个静态资源,步骤是先将资源中的内容转化为字符文本后再输出.
如果没有调用,直接将静态资源的流传出去.(涉及getOutputStream方法)
3.在调用forward方法之前写入缓冲区的数据将会在调用forward方法的时候被清空,而在调用完forward方法之后写入的数据,将被忽略.这里注意,缓冲区清空这种情况只出现在请求转发中,而请求包含,在调用Include方法前和方法后都可以继续写数据且在页面正常相应.
4.在调用者程序中设置的响应状态码和响应头都不会忽略,而在被调用者程序中设置的响应状态码和响应头也不会忽略(和请求包含不同).被调用者相同的数据会把调用者的数据覆盖.
四.请求重定向和请求转发的比较
1.请求转发只能将请求转发给同一个web应用的组件而请求重定向可以转发到同一个站点上的其他web应用资源
2.请求重定向会改变浏览器显示的URL,而请求转发不会
3.请求转发调用者和被调用者共享同一个request和response,而请求重定向调用者和被调用者分别有自己的request和response.
4.无论是请求转发还是请求重定向都不能有内容已经被实际输出到了客户端.