HttpServlet的response和request对象

来源:互联网 发布:淘宝是如何开直通车 编辑:程序博客网 时间:2024/05/16 11:08

我们知道servlet 通常通过 HTTP协来接收和响应来自 Web 客户端的请求。 因此我们在使用中最常用的是Servlet接口的实现类HttpServlet类。HttpServlet类的方法的主要参数是HttpServletRequest和HttpServletResponse对象。通过合理地使用这两个对象,我们可以实现很多有用的功能,例如在界面输出数据、文件下载等。

一、HttpServletResponse对象

HttpServletResponse是HttpServlet接口的实现类。HttpServletResponse代表响应对象,于是对应于Http协议响应消息中的状态码、响应头、实体内容都可以由它进行操作。具体使用的示例如下:

1、利用Response输出数据到客户端

调用HttpServletResponse对象的getOutputStream()和getWriter()分别可以得到输出字节流和字符流。在输出数据的时候我们要注意的是避免出现乱码。

response.getOutputStream().write("中文".getBytes());

输出数据,这是一个字节流,是什么字节输出什么字节,而浏览器默认用平台字节码打开服务器发送的数据,如果服务器端使用了非平台码去输出字符的字节数据就需要明确的指定浏览器编码时所用的码表,以防止乱码问题。
response.addHeader(“Content-type”,”text/html;charset=gb2312”);

response.getWriter().write(“中文”);

输出数据,这是一个字符流,response会将此字符进行转码操作后输出到浏览器,这个过程默认使用ISO8859-1码表,而ISO8859-1中没有中文,于是转码过程中用?代替了中文,导致乱码问题。可以指定response在转码过程中使用的目标码表,防止乱码。
response.setCharcterEncoding(“gb2312”);
其实response还提供了setContentType("text/html;charset=gb2312");方法,此方法会设置content-type响应头,通知浏览器打开的码表,同时设置response的转码用码表,从而一行代码解决乱码。

2.利用Response 设置 content-disposition头实现文件下载

设置响应头content-disposition为“attachment;filename=xxx.xxx”,文件将以附件的形式可以提供下载。 利用流将文件读取进来,再利用Response获取响应流输出。因为响应头中的附件名是不能设置为中文的,所以如果希望文件名为中文,一定要进行URL编码,编码所用的码表一定要是utf-8。实现代码示例:

    import java.io.FileInputStream;    import java.io.IOException;    import java.io.InputStream;    import java.io.OutputStream;    import java.io.UnsupportedEncodingException;    import java.net.URLDecoder;    import java.net.URLEncoder;    import javax.servlet.ServletException;    import javax.servlet.http.HttpServlet;    import javax.servlet.http.HttpServletRequest;    import javax.servlet.http.HttpServletResponse;    public class DownServlet extends HttpServlet {        public void doGet(HttpServletRequest request, HttpServletResponse response)                throws ServletException, IOException {            response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode("中文.jpg", "utf-8"));            InputStream in = new FileInputStream(this.getServletContext().getRealPath("1.jpg"));            OutputStream out = response.getOutputStream();            byte[]bs = new byte[1024];            int i = 0;            while((i=in.read(bs))!=-1){                out.write(bs,0,i);            }            in.close();        }        public void doPost(HttpServletRequest request, HttpServletResponse response)                throws ServletException, IOException {            doGet(request, response);        }      }

3.refresh头控制定时刷新

平时我们经常可以看到“恭喜您,注册成功!**秒后回到XX页面“的提示。那么这个是怎么实现的呢?其实Http响应头中有个Refresh响应头,可以通过设置它来实现页面定时刷新的功能。我们可以通过response对象设置响应头Refresh为一个数值,可以指定多少秒后刷新当前页面。例如设置响应头Refresh为 3;url=index.jsp,指定多少秒后刷新到index.jsp页面。

其实除了可以通过response对象来设置外,我们也可以在HTML中利用标签模拟响应头,将http-equiv设置为refresh,content设置为秒数来实现同样的功能。

4.控制是否缓存资源

不同版本的浏览器的缓存机制是不一样的,甚至同一个浏览器的不同版本的缓存机制也是不一样的。响应头中控制缓存的有expires、Cache-Control和Pragma三个配置。可以利用response设置来控制保证浏览器是否缓存资源。例如response设置expires响应头为0或-1,那么浏览器就不会缓存当前资源。(同样功能的头还有Cache-Control:no-cache、Pragma:no-cache)。如果希望浏览器能够缓存,可以用expires取值为一个时间来指定要缓存到的日期。

//设置不缓存的代码import java.io.IOException;import java.util.Date;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;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.setContentType("text/html;charset=utf-8");        response.getWriter().write("当前时间是:"+new Date().toLocaleString());    }    public void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}//设置缓存的代码import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class CacheServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)        throws ServletException, IOException {    response.setDateHeader("Expires", System.currentTimeMillis()+1000l*3600*24*30);    InputStream in = new FileInputStream(this.getServletContext().getRealPath("1.jpg"));    OutputStream out = response.getOutputStream();    byte[]bs = new byte[1024];    int i = 0;    while((i=in.read(bs))!=-1){        out.write(bs,0,i);    }    in.close();}public void doPost(HttpServletRequest request, HttpServletResponse response)        throws ServletException, IOException {    doGet(request, response);}

}

注意:设置expires缓存时间的时候,date是一个long型值,代表从1970年1月1日0点到当前时间的秒数,所以可以用system得到当前时间的秒数,然后加上缓存时间的毫秒数。此外保证它是long型值,否则默认是整型值,然后超过范围溢出后会变成负数。所以这里在1000后面有个小写的L表示它是long型值。

5.response实现请求重定向
通过response设置状态码为302,并设置响应头Location为重定向的地址,就可以实现请求重定向的操作。为了方便进行请求重定向操作,response也提供了response.sendRedirect(“…”)来实现请求重定向。

import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class RedirectServlet extends HttpServlet {    public void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {//      response.setStatus(302);//      response.setHeader("Location", "index.jsp");        response.sendRedirect("index.jsp");    }    public void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }}

在大部分情况下请求重定向和转发的效果是差不多的,这时候我们推荐使用转发,以减少对服务器的访问。而在某些情况下是需要使用转发的,目的往往是为了改变浏览器地址栏里的地址(如登录成功后转到主页),和更改刷新操作(如加入商品到购物车后转到购物车页面的操作)。

6.response使用的其他注意事项

1、getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。因为response的缓冲区中不能同时存储字节流和字符流,会报500错误。

2、Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。

3、Serlvet的service方法结束后,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎tomcat将调用close方法关闭该输出流对象。一般我们不用自己去关闭这个流,让servlet引擎自己去关闭。

二、HttpServletRequest对象

HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户的信息。

通过Request对象进行的常用操作主要包括四个:

  1. 获取客户机信息
  2. 获取请求头信息
  3. 获取请求参数
  4. 利用请求域传递对象

1、获取客户机信息
常用的方法如下:

getRequestURL方法返回客户端发出请求完整URLgetRequestURI方法返回请求行中的资源名部分getQueryString 方法返回请求行中的参数部分getRemoteAddr方法返回发出请求的客户机的IP地址getMethod得到客户机请求方式getContextPath 获得当前web应用虚拟目录名称

代码示例如下:

import java.io.IOException;   import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class CustInfoServlet extends HttpServlet {    public void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        //1.获取客户端请求的完整URL        String url = request.getRequestURL().toString();        System.out.println(url);        //2.获取客户端请求的资源部分的名称        String uri = request.getRequestURI();        System.out.println(uri);        //3.获取请求行中参数部分        String qStr = request.getQueryString();        System.out.println(qStr);        //4.获取请求客户端的ip地址        String ip = request.getRemoteAddr();        System.out.println(ip);        //5.获取客户机的请求方式        String method = request.getMethod();        System.out.println(method);        //6.获取当前web应用的名称        String name = request.getContextPath();        System.out.println(name);        response.sendRedirect(request.getContextPath()+"/index.jsp");    }    public void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    }   }

2、获取请求头信息

获得客户机请求头的方法:

getHeader(name)方法 --- String getHeaders(String name)方法 --- Enumeration<String>getHeaderNames方法 --- Enumeration<String>

获得具体类型客户机请求头:

getIntHeader(name)方法  --- intgetDateHeader(name)方法 --- long(日期对应毫秒)

代码实现

import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * request获取请求头信息 */public class HeaderServlet extends HttpServlet {    public void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {//      String value = request.getHeader("Host");//      System.out.println(value);//      Enumeration<String> enumeration = request.getHeaderNames();//      while(enumeration.hasMoreElements()){//          String name = enumeration.nextElement();//          String value = request.getHeader(name);//          System.out.println(name+":"+value);//      }    }    public void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        doGet(request, response);    } }

典型的应用:通过referer信息实现防盗链。

例如一些大的门户网站,例如网易,它的网页上会有新闻和广告,通过新闻来吸引人眼球,通过广告来挣钱。有些小网站可以超链接到网易的新闻上来给自己的网站带来阅读量,然后根据广告挣钱,这种行为就是盗链行为。为了防盗链,可以通过拦截访问新闻的连接,然后获取referer信息来获取访问的域名,对域名就行判断,如果不存在或者不是网易的域名就把它跳转到网易的页面上。这就是防盗链。

3、获取请求参数

常用的方法:

getParameter(name) --- String 通过name获得值getParameterValues  --- String[ ] 通过name获得多值 checkboxgetParameterNames  --- Enumeration<String> 获得所有namegetParameterMap  --- Map<String,String[ ]> key :name value: 多值

可能存在的乱码的问题:

我们知道出现乱码问题必然是编码和解码不是一张码表。那么我们要先知道浏览器是以什么编码来发送请求参数以及服务器是以什么编码来打开的。浏览器以什么编码来发送请求参数? 浏览器以什么编码打开的表单页面,就用什么编码发送这个页面提交的数据。服务器以什么编码来打开呢?如果不指定,则使用ISO8859-1,这样如果请求参数中有中文必然就乱码了。

然后对于不同的提交方式有不同的解决乱码的方法:对于POST提交,可以设置request.setCharacterEncoding("utf-8");明确的通知服务器以浏览器发送过来的编码来打开数据就可以解决乱码。但是上面的方法只对请求中实体内容部分起作用,所以GET提交的乱码并不能解决。对于GET提交的乱码,只能手动的进行编解码从而解决乱码问题:String username = request.getParameter("username");
sername = new String(username.getBytes("iso8859-1"),"utf-8");

4、利用请求域传递对象

request这个域的作用范围:整个请求链上。

生命周期:当服务器收到一个请求,创建出代表请求的request对象,request域开始。当请求结束,服务器销毁代表请求的request对象,request域结束。

作用:在整个请求链范围内共享数据,通常我们在Servlet中处理好的数据会存入request域后请求转发到jsp页面来进行展示。

request对象同时也是一个域对象,开发人员通过request对象在实现转发时,把数据通过request对象带给其它web资源处理,它具有四个方法:

setAttribute方法 getAttribute方法  removeAttribute方法getAttributeNames方法

5、实现请求转发和请求包含
除了用ServletContext进行请求转发,我们也可以用request对象就行请求转发。request对象提供了一个getRequestDispatcher方法,该方法返回一个RequestDispatcher对象,调用这个对象的forward方法可以实现请求转发,从而共享请求中的数据。
两种调用方式如下:

this.getServletContext().getRequestDispatcher("").forward(request,response);request.getRequestDispatcher("").forward(request,response);

请求转发的思想是希望将请求交给另外一个资源执行,所以应该保证只有最后真正要执行的资源才能够输出数据,所以:请求转发时,如果已经有数据被写入到了response的缓冲区,但是这些数据还没有被发送到客户端,则请求转发时,这些数据将会被清空。但是清空的只是响应中的实体内容部分,头信息并不会被清空。而如果请求转发时已经有数据被打给了浏览器,那么再进行请求转发,不能成功,会抛出异常,原因是响应已经结束了,再转发交给其他人没意义了。另外,在最终输出数据的Servlet执行完成后,response实体内容中的数据将会被设置为已提交的状态,再往里写数据也不会起作用。这样就保证了最终只有一个Servlet能够向浏览器输出数据,所以一个Servlet里两次请求转发也是不可以的,一次请求交给两人处理自然也是不行.

总结可知,利用请求转发则不能输出转发前servlet的任何信息。然而有时候我们希望能够拼接转发前后两个servlet的信息,用请求转发就不能实现,这时候我们可以用请求包含。

请求包含:将两个资源的输出进行合并后输出

使用方法和请求转发类似:

this.getServletContext().getRequestDispatcher("").include(request,response);request.getRequestDispatcher("").include(request,response);

RequestDispatcher.include方法用于将RequestDispatcher对象封装的资源内容作为当前响应内容的一部分包含进来,从而实现可编程的服务器端包含功能。被包含的Servlet程序不能改变响应消息的状态码和响应头,如果它里面存在这样的语句,这些语句的执行结果将被忽略。include在程序执行上效果类似forward,但是使用forward只有一个程序可以生成响应,include可以由多个程序一同生成响应 —– 常用来页面布局。

除了请求转发和请求包含,还有一个容易混淆的概念是请求重定向,那么请求重定向和请求转发有哪些不同呢?

请求重定向和请求转发的区别 :

  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对象,它们属于两个独立的访问请求和响应过程。

所以我们实践中如何使用呢?

如果需要在资源跳转时利用request域传递域属性则必须使用请求转发;如果希望资源跳转后修改用户的地址栏则使用请求重定向;如果使用请求转发也可以重定向也可以,则优先使用请求转发,减少浏览器对服务器的访问次数,减轻服务器的压力。

1 0
原创粉丝点击