Response

今日目标

  1. 0. http响应
  2. 1. response【重点】

1630649573693.png

零 Http响应

之前我们已经学习过Http请求, 现在我们一起再来学习Http响应

0.1 浏览器查看Http响应协议

1590735760486.png

1649559718295.png

抓包的代码

    <a href="ResponseServlet">03_查看响应报文</a> <br>
package com.itheima03.response;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/ResponseServlet")
public class ResponseServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.getWriter().print("hello");
    }
}

0.2 HTTP响应消息格式

① 响应行

* 格式
        协议/版本号 状态码
* 例如
        HTTP/1.1 200

* 常见状态码
    --------------- 用户可以正常访问页面
    200 :表示成功    
    302 :重定向(response学习)
    304 :从缓存中读取数据
        (如果这个资源没有变化,浏览器是会缓存的,下次请求,就直接从缓存中读取,提高效率)
    --------------- 用户无法正常访问页面
    404:请求资源未找到(not  found)
    405:请求的方法未找到(明天讲了servlet给大家演示....)
    500:服务器内部错误(java代码写错了.....)

② 响应头

* 格式
        响应头名称:响应头的值
* 例如
        Last-Modified: Mon, 13 Apr 2020 06:11:24 GMT

常见响应头

1. Location:通常与状态码302一起使用,实现重定向操作
        Location:http://www.itcast.cn
2. Content-Type:服务器告诉客户端,返回响应体的数据类型和编码方式
        Content-Type:text/html;charset=utf-8
3. Refresh:在指定间隔时间后,跳转到某个页面
        Refresh:5;http://www.itcast.cn

③ 响应体(正文)

* 服务器返回的数据,由浏览器解析后展示给用户
   用户看到页面所有的内容,都是在响应体中返回的

一 Response【重点】

1.1 概述

  • response对象表示web服务器给浏览器返回的响应信息

    response是由tomcat创建的

  • 作用:开发人员可以使用response对象的方法,设置要返回给浏览器的响应信息

    注意: tomcat + servlet (服务端)

    在response中但凡不是Servlet设置的信息,就是tomcat设置的, 我们可以用Servlet中设置覆盖tomcat的设置

    ServletResponse 接口 (理论上兼容大部分协议)
            |    
    HttpServletResponse 接口 (专门指的是http协议的响应)
            |    
    org.apache.catalina.connector.ResponseFacade 实现类(由tomcat提供的)

1.2 设置Http响应行

* 格式
         协议/版本号 状态码(status code) 
* 例如
        HTTP/1.1 200    
* API
    1. 设置状态码(status code) 
             void setStatus(int sc)  
    2. 常见响应状态码:
            200:请求和响应都OK(顺利)

            302:重定向 (待会)
            304:浏览器从缓存中加载数据

            404:请求的路径错误或请求的资源不存在(自己开发的时候,路径有问题)
            405: 在HttpServlet的子类中, doGet或doPost方法没有重写
            500:服务器内部异常

    3. 注意事项:上述常见的响应状态码,通常是由tomcat服务器自动响应给浏览器。我们一般是不需要手动设置的;

浏览器缓存(304)

1599619354298.png

package com.itheima03.response;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/*

    TODO response
        1. 响应: 服务器发送给浏览器的数据
        2. 服务器 : tomcat + servlet
        响应的内容不是servlet设置的就是tomcat设置

   TODO:
       响应报文
       1. 响应行
            1). 协议 : tomcat8默认使用 HTTP/1.1 (固定)
            2). 状态码 (status code) : 表示当前响应的状态
                 I. 200 : 响应成功
                 II. 302 : 重定向 (待会)
                 III. 304 : 读取缓存 ,没有从服务器读取数据
                    浏览器的缓存机制 (提高效率)
                    可能给开发带来麻烦,以后更新网页出问题记得清除缓存
                 IV. 404 : not found , 资源不存在
                    开发碰到这个问题: 网址写错了
                    生活碰到这个问题: 网址写错了/资源下架了
                 V. 500 : 服务器错误
                    开发碰到这个问题: 服务器程序有bug
                        (查看bug信息 要么在server控制台,要么就在tomcat localhost log)
                    生活碰到这个问题: 服务器崩溃了
              注意: 状态码一般由tomcat自动设置即可!!!!
                   目前,只有用到重定向功能,我们才会主动设置状态码
                    response.setStatus(302);
       2. 响应头

       3. 响应体
 */
@WebServlet("/ResponseDemo01Servlet")
public class ResponseDemo01Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

//        int i = 1/0;
        response.setStatus(302);
    }
}

1.3 设置Http响应头

* 格式
        响应头名称:响应头的值
        (name : value)
* 例如
        Content-Length : 5 (指的是响应体内容长度5个字符)
* API
    1. 设置指定头名称和对应的值
            void setHeader(String name, String value)  
    2. value值可以由多个参数组成,不同参数之间使用分号隔开:         
            response.setHeader(key,"value1;value2");
    3. 常用响应头:
            refresh:定时刷新;
            content-type:设置响应数据的数据类型和编码格式; (内容类型)
            location:配合302响应状态码完成重定向;(位置)

1.4 设置Http响应体

响应体中包含 响应数据的正文
响应是服务器给浏览器发送数据: 输出流
* API(输出流对象)
    1. 字符输出流 : 用于向浏览器输出字符数据(文本)
            PrintWriter getWriter()            
    2. 字节输出流 : 用于向浏览器输出二进制数据(比如文件下载)
            ServletOutputStream getOutputStream()

    注意:在同一个servlet中,二种类型的输出流不能同时存在,互斥
package com.itheima03.response;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/*

    TODO response
        1. 响应: 服务器发送给浏览器的数据
        2. 服务器 : tomcat + servlet
        响应的内容不是servlet设置的就是tomcat设置

   TODO:
       响应报文
       1. 响应行
            1). 协议 : tomcat8默认使用 HTTP/1.1 (固定)
            2). 状态码 (status code) : 表示当前响应的状态

       2. 响应头

       3. 响应体
            0). 响应: 服务器发送给浏览器的数据
                response中用输出流来给浏览器发送数据
            1). 字节输出流
                        ServletOutputStream os = response.getOutputStream();
                        用来响应文件数据
            2). 字符输出流
                        response.getWriter().print("hello world");
                        一般用来响应文本内容
            注意: 两个流只能用一个,同时使用会冲突!!!
            扩展:
                浏览器获取到响应数据
                1). 如果响应数据是文本内容: 浏览器会默认显示在网页上
                2). 如果响应数据是文件 : 浏览器具体看情况(可能显示,也可能下载)

 */
@WebServlet("/ResponseDemo01Servlet")
public class ResponseDemo01Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

//        int i = 1/0;
        //设置状态码 : 重定向(再说)
//        response.setStatus(302);
            //字符输出流: 响应体
//        PrintWriter writer = response.getWriter();
//        writer.print("hello world");

//        response.getWriter().print("hello world");

            //字节输出流: 响应体
        ServletOutputStream os = response.getOutputStream();
        os.write("hello".getBytes());

    }
}

二. 响应头的功能【重点】

2.1 响应定时刷新(refresh)

需求

在当前页面停留3秒钟之后,跳转到传智播客首页

1587095031542.png

步骤分析

1. 通过response设置响应头 Refresh
        response.setHeader("Refresh","间隔时间(秒);跳转页面");
  <a href="ResponseDemo02Servlet">05_定时刷新_响应头refresh(付款)</a> <br>
package com.itheima03.response;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

/*
    TODO 响应头 (格式 name : value)
    * API
    1. 设置指定头名称和对应的值
            void setHeader(String name, String value)
    2. value值可以由多个参数组成,不同参数之间使用分号隔开:
            response.setHeader(key,"value1;value2");
    3. 常用响应头:
            refresh:定时刷新;
            content-type:设置响应数据的数据类型和编码格式; (内容类型)
            location:配合302响应状态码完成重定向;(位置)
 */
@WebServlet("/ResponseDemo02Servlet")
public class ResponseDemo02Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //TODO: 重点
            /*
            *  响应头: refresh
            *   服务器告诉浏览器, 5秒之后跳转到百度
            * */
        response.setHeader("refresh","5;http://www.baidu.com");

        response.setContentType("text/html;charset=utf-8");
        response.getWriter().print("付款成功,5秒之后跳转到主页");
    }
}

2.2 响应体中文乱码(content-type)

需求

向页面输出中文数据没有乱码

原因分析

1. 通过response获取字符输出流
        PrintWriter pw = response.getWriter();        
2. 通过字符输出输出中文字符
        pw.write("中文....");
3. 浏览器访问,网页出现中文乱码    

4. 解决 : 统一浏览器和服务器编码(utf-8)    
    response.setContentType("text/html;charset=utf-8");

解决中文乱码

package com.itheima03.response;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

/*
  TODO 响应头 (格式 name : value)
    * API
    1. 设置指定头名称和对应的值
            void setHeader(String name, String value)
    2. value值可以由多个参数组成,不同参数之间使用分号隔开:
            response.setHeader(key,"value1;value2");
    3. 常用响应头:
            refresh:定时刷新;
            content-type:设置响应数据的数据类型和编码格式; (内容类型)
            location:配合302响应状态码完成重定向;(位置)

  TODO
    1. 问题: 响应体中文乱码
        1). 根本原因: tomcat默认编码表是ISO-8859-1,不支持中文
                    默认告诉浏览器的解码表: ISO-8859-1
                    Content-Type: text/html;charset=ISO-8859-1
                    服务器告诉浏览器,用ISO-8859-1进行解码
        2). 解决问题: 告诉浏览器用utf-8解码

    2. 服务器告诉浏览器,应该用什么语法解析响应体
       MIME type :    text/plain      text/html     application/json    
    windows文件类型:     txt(纯文本)     html(超文本)     json

    TODO 常用
    response.setContentType("text/html;charset=utf-8");
 */
@WebServlet("/ResponseDemo03Servlet")
public class ResponseDemo03Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

//        response.setHeader("content-type","text/html");
//        response.setHeader("content-type","text/html;charset=utf-8"); //完整写法

        response.setContentType("text/html;charset=utf-8");// 省略写法
        response.getWriter().print("<h1>你好,世界</h1>");
    }
}

2.3 响应重定向(location+302)

需求

用户访问CServlet后,服务器告诉浏览器重定向到DServlet

1630656986308.png

步骤分析

* 方式一
    // 1.设置状态码
        response.setStatus(302);
    // 2.设置响应头 Location
        response.setHeader("Location","重定向网络地址");

* 方式二
    // 1.response这哥们封装专门处理重定向的方法 (掌握)
        response.sendRedirect("重定向网络地址");

重定向特点

# 重定向是浏览器行为
1. 起码发起两次请求
2. 浏览器地址栏的url被修改了(指向最后访问的地址)
3. 可以访问服务器外部资源
4. 不能通过request域对象共享数据
<a href="CServlet?msg=shengjian">07_重定向</a> <br>
package com.itheima04.redirect;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/CServlet")
public class CServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 接收请求
        String msg = request.getParameter("msg");
        System.out.println("CServlet:" + msg);
        //2. 业务处理
        System.out.println("CServlet: 收钱,打单...");
        //3. 响应: 重定向
            // 告诉浏览器准备再次发起请求
//        response.setStatus(302);
            // 告诉浏览器重定向的地址
//        response.setHeader("location","DServlet");

        //等价于以上两行
        response.sendRedirect("DServlet");
    }
}
package com.itheima04.redirect;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/DServlet")
public class DServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 接收请求 (因为这是一个全新的请求,没有携带参数,所以这就不获取了)
        System.out.println("接收到请求啦");
        //2. 业务处理
        System.out.println("开始装生煎");
        //3. 响应
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().print("生煎给你,慢点吃");
    }
}

请求转发与重定向的区别

从最终效果来看,请求转发和重定向非常相似

1. 哪个对象的方法
    1). 请求转发 : request
        request.getRequestDispatcher(转发地址).forward(request,response)        
    2). 重定向 : response
        response.sendRedirect(重定向地址); // 302状态码 + location头
2. 行为人
    请求转发 : 服务器内部行为,浏览器不知道服务器内部发生了什么
    重定向 : 浏览器行为  
            可以定向到其他服务器,但是请求转发只能在本服务器内
3. 请求次数
    请求转发 : 1次
    重定向: 起码2次    
            浏览器会限制重定向的次数,比如 google浏览器不超过20次    

4. 数据共享 (重要)
    请求转发: request域对象可以传递数据
    重定向 : 不可以用request域对象传递数据 
        重定向涉及多次请求, 超出一个request作用范围

5. 地址栏指向
    请求转发: 一开始设置的路径 AServlet
        一次请求
    重定向: 最终指向最后一次请求的地址 DServlet        
        多次请求,后续请求地址覆盖前面请求地址
6. 总结
    请求转发: 就一次请求, 可以使用request域传递数据,不可以转发到其他服务器
    重定向: 涉及多次请求,不可以用request域传递数据,但是可以定向到其他的服务器

7. 使用
    a. 如果用request域对象传递数据,并且两个资源在同服务器内 -> 请求转发
    b. 如果不需要request传递数据,或两个资源在不同服务器上  -> 重定向