Servlet Response 返回服务器的响应


Servlet Response

  • Response 对象表示 web 服务器给浏览器返回的响应信息。
  • 开发人员可以使用 Response 对象的方法,设置要返回给浏览器的响应信息。

1. Response 的体系结构

ServletResponse 接口
			|	
HttpServletResponse 接口
			|	
org.apache.catalina.connector.ResponseFacade 实现类(由 Tomcat 厂商提供的实现类)

2 设置 HTTP 响应消息

a. 响应行

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

  • 相关 API:

    1. 设置状态码:
      void setStatus(int ss)
  • 200 OK:请求已成功,请求所希望的响应头或数据体将随此响应返回。出现此状态码是表示正常状态。

  • 302 Move temporarily:重定向,请求的资源临时从不同的 URI响应请求。

  • 304 Not Modified:从缓存中读取数据,不从服务器重新获取数据。

  • 403 Forbidden:服务器已经理解请求,但是拒绝执行它,一般在权限不够的时候常见。

  • 404 Not Found:请求失败,请求所希望得到的资源未被在服务器上发现。

  • 405 Method Not Allowed:请求行中指定的请求方法不能被用于请求相应的资源。

  • 500 Internal Server Error:服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。

b. 响应头

  • 格式:响应头名称:响应头的值,例如 Location:https://blog.csdn.net/Regino
  • 相关 API:
    1. 设置指定头名称和对应的值:
      void setHeader(String name, String value)

c. 响应体

  • 相关 API(输出流对象):
    1. 获取输出字符流:
      PrintWriter getWriter()
    2. 获取输出字节流:
      ServletOutputStream getOutputStream()

注意:

  • 在同一个 Servlet 中,二种类型的输出流不能同时存在,互斥。
  • 向浏览器输出文件时用字节流,输出文本内容时用字符流。

3. 案例:响应重定向

a. 主要需求

  • 用户访问 AServlet 后,服务器告诉浏览器重定向到 BServlet。

b. 步骤分析

  • 方式一:
    1. 设置状态码(响应行):
      response.setStatus(302);
    2. 设置响应头 Location:
      response.setHeader("Location","重定向网络地址");
  • 方式二:
    1. Response 封装了专门处理重定向的方法:
      response.sendRedirect("重定向网络地址");

c. 重定向特点

  1. 地址栏会发生改变;
  2. 重定向是二次请求;
  3. 重定向是客户端(浏览器)行为,可以跳转到服务器外部资源。
  4. 不能使用 Request 域共享数据

d. 代码实现

  • AServlet:
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;

@WebServlet("/AServlet")
public class AServlet extends HttpServlet {

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

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /*System.out.println("AServlet执行了....");
        // 1.设置状态码
        response.setStatus(302);
        // 2.设置响应头
        response.setHeader("Location","/webappPractice2/BServlet");*/

        // 1.Response封装的专门处理重定向的方法
        response.sendRedirect("/webappPractice2/BServlet");
    }
}
  • BServlet:
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;

@WebServlet("/BServlet")
public class BServlet extends HttpServlet {

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

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

    }
}

e. 效果展示

4. 请求转发与请求重定向的区别

a. Request 的请求转发

  • AServlet:
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;

@WebServlet("/AServlet")
public class AServlet extends HttpServlet {

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

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //往Request域中存储数据
        request.setAttribute("name","Regino");
        //请求转发到BServlet
        request.getRequestDispatcher("/BServlet").forward(request,response);
    }
}
  • BServlet:
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;

@WebServlet("/BServlet")
public class BServlet extends HttpServlet {

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

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //解决向浏览器输出中文乱码问题
        response.setContentType("text/html;charset=utf-8");
        //从Request域中取出数据
        String name = (String) request.getAttribute("name");
        //向浏览器输出
        response.getWriter().write("从Request域中获取到的数据:"+ name);
    }
}

b. 请求转发与请求重定向的原理

  • Request 转发请求原理:
    • Tomcat 服务器创建 AServlet 的对象后,又创建一对 Request 与 Response 对象并用来调用 AServlet 的 doPost 方法。
    • 当 Tomcat 服务器读到 AServlet 的转发请求时,会马上利用 AServlet 的那对 Request 与 Response 对象调用 BServlet 的 doPost 方法,并没有创建一对新的 Request 与 Response 对象。
  • Response 请求重定向的原理:
    • Tomcat 服务器创建 AServlet 的对象后,又创建一对 Request 与 Response 对象并用来调用 AServlet 的 doPost 方法。
    • 当 Tomcat 服务器读到 AServlet 的重定向请求时,会马上把该请求返回给浏览器;浏览器发送重定向请求后,Tomcat 服务器读到浏览器的第二次请求时,会马上创建 BServlet 的对象,然后用新的一对 Request 与 Response 对象调用 BServlet 的 doPost 方法。

c. 请求转发与请求重定向的区别

  1. 请求转发的时候浏览器地址栏不变化的,请求重定向浏览器地址栏是变化的。
  2. 请求转发浏览器发出一次请求,请求重定向浏览器发出了两次请求。
  3. 请求转发的服务器只有一对请求和响应对象,而请求重定向的服务器有两对请求和响应对象。
  4. 请求转发发生在服务器,而请求重定向发生在浏览器。
  5. 地址不同,重定向需要加 项目根路径/,而请求转发是模块内部的跳转,所以不用。
  • / 在进行资源跳转的时候,如果是给服务器去使用,那么 / 代表了 http://localhost:8080/项目根路径;如果是给浏览器去使用的时候,那么/ 代表了 http://localhost:8080/

d. 使用场景

  • 如果需要传递数据(Request域),使用转发;
  • 如果不需要传递数据(Request域),使用重定向。

5. 案例:响应定时刷新

a. 主要需求

  • 在当前页面停留 3 秒钟之后,跳转到个人博客首页。

b. 步骤分析

  1. 通过 Response 设置响应头 Refresh:
    response.setHeader("Refresh","间隔时间(秒);跳转页面");

c. 代码实现

  • RefreshServlet:
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;

@WebServlet("/refreshServlet")
public class RefreshServlet extends HttpServlet {

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

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 通过Response设置响应头Refresh
        response.setHeader("Refresh", "3;https://blog.csdn.net/Regino");
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("操作成功,3秒后跳转到Regino博客首页...");
    }
}

6. 案例:响应中文

a. 主要需求

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

b. 步骤分析

  1. 通过 Response 获取字符输出流:
    PrintWriter pw = response.getWriter();
  2. 通过字符输出输出文本:
    pw.write("中文....");

c. 解决中文乱码

  1. 指定服务器响应编码方式:
    response.setCharacterEncoding("GBK");
  2. 统一浏览器和服务器编码:
    response.setContentType("text/html;charset=utf-8");

request.setCharacterEncoding("utf-8"); 应该在获取请求参数之前设置,若已获取请求参数,此时还没有设置过编码,Tomcat 会设置默认 Post 请求参数编码为 ISO8859_1,那么再设置成 UTF-8 也无效了。所以应该用 response.setContentType("text/html;charset=utf-8");

d. 代码实现

  • EncodeServlet :
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;
import java.io.PrintWriter;

@WebServlet("/encodeServlet")
public class EncodeServlet extends HttpServlet {

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

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //  指定服务器响应编码方式
        // response.setCharacterEncoding("UTF-8");//出现乱码
        // 统一浏览器和服务器编码
        response.setContentType("text/html;charset=utf-8");

        // 1. 通过response获取字符输出流
        PrintWriter pw = response.getWriter();
        // 2. 通过字符输出输出文本
        pw.write("中文....");
    }
}

7. 综合案例:点击切换验证码

a. 主要需求

  • 在页面展示登录验证码,点击此验证码可以更换新的验证码 。
  • 作用:防止表单的恶意提交。

b. 用 Java 制作一个验证码

  1. 使用画图工具;
  2. 指定宽和高;
  3. 指定背景色;
  4. 生成四位随机数;
  5. 制作干扰线;
  6. 通过 Response 响应到浏览器。

  • CheckcodeServlet:
import javax.imageio.ImageIO;
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.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

@WebServlet("/CheckcodeServlet")
public class CheckcodeServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //  创建画布
        int width = 120;
        int height = 40;
        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        //  获得画笔
        Graphics g = bufferedImage.getGraphics();
        //  填充背景颜色
        g.setColor(Color.white);
        g.fillRect(0, 0, width, height);
        //  绘制边框
        g.setColor(Color.red);
        g.drawRect(0, 0, width - 1, height - 1);
        //  生成随机字符
        //  准备数据
        String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
        //  准备随机对象
        Random r = new Random();
        //  声明一个变量 保存验证码
        String code = "";
        //  书写4个随机字符
        for (int i = 0; i < 4; i++) {
            //  设置字体
            g.setFont(new Font("宋体", Font.BOLD, 28));
            //  设置随机颜色
            g.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));

            String str = data.charAt(r.nextInt(data.length())) + "";
            g.drawString(str, 10 + i * 28, 30);

            //  将新的字符 保存到验证码中
            code = code + str;
        }
        //  绘制干扰线
        for (int i = 0; i < 6; i++) {
            //  设置随机颜色
            g.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));

            g.drawLine(r.nextInt(width), r.nextInt(height), r.nextInt(width), r.nextInt(height));
        }

        //  将验证码 打印到控制台
        System.out.println(code);

        //  将验证码放到session中
        request.getSession().setAttribute("code_session", code);

        //  将画布显示在浏览器中
        ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
    }

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

c. 前端页面

  • index.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<img src="/webappPractice2/CheckcodeServlet" alt="Servlet随机验证码" id="img1"><br>
<script>
    // 给图片绑定一个点击事件
    document.getElementById('img1').onclick=function () {
        // 重置src路径,重写发送请求
        this.src='/webappPractice2/CheckcodeServlet?'+new Date().getTime();
        // 后面加一个毫秒值的时间戳来欺骗浏览器,否则因为是同一个请求,验证码不刷新
    }
</script>
</body>
</html>

8. 综合案例:文件下载

a. 文件目录

在这里插入图片描述

b. 主要需求

  • 用户点击页面的链接,浏览器开始下载文件。

c. 方式一:直接使用超文本链接下载文件

i. 前端页面

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Download</title>

</head>
<body>
<h3>文件下载</h3>
<h5>超文本链接下载</h5>
<a href="/webappPractice2/download/demo.doc">word文档</a><br>
<a href="/webappPractice2/download/test.zip">压缩包下载</a><br>
<a href="/webappPractice2/download/car.jpg">图片下载</a><br>
</body>
</html>

ii. 效果

iii. 缺点

  1. 如果要下载的是浏览器可解析的媒体类型,是直接打开而不是下载。
  2. 不能判断用户是否登录(VIP 权限问题),并进行限制。

d. 方式二:使用 Servlet 下载文件

i. 步骤分析

  1. 被下载文件的字节输入流:
    FileInputStream
  2. Response 字节输出流:
    ServletOutputStream
  3. 告知客户端下载文件的 MIME 类型(最新的浏览器此步骤可以省略):
    Content-Type:MIME类型
  4. 告知浏览器以附件的方式保存:
    Content-Disposition:attachment;filename=文件名

ii. 前端页面

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Download</title>

</head>
<body>
<h3>文件下载</h3>
<h5>超文本链接下载</h5>
<a href="/webappPractice2/download/demo.doc">word文档</a><br>
<a href="/webappPractice2/download/test.zip">压缩包下载</a><br>
<a href="/webappPractice2/download/car.jpg">图片下载</a><br>
<h5>Servlet下载</h5>
<a href="/webappPractice2/downloadServlet?filename=demo.doc">word文档</a><br>
<a href="/webappPractice2/downloadServlet?filename=test.zip">word文档</a><br>
<a href="/webappPractice2/downloadServlet?filename=car.jpg">图片下载</a><br>
</body>
</html>

iii. DownloadServlet

import javax.servlet.ServletContext;
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.FileInputStream;
import java.io.IOException;

@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {

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

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1.获取请求文件名
        String filename = request.getParameter("filename");
        // 2.获取文件真实路径,封装到字节输入流
        ServletContext servletContext = request.getServletContext();
        String realPath = servletContext.getRealPath("/download/" + filename);
        FileInputStream in = new FileInputStream(realPath);
        // 3.告诉浏览器MIME类型
        String mimeType = servletContext.getMimeType(filename);
        response.setContentType(mimeType);

        // 4.告诉浏览器以附件方式保存
        // 解决中文乱码和浏览器兼容性
        String userAgent = request.getHeader("user-agent");
        // 调用工具类处理
        filename = DownLoadUtils.getName(userAgent, filename);

        response.setHeader("content-disposition", "attachment;filename=" + filename);

        // 5.获取字节输出流
        ServletOutputStream out = response.getOutputStream();

        // 6.IO流的拷贝
        byte[] b = new byte[4096];// 4kb
        int len = -1;
        while ((len = in.read(b)) != -1) {
            out.write(b, 0, len);
        }

        // 7.释放资源
        out.close(); // out流对象,可以交给Tomcat关闭
        in.close();
    }
}

iv. 解决文件名中文乱码问题

  • 如果该下载文件名是中文的话,会出现乱码。
  • 需要考虑浏览器兼容性问题:
    • 谷歌和绝大多数的浏览器是通过 URL编码,相关方法:
      URLEncode() 编码
      URLDecode() 解码
    • 火狐浏览器是 base64编码
  • 判断浏览器不同编码的工具类 DownLoadUtils:
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import sun.misc.BASE64Encoder;

public class DownLoadUtils {
	public static String getName(String agent, String filename) throws UnsupportedEncodingException {
		if (agent.contains("Firefox")) {
			// 火狐浏览器
			BASE64Encoder base64Encoder = new BASE64Encoder();
			filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
		} else {
			// 其它浏览器
			filename = URLEncoder.encode(filename, "utf-8");
		}
		return filename;
	}
}

v. 效果


原文链接:https://qwert.blog.csdn.net/article/details/105556856

发布了401 篇原创文章 · 获赞 416 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Regino/article/details/105556856