Tomcat的response

1. response组成

  • response有三部分组成:响应行、响应头、响应体

1.1 response对象

  • HttpServletResponse 对象封装了向客户端发送数据、发送响应头,发送响应状态码的方法。

1.1.1 常用方法

- void setStatus(int sc); 设置此响应的状态代码;
- void setHeader(String name, String value); 用给定名称和值设置响应头

1.2 响应行(状态行)

1.2.1概述

  • 响应行是http 响应内容的第一行
  • 响应行分为三个部分:协议版本、响应状态码、对响应状态码的解释
  • 对于Tomcat,一般数据为:HTTP/1.1 200(tomcat8.5) 或者HTTP/1.1 200 OK(tomcat7)

1.2.2 常见的响应状态码

  1. 200 OK :请求和响应都已成功,服务器通信正常;
  2. 302 Move temporarily :设置重定向页面跳转的动作执行;
  3. 304 Not Modified :从浏览器缓存中读取数据,不从服务器重新获取数据;
  4. 404 Not Found :请求失败,请求所希望得到的资源未被在服务器上发现。一般是用户输错了url 导致;
  5. 405 Method Not Allowed :请求行中指定的请求方法不存在。例如,发送post 请求,服务器没有doPost方法,就会报这个错误;
  6. 500 Internal Server Error : 服务器发生了错误。一般服务器代码错误。

1.3 响应头

响应头包含但不仅限于以下几个属性:

  1. refresh:定时刷新跳转页面;
  2. location:重定向操作:通常告知浏览器马上向该地址发送请求,通常和响应码302 一起使用;
  3. content-encoding:设置当前数据的压缩格式,告知浏览器以何种压缩格式解压数据(目前浏览器只支持”gzip”格式解压);
  4. content-disposition:通知浏览器以何种方式获取数据(直接解析数据(网页,图片文本),或者以附件方式(下载文件));
  5. content-type:实体头部用于指示资源的MIME 类型。
注意:我们content-type 常用的设置一般都是“text/html;charset=utf-8”
	其中“text/html;”——设置浏览器以文件格式解析数据;“charset=utf-8”——响应数据的编码表

1.3.1 响应头:Location

  • 作用:通知浏览器要进行页面跳转的目标地址
  • http 状态码302 的作用是通知浏览器进行页面跳转的动作执行,所以响应头location(跳转目标地址)和http 状态码302 (跳转动作执行)配合起来才可以完成页面跳转。
1.3.1.1 实现代码
方式一
//需求:跳转到资源CountServlet
//设置跳转目标地址
response.setHeader("location", "index.html");
//设置http状态码为302
response.setStatus(302);
优化代码(重要)
- response.sendRedirect("目标页面/目标Servlet");
	发送重定向【原理就是方法一】

注意:目标页面/Servlet需要写工程名称,但是不能将工程名写死,需要用servletContext来 获取工程名字

1.3.1.2 请求转发与请求重定向的区别及选择(重点)
请求转发的原理

1536570357045

请求重定向

1536570389228

总结(重要):
  1. 转发在一次请求中完成,转发地址栏不变(只有一次请求);重定向则是两次请求,重定向地址栏变化(两次请求,对应两个地址);
  2. 转发发生在服务器内部,只能访问服务器当前工程的内部资源,不能访问外部资源;重定向是浏览器执行跳转操作,可以访问任何资源;
  3. 转发前后均共享同一个request和response(由服务器收到第一次请求时创建);重定向前后有各自独立的request和response;
  4. 一次请求创建一个request和response,在服务器响应结束时就会销毁。不同请求不会共享request和response。
选择:
  • 由于request 是请求域对象,如果页面跳转需要在一次请求域内传递数据的(即前后页面要传输数据)使用请求转发,否则建议使用重定向
注意事项:
  • 重定向除了设置location实现之外,也能通过设置refresh实现。

1.3.2 响应头:Content-Type

  • 格式:Content-Type: text/html; charset=码表

解释:Content-Type是设置响应正文类型/报文类型,包含两部分

第一部分:设置响应的数据类型(Mime-Type),服务器
可以响应任何类型的资源给客户端,资源不同,Mime-Type 不同。

Mime-Type 含义
text/html Html 代码
text/plain Txt 文本文件
image/jpeg Jpg 图片文件
Application/json Json 数据

第二部分:响应字符码表:通知浏览器以什么码表解码数据

  • 作用:用于服务器通知浏览器采用什么码表对服务器响应的数据进行解码。解决响应中文乱码问题
1.3.2.1 获取Mime-Type方法
- getServletContext().getMimeType(fileName)

根据Mime-Type设置文件媒体格式

  • 用来告知浏览器文件的媒体格式,要根据哪种媒体格式解析资源
- response.setContentType(getServletContext().getMimeType(fileName));
1.3.2.2 服务器端输出中文数据有2 种方式
字节流输出
response.getOutputStream().write("输出字符串".getBytes());
字符流输出
response.getWriter().write("输出字符串");
1.3.2.3 解决服务器输出字符流中文乱码(重要)
  • 产生乱码原因:服务器响应数据默认采用iso8859-1 码表,浏览器默认采用GBK码表。

浏览器与服务器传输中文数据的url 编码过程(了解)

  1. 对字节数组中的负数加256
  2. 将加后的字节数组转换为十六进制整型数据
  3. 之后在每个16 进制数据前加%
解决方案:设置响应头content-type

方案1:

//修改response默认输出中文数据码表
response.setCharacterEncoding("utf-8");
//通知浏览器以utf-8 解码
response.setHeader("content-type","text/html;charset=utf-8");

优化方案:

- response.setContentType("text/html;charset=utf8");
	修改服务器响应的编码码表和浏览器解码码表【原理就是方案1
1.3.2.4 response输出中文数据的区别
  1. 字节流输出中文不乱码原因是中国编码默认使用gbk,浏览器默认gbk解码;
  2. 但由于有可能部分浏览器不适用gbk,用字节流输出中文数据不安全,故建议输出中文数据使用字符流。

1.3.3 响应头:refresh

  • 格式:Refresh: 定时时间;url=跳转网址

    • 定时时间:以秒为单位,不用引号;
    • 跳转网址:整个网页的完整地址,不用引号;
  • 作用:设置浏览器定时刷新页面或定时跳转到指定的资源;

1.3.3.1 设置方式
/*需求:3 秒以后跳转到demo.html*/
response.setHeader("refresh","3;url="+getServletContext().getContextPath()+"/demo.html");
  • 注意,工程名不能写死
1.3.3.2 刷新和跳转案例
@WebServlet(urlPatterns = "/demo01")
public class Notes01Servlet extends HttpServlet {
/*
需求:倒计时5秒后跳转到新的页面succeed.html。
思路:首先直接进入demo01(Servlet),然后进入到倒计时的wait.html,5秒后跳转到新的页面
 */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //5秒后跳转到新的页面中,使用refresh重定向跳转
        //这里修改第一次进入demo01时创建的response对象refresh属性实现跳转功能
        response.setHeader("refresh", "2;url=succeed.html");

        //1.这里必须使用请求转发页面跳转
        //2.如果这里也使用重定向,那么重定向后上面修改refresh的response已经被销毁而创建了新的response,自然无法跳转
        //3.上一行代码和这行代码的顺序不能调,因为本行代码已经将request和response转发了,那么就没有response能调用上行代码
        request.getRequestDispatcher("/wait.html").forward(request, response);
        
        /*
        response.sendRedirect("/day29/wait.html");    //跳转后此时传入的response已经被销毁     
        response.setHeader("refresh", "2;url=succeed.html");    //这里的response仍是传入的response,已被销毁,该行代码无效,也不会修改到跳转后的refresh实现再度跳转
        //上述两行代码也说明,即使是先重定向到wait.html,再refresh也无法跳转
        */

    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //*如果post方式的执行内容与get不同,则删掉下面代码重写即可
        doGet(request, response);
    }
}

1.3.4 响应头:Content-Disposition

  • 格式:Content-Disposition: attachment; filename=下载文件名

    • attachment,通知浏览器不要显示数据要以附件形式下载
    • 下载文件名:下载的文件名字,包含后缀名
  • 作用:默认浏览器查看数据是直接显示数据,而Content-Disposition可以通知浏览器不要直接显示数据,以附件形式下载数据

1.3.4.1 设置方式
response.setHeader("content-disposition","attachment;filename="+fileName);
1.3.4.2 注意事项
  • 如果响应头传递中文默认不会进行url编码,故无法传递中文,需要手动进行url编码;
  • 请求体和请求行、响应体在传递中文数据会自动进行url编码;
  • 浏览器和服务器在传输中文数据时,中间过程还要进行url编码,才能执行中文数据传输。
手动进行URL编码的代码(不需强记,直接复制即可)
String fileName = "文件名.后缀名";
String ua = request.getHeader("User-Agent");
boolean IE_LT11 = ua.contains("MSIE"); // IE11 以下版本
boolean IE11 = ua.contains("rv:11.0) like Gecko"); // IE11
boolean Edge = ua.contains("Edge"); // win10 自带的Edge 浏览器
// 如果是微软的浏览器,直接进行UTF-8 编码
if (IE_LT11 || IE11 || Edge) {
	fileName = URLEncoder.encode(fileName, "UTF-8");
	// java 的编码方式和浏览器有略微的不同:对于空格,java 编码后的结果是加号,
	// 而浏览器的编码结果是%20,因此将+替换成%20, 这样浏览器才能正确解析空格
    fileName = fileName.replace("+", "%20");
}
// 标准浏览器使用Base64 编码
else {
	Base64.Encoder encoder = Base64.getEncoder();
	fileName = encoder.encodeToString(fileName.getBytes("utf-8"));
	// =?utf-8?B?文件名?= 是告诉浏览器以Base64 进行解码
	fileName = "=?utf-8?B?" + fileName + "?=";
}
1.3.4.3 扩展案例:以附件形式下载不同类型文件(类似于工具类)

前期准备:download页面由前端编写,这里只是简单的页面。

另外还要注意,本地可访问资源要放在IDEA的web文件夹内

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>下载页面</title>
    <style type="text/css"></style>
    <script type="text/javascript">
        /*
        除了IE浏览器(IE11及以下)默认不对路径中的中文字符进行编码以外,其余浏览器都会支持中文路径编码,
        所以为了发送请求时解决中文乱码的问题,这里要对浏览器进行判断。如果是ie11及以下的要手动url编码
        */
        function isIE() {
            //获取当前浏览器相关信息
            var explorer = window.navigator.userAgent.toLowerCase();
            //判断是否是ie浏览器
            if (explorer.indexOf("msie") >= 0 || explorer.indexOf("rv:11.0) like gecko") >= 0) {
                return true;
            } else {
                false;
            }
        }

        window.onload = function () {
            if (isIE()) {
                //在是IE浏览器的情况下,对中文请求参数编码
                var str = document.getElementById("sn").href;
                var str = encodeURI(str);
                document.getElementById("sn").href = str;
            }
        };

    </script>
</head>
<body>
<a href="download?fileName=1.txt">1.txt</a>     <!--路径默认使用get方式提交,用?串联相当于提交该文件-->
<a href="download?fileName=Impactor_0.9.34.zip">Impactor_0.9.34.zip</a>
<a href="download?fileName=hq.jpg">hq.jpg</a>
<a id="sn" href="download?fileName=烧酒.png">烧酒.png</a>
</body>
</html>

后端代码

注意:如果文件名有中文和特殊字符,必须要先解决request的中文乱码问题,否则获取的文件名乱码,导致对应的资源无法下载

@WebServlet(urlPatterns = "/download")
public class Notes02DownloadServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //解决request中文乱码问题
        request.setCharacterEncoding("utf-8");

        /*1.获得下载的文件名字*/
        String fileName = request.getParameter("fileName");
        System.out.println(fileName);

        /*2.获取资源的真实路径*/
        ServletContext servletContext = getServletContext();
        String realPath = servletContext.getRealPath("/download");
        //根据真实路径获得file对象,用来创建输入流,读取文件
        File file = new File(realPath,fileName);

        /*3.根据Mime-Type设置文件媒体格式,告知浏览器传输的文件是什么格式*/
        response.setContentType(servletContext.getMimeType(fileName));

        /*4.解决中文文件名乱码的问题,复制黏贴即可*/
        String ua = request.getHeader("User-Agent");
        boolean IE_LT11 = ua.contains("MSIE"); // IE11以下版本
        boolean IE11 = ua.contains("rv:11.0) like Gecko"); // IE11
        boolean Edge = ua.contains("Edge"); // win10自带的Edge浏览器
        // 如果是微软的浏览器,直接进行UTF-8编码
        if (IE_LT11 || IE11 || Edge) {
            fileName = URLEncoder.encode(fileName, "UTF-8");
            // java的编码方式和浏览器有略微的不同:对于空格,java编码后的结果是加号,
            // 而浏览器的编码结果是%20,因此将+替换成%20, 这样浏览器才能正确解析空格
            fileName = fileName.replace("+", "%20");
        }
        // 标准浏览器使用Base64编码
        else {
            Base64.Encoder encoder = Base64.getEncoder();
            fileName = encoder.encodeToString(fileName.getBytes("utf-8"));
            // =?utf-8?B?文件名?= 是告诉浏览器以Base64进行解码
            fileName = "=?utf-8?B?" + fileName + "?=";
        }

        /*5.提示浏览器以下载的形式获取服务器资源*/
        response.setHeader("content-disposition","attachment;filename=" + fileName);

        /*6.使用流输出数据*/
        FileInputStream fis = new FileInputStream(file);
        ServletOutputStream sos = response.getOutputStream();
        byte[] bytes = new byte[1024];
        int len = -1;
        while ((len = fis.read(bytes))!= -1) {
            sos.write(bytes,0,len);
        }

        /*7.记得关闭输入流释放资源*/
        fis.close();
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //*如果post方式的执行内容与get不同,则删掉下面代码重写即可
        doGet(request, response);
    }
}

1.4 响应体

  • 浏览器将服务器输出的数据直接显示给用户
  • 作用:
    • 输出字符数据、字节数据
    • 输出资源文件数据(资源图片)
    • 输出缓存(内存中)图片(资源没有对应的物理资源,不是真实资源)–验证码

1.4.1 验证码案例(类似工具类)

实现验证码功能

@WebServlet(urlPatterns = "/CheckCodeServlet")
public class Notes03CheckCodeServlet extends HttpServlet {
    //获取随机数
    private Random random = new Random();

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

        //创建一个缓存图片
        //BufferedImage(int width,int height,int imageType)
        BufferedImage image = new BufferedImage(90,30,BufferedImage.TYPE_INT_RGB);
        //获取画笔
        Graphics g = image.getGraphics();
        //设置画笔的颜色
        g.setColor(Color.white);
        //设置画布范围
        g.fillRect(0,0,90,30);
        //画干扰线
        //g.drawLine(x1,y1,x2,y2);
        for (int i=1;i<=4;i++){
            //设置每条线的随机颜色
            g.setColor(getColor());
            int x1=random.nextInt(91);
            int y1=random.nextInt(31);
            int x2=random.nextInt(91);
            int y2=random.nextInt(31);
            g.drawLine(x1,y1,x2,y2);
        }

        //画验证码
        String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890小天使";
        //随机选取4个
        StringBuilder checkCode = new StringBuilder();
        for(int i=0;i<4;i++){
            int index = random.nextInt(data.length());
            char item = data.charAt(index);
            checkCode.append(item);
        }
        System.out.println("验证码:"+checkCode.toString());
        //将验证码画到图片上
        //g.drawString(str,x,y);
        int i=0;
        for(char item : checkCode.toString().toCharArray()){
            //设置每个字符随机颜色
            g.setColor(getColor());
            //将每个字符画到图片上
            g.drawString(item+"",10+(i*20),15);
            i++;
        }
        //将内存图片输出到浏览器中
        ImageIO.write(image,"png",response.getOutputStream());
    }

    /*获取随机颜色*/     
    public Color getColor(){
        int r = random.nextInt(256);
        int g = random.nextInt(256);
        int b = random.nextInt(256);
        //Color(int r, int g, int b)
        return new Color(r,g,b);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //*如果post方式的执行内容与get不同,则删掉下面代码重写即可
        doGet(request, response);
    }
}

2. ServletContext

2.1 ServletContext概述

    1、ServletContext 是一个容器(**域对象**)可以存储**键值对数据(String key,Object value)**,保存在ServletContext 中的数据不仅可以**提供给所有的servlet 使用**,而且可以在**整个项目范围内使用**(后期的过滤器、监听器也可以使用ServletContext)

    2、服务器会为每一个工程创建一个ServletContext 对象,这个对象就是ServletContext 对象。这个对象**全局唯一(一个工程仅一个)**,而且**工程内部的所有servlet 都共享**这个对象。所以叫**全局应用程序共享对象**。

    3、服务器启动就创建ServletContext,服务器关闭了才销毁ServletContext

2.2 ServletContext的作用

  1. 可以读取资源在当前项目中的文件位置
  2. 可以作为域对象在项目全局范围内提供共享数据

2.3 使用ServletContext读取资源在当前项目中的文件位置

  • 方法:

    - String getServletContext().getRealPath(path);
    	根据相对路径获取项目部署位置上资源的绝对路径
    

2.3.1 示例代码

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
	//servletContext可以获取当前项目下的资源文件
	//可以根据相对路径获取在服务器上的绝对路径
	String realPath = getServletContext().getRealPath("img/2.jpg");
	FileInputStream fileInputStream = new FileInputStream(realPath);
	int length = -1;
	byte[] bytes = new byte[1024];
	while((length = fileInputStream.read(bytes))!=-1){
		//将读取到数据的字节数组输出到浏览器
		response.getOutputStream().write(bytes, 0, length);
	}
	//关闭输入流
	fileInputStream.close();
}

2.4 使用ServletContext作为域对象共享数据

2.4.1 常用API

  • GenericServlet:
- ServletContext getServletContext();
	获取ServletContext 对象
	该方法直接调用即可
  • ServletContext:
- void setAttribute(String name, Object object);
	往servletcontext 容器中存入数据,name 为数据名称,object 为数据的值
- Object getAttribute(String name);
	从ServletContext 中获取指定数据名称的数据
- void removeAttribute(String name);
	从ServletContext 中移除指定数据名称的数据

2.4.2 request 域与servletContext 域的区别

  1. request 请求域对象,数据有效范围一次请求内,用于转发传递数据;
  2. servletContext 全局域(上下文域)对象,数据有效范围是整个应用程序内,所有资源共享.用于存储全局共享的数据。

猜你喜欢

转载自blog.csdn.net/KeepStruggling/article/details/82767871
今日推荐