JavaWeb - Respnse响应消息、ServletContext对象
1.响应消息数据格式
1. 响应行
1. 组成:协议 / 版本 响应状态码 状态码描述 如:HTTP/1.1 200 OK
2. 响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态
1. 状态码都是3位数字
2. 分类:
1. 1xx:服务器接收客户端消息,但没有接收完成,等待一段时间后,发送1xx多状态码
2. 2xx:成功。代表:200
3. 3xx:重定向。代表:302(重定向) 304(访问缓存)
4. 4xx:客户端错误。代表:404(请求路径没有对应的资源)405(请求方式没有对应的doXxx方法)
5. 5xx:服务器端错误。代表:500(服务器内部出现异常)
2. 响应头
1. 格式:头名称:值
2. 常见的响应头:
1. Content - Type:服务器告诉客户端本次响应消息体数据格式以及编码格式
2. Content - disposition:服务器告诉客户端以什么格式打开响应体数据
* 值:
* in - line:默认值,在当前页面内打开
* attachment(filename=xxx):以附件形式打开响应体。文件下载时使用
3. 响应空行
4. 响应体
* 字符串格式:
2.Response
* 功能:设置相应消息
1. 设置响应行
1. 格式: HTTP/1.1 200 ok
2. 设置状态码:setStatus(int sc)
2. 设置响应头
1. setHeader(String name,String value)
3. 设置响应体
* 使用步骤:
1. 获取输出流
* 字符输出流:PrintWriter getWriter()
* 字节输出流:ServletOutputStream getOutputStream()
2. 使用输出流,将数据输出到客户端浏览器
response.setStatus(302);
response.setHeader("location","/web_servlet/respnseDemo01")
### 简单的重定向方法:sendRedirect()
* 重定向的特点:
1. 地址栏发生变化
2. 重定向可以访问其他站点(服务器)的资源
3. 重定向是两次请求。不能使用request域来共享数据了
* 转发的特点:
1. 转发地址栏路径不变
2. 转发只能访问当前服务器下的资源
3. 转发是一次请求,可以使用request对象来共享数据
* 面试题:forward和redirect的区别,既转发和重定向的区别
* 路径写法:
1. 路径分类
1. 相对路径:通过相对路径不可以确定唯一资源
* 如: . / index.html 或 ../ index.html
* 不以 / 开头,以 . 开头
* 规则:找到访问当前资源和目标资源的相对位置关系 . / 代表当前目录 . . / 代表后退一级的目录
2. 绝对路径:通过绝对路径可以确定唯一资源
* 如: http://localhost/web_servlet/ResponseDemo01 或 /web_servlet/ResponseDemo01
* 以 / 开头的路径
* 规则:判断定义的路径是给谁用的?判断请求将来从哪发出
* 给客户端浏览器使用:需要加虚拟目录(项目的访问路径),重定向就是客户端发出的,需要加虚拟目录
* 建议:之后动态获取虚拟目录 request.getContextPath() 来获取
* 如:<a> <href> 重定向
* 给服务器使用:不需要加虚拟目录 如:转发路径
3. 服务器输出字符数据到浏览器
* 步骤:
1. 获取字符输出流
2. 输出数据
* 注意:
* 乱码问题:
1. PrintWriter pw = response.getWriter(); 获取的流的默认编码是ISO-8859-1的拉丁编码
2. 设置该流的默认编码和响应体数据使用的编码:response.setContentType("text/html;charset=utf-8"); 在获取流之前设置
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //告诉浏览器,服务器发送的消息体数据的编码 response.setHeader("Content-type","text/html;charset=utf-8"); //简单形式设置编码 response.setContentType("text/html;charset=utf-8"); //1.获取字符输出流 PrintWriter pw = response.getWriter(); //2.输出数据 pw.write("hello response"); }
4. 案例:验证码
1. 本质:图片
2. 目的:防止恶意表单注册
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script> window.onload = function () { // 1.获取图片对象 var img = document.getElementById("checkcode"); // 2.绑定单击事件 img.onclick = function () { //加时间戳 var date = new Date().getTime(); //因为浏览器会从缓存里拿图片,而不是重新请求,所以我们修改路径,通过传参,达到修改了路径 img.src= "/servlet3/checkCode?" + date; }; document.getElementById("change").onclick = function () { // 1.获取图片对象 var img = document.getElementById("checkcode"); //加时间戳 var date = new Date().getTime(); //因为浏览器会从缓存里拿图片,而不是重新请求,所以我们修改路径,通过传参,达到修改了路径 img.src= "/servlet3/checkCode?" + date; } } </script> </head> <body> <img id="checkcode" src="/servlet3/checkCode"> <a id="change" href="">看不清,换一张</a> </body> </html>
package web.servlet; 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("/checkCode") public class CheckCode extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int width = 100; int height = 50; // 1.创建一对象,在内存中的图片(验证码图片对象) BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); // 2. 美化图片 // 2.1 填充背景色 Graphics graphics = image.getGraphics(); //画笔对象 // 设置画笔填充颜色 graphics.setColor(Color.PINK); graphics.fillRect(0,0,width,height); // 2.2 画边框 graphics.setColor(Color.BLUE); graphics.drawRect(0,0,width-1,height-1); String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; // 生产随机角标 Random random = new Random(); for (int i = 1; i <= 4; i++) { int index = random.nextInt(str.length()); // 获取字符 char random_char = str.charAt(index); // 2.3 写验证码 graphics.drawString(random_char+"",width/5*i,height/2); } // 2.4画干扰线 graphics.setColor(Color.GREEN); for (int i = 0; i < 10; i++) { // 随机生成坐标点 int x1 = random.nextInt(width); int x2 = random.nextInt(width); int y1 = random.nextInt(height); int y2 = random.nextInt(height); graphics.drawLine(x1,y1,x2,y2); } // 3.将图片输出到页面展示 ImageIO.write(image,"jpg",response.getOutputStream()); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request,response); } }
5. ServletContext对象
1. 概念:代表整个web应用,可以和程序的容器(服务器)来通信
2. 获取:
1. 通过request对象来获取
request.getServletContext();
2. 通过HttpServlet获取
this.getServletContext();
3. 功能:
1. 获取MIME类型:
* MIME类型:在互联网通信过程中定义的一种文件数据类型
* 格式:大类型 / 小类型 text / html image / jpeg
* 获取:String getMimeType(String file)
2. 域对象:共享数据
1. setAttribute(String name,Object value)
2. getAttribute(String name)
3. removeAttribute(String name)
* ServletContext对象范围:可共享所有用户所有请求的数据
3. 获取文件的真实(服务器)路径(查看文件的工作空间路径)Using CATALINA_BASE 对应的路径去找 conf / Catalina / localhost 下的xml文档中
1. 方法:String getRealPath(String path)
* web目录下的资源路径写法:/ a.txt
* WEB - INF目录下的资源路径写法:/ WEB - INF / a.txt
* src目录下的资源路径写法:WEB - INF / classes / a.txt
6. 案例:图片及任何文件下载
* 使用响应头设置资源的打开方式:
* content - dispostion:attachment;filename=xxx
* 步骤:
1. 定义页面,编辑超链接的href属性,指向servlet,传递资源名称filename
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>图片下载</title> </head> <body> <a href="img/九尾.jpg">九尾图片</a> <hr/> <a href="/case_download/downloadServlet?filename=九尾.jpg">九尾图片</a> </body> </html>
2. 定义servlet
1. 获取文件名称
2. 使用字节输入流加载文件进内存
3. 指定response的响应头:content - dispostion:attachment;filename=xxx
4. 将数据写出到response的输出流
package download; import utils.DownLoadUtils; 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 doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1.获取请求参数,文件名称 String filename = request.getParameter("filename"); // 2. 使用字节输入流加载文件进内存 // 2.1 找到文件的服务器路径 ServletContext servletContext = this.getServletContext(); String realPath = servletContext.getRealPath("/img/" + filename); // 2.2 用字节流关联 FileInputStream fis = new FileInputStream(realPath); //3. 设置response的响应头 //3.1 设置响应头类型:content-type String mimeType = servletContext.getMimeType(filename);//获取文件的mime类型 response.setHeader("content-type",mimeType); //3.2 设置响应头打开方式 content-disposition //解决中文文件名问题 //1. 获取user-agent请求头 //2. 使用工具类方法编码文件名 String agent = request.getHeader("user-agent"); filename = DownLoadUtils.getFileName(agent, filename); response.setHeader("content-disposition","attachment;filename="+filename); //4. 将输入流的数据写入到输出流中 ServletOutputStream sos = response.getOutputStream(); byte[] buff = new byte[1024*8]; int len = 0; while ((len = fis.read(buff))!=-1){ sos.write(buff,0,len); } fis.close(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request,response); } }
* 问题:
* 中文文件名问题:
* 解决思路:
1. 获取客户端使用的浏览器版本信息
2. 根据不同的版本信息,响应不同的数据,设置filename的编码格式不同
package utils; import sun.misc.BASE64Encoder; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Base64; public class DownLoadUtils { public static String getFileName(String agent, String filename) throws UnsupportedEncodingException { if (agent.contains("MSIE")) { // IE浏览器 filename = URLEncoder.encode(filename, "utf-8"); filename = filename.replace("+", " "); } else 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; } }