JavaWeb——【Response】——一篇文章学会Respose,值得你一看!

Response


1.概述

Servlet 最主要的作用就是处理客户端请求,并向客户端做出响应。为此,针对 Servlet 的每次请求,Web 服务器在调用 service()之前,都会创建两个对象,分别是 HttpServlet Request和HttpServletResponse,

其中,HttpServletRequest 用于封装 HTTP 请求消息,简称 request 对象。
HttpServlet Response 用于封装 HTTP 响应消息,简称 response 对象。

需要注意的是,在 Web 服务器运行阶段,每个 Servlet 都只会创建一个实例对象。然而,每次 HTTP 请求,Web 服务器都会调用所请求 Servlet 实例的 service(HttpServletRequest request, HttpServletResponse response)方法,重新创建一个 request 对象和一个 response 对象。
在这里插入图片描述

2.HttpServletResponse 对象

在Servlet API 中,定义了一个 HtpServletResponse 接口,它继承自 ServletResponse ,专门用来封装 HTTP 响应消息

HTTP 响应消息分为三部分:

  • 状态行
  • 响应消息头
  • 消息体

在HttpservletResponse 接口中定义了向客户端发送响应状态码、响应消息头、响应消息体的方法

2.1.发送状态码的方法

2.1.1. setStatus(int status)方法

该方法用于设置 HTTP 响应消息的状态码

由于响应状态行中的状态描述信息直接与状态码相关,而 HTTP 版本由服务器确定,因此,只要通过 setStatus( int status)方法设置了状态码,即可实现状态行的发送。

需要注意的是,正常情况下,Web 服务器会默认产生一个状态码为 200 的状态行

2.1.2. setError(int sc)方法

该方法用于发送表示错误信息的状态码,例如 404,状态码表示找不到客户端请求的资源,在response 对象中

2.2.发送响应消息头的相关方法

当Servlet 向客户端回送响应消息时,由于 HTTP 的响应头字段有很多种,为此,在 HttpServletResponse 接口中,定义了一系列设置 HTTP 响应头的方法字段的方法。

addHeader(String name, String value)

addIntHeader(String name, int value)
设置响应头

addIntHeader(String name, String value)

setIntHeader(String name, int value)

设置整数类型的响应头

SetContentLength(int len)

设置响应消息的实体内容大小,单位为字节

setContentType(String type)

设置 servlet 输出内容的 MIME 类型,对于 HTTP 来说,就是设置 Content-Type 响应头字段的值

2.3.发送响应消息相关方法

由于在 HTTP 响应消息中,大量的数据都是通过响应消息体传递的,因此 ServletResponse 遵循以 1O 流传递大量数据的设计理念,在发送响应消息体时,定义了两个与输出流相关的方法

getOutputStream()方法

该方法所获取的字节输出流对象为 ServletOutputStream 类型 。 由于 ServletOut putStream 是 OutputStream 的子类,它可以直接输出字节数组中的二进制数据。因此,要想输出二进制格式的响应正文,就需要使用 getOutputStream() 方法

get Writer()方法

该方法所获取的字符输出流对象为 PrintWriter 类型。由于 PrintWriter 类型的对象可

以直接输出字符文本内容,因此,要想输出内容全为字符文本的网页文档,需要使用getWriter()方法

代码示例


/**

*演示HttpServletResponse输出流 */

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

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

//方式一

//String data = "jifeng";

//OutputStream stream = response.getOutputStream();

//stream.write(data.getBytes());

//方式二

String data = "jifeng";

PrintWriter stream = response.getWriter(); stream.write(data);

//注意:对象的getOutputStream() 和 getWriter() 方法都可以输出数据,但是两个方法是互斥的。

}

}

注意:对象的 getOutputStream() 和 getWriter() 方法都可以输出数据,但是两个方法是互斥的,不可同时使用。

3.中文输出乱码问题

由于计算机中的数据都是以二进制形式存储的,因此,当传输文本时,就会发生字符和字节之间的转换。

字符与字节之间的转换是通过查码表完成的,将字符转换成字节的过程称为编码,将字节转换成字符的过程称为解码,如果编码和解码使用的码表不一致就会导致乱码问题。

3.1.乱码解决

只需要保证响应数据的编码和浏览器解析的编码一致即可

response 缓冲区的默认编码是 iso8859-1,此码表中没有中文,可以通过 response 的 setCharacterEncoding(String charset) 设置 response 的编码

将response 缓冲区的编码设置成 UTF-8,但浏览器的默认编码是本地系统的编码,中文系统默认编码是 GBK,我们可以 手动修改浏览器的编码是 UTF-8。

还可以在代码中指定浏览器解析页面的编码方式,通过 response 的 setContentType(Stringtype)方法指定页面解析时的编码是 UTF-8

response.setContentType(“text/html;charset=UTF-8”);

上面的代码不仅可以指定浏览器解析页面时的编码,同时也内含 setCharacterEncoding 的功能

开发中只要编写 response.setContentType(“text/html;charset=UTF-8”);就可以解决页面输出中文乱码问题。

4.文件下载

文件下载的实质就是文件拷贝,将文件从服务器端拷贝到浏览器端。

所以文件需要使用到 IO 流将服务器端的文件使用 InputStream 读取到,再使用

ServletOutputStream 写到 response 缓冲区中。

4.1.文件下载的基本方式

使用 a 标签直接指向服务器上的资源

a.xlsx
b.png
c.mp3
d.txt

文件存放截图
在这里插入图片描述

4.2. 访问 Servlet 直接进行下载

要实现文件下载单独用 a 标签是不那么完美的。那么,究竟该怎样实现文件下载功能呢?此时,就需要使用 Servlet 编码读取要下载的文件,然后写到响应流中以达到用户下载文件的目的。

ServletOutputStream 抽象类。利用这个输出流可以将数据返回到客户端想浏览器直接输出字节流,浏览器会根据对应的格式进行解析

 public class ByteServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

//使用 response 获得字节输出流
ServletOutputStream out = response.getOutputStream();

//获得服务器上的图片,此时浏览器会直接解析并显示

String realPath = this.getServletContext().getRealPath("download/b.png");

//xlsx 文件浏览器并不能解析,所以会以下载的方式进行

//String realPath = this.getServletContext().getRealPath("download/a.xlsx");

InputStream in = new FileInputStream(realPath);

int len = 0;

byte[] buffer = new byte[1024];

while ((len = in.read(buffer)) > 0) {

out.write(buffer, 0, len);

}

in.close();

out.close();

}

}

使用上面的代码还是有问题的,浏览器如果能够解析该文件,那么会直接将文件进行解析,并显示,并且不能解析的文件下载后,而且文件名也不对。需要进行如下操作
告诉客户端(浏览器)以下载的方式打开文件

通过文件的 MIME 类型去区分类型(tomcat/conf/web.xml)
文件的名字


//设置文件的类型


response.setContentType(this.getServletContext().getMimeType("download/


b.png"));


//告诉客户端(浏览器)以下载的方式打开文件,设置 Context-Dispostion 头,并设置文件名字


response.setHeader("Content-Disposition", "attachment;filename=b.png");

设置好两个头后,就可以实现文件下载的功能,并且文件也已经有名字了

4.3.下载指定文件

编写新的 Servlet,访问 Servlet 时传递指定的文件名字,下载指定的文件

HTM L 代码如下(添加多一个中文名字的文件)

给 Servlet 传递参数只需要在 URL 后面加?并且拼接文件名字参数即可(键值对新式)

<a href="/Response/downloadServlet2?filename=a.xlsx">a.xlsx</a><br> 
<a href="/Response/downloadServlet2?filename=b.png">b.jpg</a><br> 
<a href="/Response/downloadServlet2?filename=c.mp3">c.mp3</a><br> 
<a href="/Response/downloadServlet2?filename=d.txt">d.txt</a><br> 
<a href="/Response/downloadServlet2?filename=文本.txt">文本.txt</a><br>

Servlet 代码,主要改动的代码为:
获取传递过来的文件名,之后以该文件名获取 MIME 类型和读取文件

tring filename="download/"+request.getParameter("filename");//a.xlsx

主要代码如下

protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
//获得要下载的文件的名称

String filename = request.getParameter("filename");// a.xlsx

//设置文件的类型

response.setContentType(this.getServletContext().getMimeType("downlo ad/" + filename));

//告诉客户端(浏览器)以下载的方式打开文件,设置 Context-Dispostion 头,并设置文件名字

response.setHeader("Content-Disposition", "attachment;filename=" + filename);

//使用 response 获得字节输出流

ServletOutputStream out = response.getOutputStream();

//获得服务器上的图片,此时浏览器会直接解析并显示

String realPath = this.getServletContext().getRealPath("download/" + filename);

//xlsx 文件浏览器并不能解析,所以会以下载的方式进行

//String realPath =

//this.getServletContext().getRealPath("download/a.xlsx");

InputStream in = new FileInputStream(realPath);

int len = 0;

byte[] buffer = new byte[1024];

while ((len = in.read(buffer)) > 0) {

out.write(buffer, 0, len);

}

in.close();

out.close();

}

4.4.中文名字乱码(了解)

上面的代码,如果下载中文文件,页面在下载时会出现中文乱码或不能显示文件名的情况(也

有可能发生文件找不到 404),对于 GET 请求,参数追加到地址栏,会使用 UTF-8 编码,服务器(Tomcat)接受到请求之后,使用 ISO-8859-1 解码,所以会出现乱码,导致找不到资源。

因此,我们在获取文件名时,必须将文件名用 UTF-8 解码

String filename = new String(request.getParameter(“filename”).getBytes(“iso-8859-1”),“utf-8”);

并且不同的浏览器默认对下载文件的编码方式不同,IE 是 UTF-8 编码方式,而火狐浏览器是

Base64 编码方式。所里这里还需要解决浏览器兼容性问题,解决浏览器兼容性问题的首要

任务是要辨别访问者是 IE 还是火狐(其他),通过 Http 请求体中的一个属性可以辨别

在这里插入图片描述

代码

//获得请求头中的 User-Agent

String agent = request.getHeader("User-Agent"); //根据不同浏览器进行不同的编码

String filenameEncoder = "";

if (agent.contains("MSIE")) {

//IE 浏览器

filenameEncoder = URLEncoder.encode(filename, "utf-8"); filenameEncoder = filenameEncoder.replace("+", " ");

} else if (agent.contains("Firefox")) {

//火狐浏览器

BASE64Encoder base64Encoder = new BASE64Encoder(); filenameEncoder = "=?utf-8?B?"

+base64Encoder.encode(filename.getBytes("utf-8")) + "?=";

} else {

//其它浏览器

filenameEncoder = URLEncoder.encode(filename, "utf-8");

}

文件名

//告诉客户端(浏览器)以下载的方式打开文件,设置 Context-Dispostion 头,并设置文件名字

response.setHeader("Content-Disposition", "attachment;filename=" + filenameEncoder);

完整代码


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

//获得要下载的文件的名称

String filename = new String(request.getParameter("filename"));

//设置文件的类型

response.setContentType(this.getServletContext().getMimeType("downlo ad/" + filename));

// 获得请求头中的 User-Agent

String agent = request.getHeader("User-Agent");

//根据不同浏览器进行不同的编码

String filenameEncoder = ""; if (agent.contains("MSIE")) {

//IE 浏览器

filenameEncoder = URLEncoder.encode(filename, "utf-8"); filenameEncoder = filenameEncoder.replace("+", " ");

} else if (agent.contains("Firefox")) {

//火狐浏览器

BASE64Encoder base64Encoder = new BASE64Encoder(); filenameEncoder = "=?utf-8?B?" +

base64Encoder.encode(filename.getBytes("utf-8")) + "?=";

} else {

//其它浏览器

filenameEncoder = URLEncoder.encode(filename, "utf-8");

}

//告诉客户端(浏览器)以下载的方式打开文件,设置 Context-Dispostion 头,并设置文件名字

response.setHeader("Content-Disposition", "attachment;filename=" + filenameEncoder);

//使用 response 获得字节输出流

ServletOutputStream out = response.getOutputStream();

//获得服务器上的图片,此时浏览器会直接解析并显示

String realPath = this.getServletContext().getRealPath("download/" + filename);

//xlsx 文件浏览器并不能解析,所以会以下载的方式进行

//String realPath =




//this.getServletContext().getRealPath("download/a.xlsx"); InputStream in = new FileInputStream(realPath);

int len = 0;

byte[] buffer = new byte[1024];

while ((len = in.read(buffer)) > 0) {

out.write(buffer, 0, len);

}

in.close();

out.close();

}

5.请求重定向

在某些情况下,针对客户端的请求,一个 Servlet 类可能无法完成全部工作。这时可以使用请求重定向来完成。

所谓请求重定向,指的是 Web 服务器接受到客户端的请求后,可能由于某些条件限制,不能访问当前请求 URL 所指向的 Web 资源,而是指定了一个新的资源路径,让客户端重新发送请求为了实现请求重定向,在 HttpServletResponse 接口中,定义了一个 sendRedirect() 方法,该方法用于生成 302 响应码和 Location 响应头,从而通知客户端重新访问 Location 响应头中指定的 URL

重定向工作原理

在这里插入图片描述

当客户端访问 Servlet1 时,由于在 Servlet 中调用了 sendRedirect() 方法将请求重定向到 Servlet2 中,因此,Web 服务器在收到 Servlet1 的响应消息后,立刻向 Servlet2 发送请求 Servlet2 对请求处理完毕后,再将响应消息回送给客户端。

代码示例

访问 Servlet1,会重定向到 Servlet2

public class Servlet1 extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

// 重定向到Servlet2

response.sendRedirect("/Response/servlet2");

}

}



public class Servlet2 extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

response.getWriter().write("hello Servlet2");

}

}

回忆之前的登陆案例,登陆失败也可以用的对象回到登陆界面,如下代码
在这里插入图片描述

6.验证码

6.1.自己编写代码

使用 Servlet 完成验证码功能,截图如下

在这里插入图片描述
绘制验证码图片的 Servlet(了解)

public class ImgServlet extends HttpServlet {

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

BufferedImage bfi = new BufferedImage(68, 22, BufferedImage.TYPE_INT_RGB); // 图像缓冲区

Graphics g = bfi.getGraphics();

StringBuffer sb = new StringBuffer();

/* 画背景框 */

Color color = new Color(200, 215, 250);

g.setColor(color);

g.fillRect(0, 0, 68, 30);

/* 第一个数字 */

Random r = new Random();

int tmp1 = r.nextInt(20);

g.setColor(new Color(r.nextInt(100), r.nextInt(100), r.nextInt(100))); // 设置随机颜色

g.drawString(tmp1 + "", 3, 18);

/* 加号 */

g.setColor(new Color(r.nextInt(100), r.nextInt(100), r.nextInt(100))); // 设置随机颜色

g.drawString("+", 18, 18);

/* 第二个数字 */




int tmp2 = r.nextInt(20);

g.setColor(new Color(r.nextInt(100), r.nextInt(100), r.nextInt(100))); // 设置随机颜色

g.drawString(tmp2 + "", 33, 18);

/* 等号 */

g.setColor(new Color(r.nextInt(100), r.nextInt(100), r.nextInt(100))); // 设置随机颜色

g.drawString("=", 48, 18);

/* 问号 */

g.setColor(new Color(r.nextInt(100), r.nextInt(100), r.nextInt(100))); // 设置随机颜色

g.drawString("?", 57, 18);

/* 保存到 session */

int result = tmp1 + tmp2;

request.getSession().setAttribute("verifycode", result + ""); /* 写入 response 输出流 */

ImageIO.write(bfi, "JPG", response.getOutputStream());

}

}

配置 ImgServlet

<servlet>

<servlet-name>ImgServlet</servlet-name>

<servlet-class>com.jf.weidong.ImgServlet</servlet-class> </servlet>

<servlet-mapping>

<servlet-name>ImgServlet</servlet-name>

<url-pattern>/imgServlet</url-pattern>

</servlet-mapping>

表单界面

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>Insert title here</title>

</head>

<body>

<form action="/VerificationCode/loginServlet" method="post"> 姓名:<input type="text" name="name">

<br>

年龄:<input type="text" name="age">

<br>

验证码:<input type="text" name="verifycode" size=6 />




<img onclick="reloadCode()" src="/VerificationCode/imgServlet"

id="img" /> <br> <input type="submit" value="提交">

</form>

</body>

<script>

function reloadCode() {

//浏览器会对同一 url 的图像进行缓存,利用附加一个随机数,来避免客户端浏览器使用缓存(每次请求 url 都不同)

document.getElementById("img").src = "/VerificationCode/imgServlet?"+ Math.random();

}

</script>

</html>

登陆的 LoginServlet

public class LoginServlet extends HttpServlet { @Override

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

response.setContentType("text/html;charset=utf-8"); String re = request.getParameter("verifycode"); String answer = (String)

request.getSession().getAttribute("verifycode"); PrintWriter out = response.getWriter(); if (re.equals(answer)) {
out.printf("验证成功");

} else {

out.printf("验证失败");

}

out.flush();

out.close();

}

}

6.2. 使用第三方组件 kaptcha

1.添加 kaptcha 的 jar 包到项目 lib 库,下载地址 http://www.java2s.com/Code/Jar/k/Downloadkaptcha23jar.htm

2.配置 web.xml


<servlet>

<servlet-name>Kaptcha</servlet-name>




<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet -class>

</servlet>

<servlet-mapping>

<servlet-name>Kaptcha</servlet-name>

<url-pattern>/kaptcha.jpg</url-pattern>

</servlet-mapping>

表单

<body>

<form action="/VerificationCode/loginServlet_kaptcha" method="post"> 姓名:<input type="text" name="name">

<br>

年龄:<input type="text" name="age">

<br>

验证码:<input type="text" name="verifycode" size=6 />

<img onclick="reloadCode()" src="/VerificationCode/kaptcha.jpg" id="img" /> <br> <input type="submit" value="提交">

</form>

</body>

<script>

function reloadCode() {

//浏览器会对同一 url 的图像进行缓存,利用附加一个随机数,来避免客户端浏览器使用缓存(每次请求 url 都不同)


document.getElementById("img").src="/VerificationCode/kaptcha.jpg?"+ Math.random();

}

</script>

LoginServlet_kaptcha

public class LoginServlet_kaptcha extends HttpServlet { @Override

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

response.setContentType("text/html;charset=utf-8"); String re = request.getParameter("verifycode"); /*获取 session 中的验证码*/

String captcha = ((String) request.getSession().getAttribute( com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY));

PrintWriter out = response.getWriter();

if (re.equals(captcha)) {

out.printf("验证成功");




} else {

out.printf("验证失败");

}

out.flush();

out.close();

}

}
发布了230 篇原创文章 · 获赞 250 · 访问量 28万+

猜你喜欢

转载自blog.csdn.net/weixin_42247720/article/details/103792125
今日推荐