06月06, 2019

Java Web--Servlet--HttpServletResponse对象

HttpServletResponse对象

一、HttpServletResponse对象介绍

HttpServletResponse对象代表服务器的响应。这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。查看HttpServletResponse的API,可以看到这些相关的方法。

1.1、负责向客户端(浏览器)发送数据的相关方法

1.2、负责向客户端(浏览器)发送响应头的相关方法

1.3、负责向客户端(浏览器)发送响应状态码的相关方法

1.4、响应状态码的常量

HttpServletResponse定义了很多状态码的常量(具体可以查看Servlet的API),当需要向客户端发送响应状态码时,可以使用这些常量,避免了直接写数字,常见的状态码对应的常量:

状态码404对应的常量

状态码200对应的常量

状态码500对应的常量

二、HttpServletResponse对象常见应用

2.1、Response输出方法

PrintWriter getWriter()

获得字符流,通过字符流的write(String s)方法可以将字符串设置到response缓冲区中,随后Tomcat会将response缓冲区中的内容组装成Http响应返回给浏览器端。

ServletOutputStream getOutputStream()

获得字节流,通过该字节流的write(byte[] bytes)可以向response缓冲区中写入字节,再由Tomcat服务器将字节内容组成Http响应返回给浏览器。

注意:虽然response对象的getOutSream()和getWriter()方法都可以发送响应消息体,但是他们之间相互排斥,不可以同时使用,否则会发生异常。

2.2、Response乱码问题

乱码发生的原因:

计算机中的数据都是以二进制进行存储的,因此传输文本时,就会发生字节和字符之间的转换。字符与字节之间的转换需要查码表完成,将字符转换为字节的过程叫做编码,将字节转换为字符的过程叫做解码。如果编码和解码使用的码表不一致,就会发生乱码。这就是乱码发生的根本原因

①、response缓冲区的默认编码是iso8859-1,此码表中没有中文。所以需要更改response的编码方式:

②、通过更改response的编码方式为UTF-8,任然无法解决乱码问题,因为发送端服务端虽然改变了编码方式为UTF-8,但是接收端浏览器端仍然使用GB2312编码方式解码,还是无法还原正常的中文,因此还需要告知浏览器端使用UTF-8编码去解码。

上面通过调用两个方式分别改变服务端对于Response的编码方式以及浏览器的解码方式为同样的UTF-8编码来解决编码方式不一样发生乱码的问题。

response.setContentType("text/html;charset=UTF-8")这个方法包含了上面的两个方法的调用,因此在实际的开发中,只需要调用一个response.setContentType("text/html;charset=UTF-8")方法即可。

2.3、中文文件下载

示例:文件下载 注意:下载中文文件时,需要注意的地方就是中文文件名要使用URLEncoder.encode方法进行编码:URLEncoder.encode(fileName, "字符编码"),否则会出现文件名乱码。

ResponseDemo02.java

package com.lovo.study;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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 ResponseDemo02 extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public ResponseDemo02() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取要下载文件的绝对路劲
        String realPath = this.getServletContext().getRealPath("/downloads/美女.jpg");
        //获取要下载的文件名
        String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
        //设置content-disposition响应头控制浏览器以下载的形式打开文件,
        //中文文件名要使用URLEncoder.encode方法进行编码,否则会出现文件名乱码
        response.setHeader("Content-disposition", 
                "attachment;filename="+URLEncoder.encode(fileName,"UTF-8"));
        InputStream in = new FileInputStream(realPath);
        int len = 0;
        byte [] buffer = new byte[1024];
        OutputStream out = response.getOutputStream();
        while((len = in.read(buffer)) > 0){
            out.write(buffer,0,len);
        }
        in.close();
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

三、HttpServletResponse常见应用——设置响应头控制浏览器的行为

3.1、设置http响应头控制浏览器禁止缓存当前文档内容

response.setDateHeader("expries", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");

3.2、设置http响应头控制浏览器定时刷新网页(refresh)

response.setHeader("refresh", "5");//设置refresh响应头控制浏览器每隔5秒钟刷新一次

3.3、通过response实现请求重定向

请求重定向:一个web资源收到客户端请求后,通知客户端去访问另外一个web资源,这称之为请求重定向。

实现方式:response.sendRedirect(String location),即调用response对象的sendRedirect方法实现请求重定向

sendRedirect内部的实现原理:使用response设置302状态码和设置location响应头实现重定向

package com.lovo.study;

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 ResponseDemo03 extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public ResponseDemo03() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         /**
        * 1.调用sendRedirect方法实现请求重定向,
        * sendRedirect方法内部调用了
        * response.setHeader("Location", "/request_form.jsp");
        * response.setStatus(HttpServletResponse.SC_FOUND);//设置302状态码,等同于response.setStatus(302);
        */
        response.sendRedirect("/request_form.jsp");
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

3.4、重定向与转发的区别

1)转发是服务器行为,重定向是客户端行为。 为什么这样说呢,这就要看两个动作的工作流程:

转发过程:客户浏览器发送http请求——web服务器接受此请求——调用内部的一个方法在容器内部完成请求处理和转发动作——将目标资源发送给客户;

在这里,转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去,中间传递的是自己的容器内的request。

客户浏览器路径栏显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。

重定向过程:客户浏览器发送http请求——web服务器接受后发送302状态码响应及对应新的location给客户浏览器——客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址——服务器根据此请求寻找资源并发送给客户。

在这里location可以重定向到任意URL,既然是浏览器重新发出了请求,则就没有什么request传递的概念了。在客户浏览器路径栏显示的是其重定向的路径,客户可以观察到地址的变化的。重定向行为是浏览器做了至少两次的访问请求的。

2).对信息的传输不一样

重定向,其实是两次request

第一次,客户端request A,服务器响应,并response回来,告诉浏览器,你应该去B。这个时候IE可以看到地址变了,而且历史的回退按钮也亮了。重定向可以访问自己web应用以外的资源。在重定向的过程中,传输的信息会被丢失。

请求转发是服务器内部把对一个request/response的处理权,移交给另外一个

对于客户端而言,它只知道自己最早请求的那个A,而不知道中间的B,甚至C、D。传输的信息不会丢失

3).对于路径的认知不一样

请求转发是服务器端的动作,所以当转发路径如:

request.getRequestDispatcher("/RequestDemo07").forward(request, response);

路径前的绝对路径"/",会被认为是当前工程的根目录,也就是说,如果当前路径是:

http://localhost:8080/ServletDemo/RequestDemo06

那么根据上面的跳转就会跳转到

http://localhost:8080/ServletDemo/RequestDemo07

而重定向,如下面的路径: response.sendRedirect("/RequestDemo07");

如果当前路径是: http://localhost:8080/ServletDemo/RequestDemo06

就会跳转到: http://localhost:8080/RequestDemo07

四、web工程中URL地址的推荐写法

在JavaWeb开发中,只要是写URL地址,那么建议最好以"/"开头,也就是使用绝对路径的方式,那么这个"/"到底代表什么呢? 可以用如下的方式来记忆"/":如果"/"是给服务器用的,则代表当前的web工程,如果"/"是给浏览器用的,则代表webapps目录。

4.1、"/"代表当前web工程的常见应用场景

①.ServletContext.getRealPath(String path)获取资源的绝对路径

/**
* 1.ServletContext.getRealPath("/download/1.JPG")是用来获取服务器上的某个资源,
* 那么这个"/"就是给服务器用的,"/"此时代表的就是web工程
 * ServletContext.getRealPath("/download/1.JPG")表示的就是读取web工程下的download文件夹中的1.JPG这个资源
* 只要明白了"/"代表的具体含义,就可以很快写出要访问的web资源的绝对路径
*/
this.getServletContext().getRealPath("/download/1.JPG");

②.在服务器端forward到其他页面

/**
* 2.forward
* 客户端请求某个web资源,服务器跳转到另外一个web资源,这个forward也是给服务器用的,
* 那么这个"/"就是给服务器用的,所以此时"/"代表的就是web工程
*/
this.getServletContext().getRequestDispatcher("/index.jsp").forward(request, response);

③.使用include指令或者<jsp:include>标签引入页面

<%@include file="/jspfragments/head.jspf" %>
<jsp:include page="/jspfragments/demo.jsp" />

此时"/"代表的都是web工程。

4.2、"/"代表webapps目录的常见应用场景

①.使用sendRedirect实现请求重定向

response.sendRedirect("/index.jsp");

服务器发送一个URL地址给浏览器,浏览器拿到URL地址之后,再去请求服务器,所以这个"/"是给浏览器使用的,此时"/"代表的就是webapps目录,也就是上面的路径实际是:

http://localhost:8080/index.jsp

这样的路径肯定不不正确的,所以要正常访问需要改为:

response.sendRedirect("/SerlvetDemo(工程名)/index.jsp");

这种写法是将项目名称写死在程序中的做法,不灵活,万一哪天项目名称变了,此时就得改程序,所以推荐使用下面的灵活写法:

response.sendRedirect(request.getContextPath()+"/index.jsp");

request.getContextPath()获取到的内容就是"/ServletDemo",这样就比较灵活了,使用request.getContextPath()代替"/项目名称",推荐使用这种方式,灵活方便!

②.使用超链接跳转

<a href="/ServletDemo/index.jsp">跳转到首页</a>

这是客户端浏览器使用的超链接跳转,这个"/"是给浏览器使用的,此时"/"代表的就是webapps目录。

使用超链接访问web资源,绝对路径的写法推荐使用下面的写法改进:

<a href="${pageContext.request.contextPath}/index.jsp">跳转到首页</a>

③.Form表单提交 ④.js脚本和css样式文件的引用 处理方案等同于超链接

案例:生成随机图片

package com.lovo.study;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ResponseDemo04 extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public ResponseDemo04() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setHeader("refresh", "5");
        BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = (Graphics2D)image.getGraphics();

        //先设置画笔颜色然后画背景颜色
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, 80, 20);

        //设置画笔颜色画数字
        g.setColor(Color.BLUE);
        //设置字体
        g.setFont(new Font(null, Font.BOLD, 20));

        //生产随机数
        int [] nums = {0,1,2,3,4,5,6,7,8,9};
        String str = "";
        for(int i=0; i<6; i++){
            int n = (int)Math.floor(Math.random() * 10);
            str += n;
        }
        //画字符串
        g.drawString(str, 0, 20);

        //设置响应头以图片方式打开
        response.setContentType("image/jpeg");
        //设置响应头清除缓存
        response.setDateHeader("expries", -1);
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");
        //6.将图片写给浏览器
        ImageIO.write(image, "jpg", response.getOutputStream());

    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

本文链接:http://www.yanhongzhi.com/post/response.html

-- EOF --

Comments