Response响应组成&常见状态码&ServletContext&url编码&文件下载案例

回顾

请求有哪三个的组成部分

  1. 请求行:提交方式 URI HTTP/1.1
  2. 请求头:由多个键和值组成
  3. 请求体:发送给服务器的数据,只有POST才有请求体

获取请求行相关的方法

HttpServletRequest对象的方法 功能描述
String getMethod() 获取提交方式
String getRequestURI() 获取URI
String getProtocol() 获取协议和版本
String getQueryString() 获取查询字符串,?后面的参数

获取请求头相关的方法

请求方法 功能描述
String getHeader(String headName) 通过键获取值
Enumeration<String> getHeaderNames() 获取所有的请求头的名字

获取请求参数的方法

方法名 描述
String getParameter(String name) 通过参数名获取参数值
String[] getParameterValues(String name) 通过一个参数名获取所有同名的值,返回数组
Map<String,String[]> getParameterMap() 一次性获取所有的参数,封装成map
键是参数名,值是参数值
POST方式乱码解决方法 说明
setCharacterEncoding(“编码”) 解决POST方法的乱码问题

请求域有关的方法

request与域有关的方法 作用
Object getAttribute(“键”) 通过键获取值
setAttribute(“键”,Object数据) 向请求域中添加键和值,如果键不存在就是添加,存在就是修改
removeAttribute(“键”) 通过键删除键和值

转发与重定向的区别

区别 转发 重定向
地址栏会不会发生变化 不会
跳转方是哪一方 服务器端 浏览器端
请求域中数据是否丢失 不会
根目录地址 http://localhost:8080/项目访问地址/ http://localhost:8080/
Servlet是运行在服务器端的,服务器端的 / 根目录是指的web目录,web目录在浏览器上访问的时候地址:
服务器端是包含了项目的访问地址:http://localhost:8080/项目访问地址/ 网页是运行在浏览器端,浏览器端的 / 根目录是不包含项目名字的
http://localhost:8080/

学习目标

  1. 响应的格式

    1. 能够使用使用浏览器开发工具查看响应
    2. 能够理解响应行(状态行)的内容
    3. 能够理解常见的状态码
  2. 响应对象的编程
    1. 能够使用Response对象操作HTTP响应内容
    2. 能够处理响应乱码
    3. 能够完成文件下载案例
  3. 能够使用ServletContext域对象

学习内容

1. 响应的三个组成部分

目标

HTTP响应由哪三个组成部分

什么是HTTP响应

由服务器向浏览器发送的数据

数据准备

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>用户登录</title>
</head>
<body>
<h2>用户登录</h2>
<form action="login" method="post"><table><tr><td>用户名</td><td><input type="text" name="username"/></td></tr><tr><td>密码</td><td><input type="password" name="password"/></td></tr><tr><td colspan="2"><input type="submit" value="登录"/></td></tr></table>
</form>
</body>
</html>

响应信息的三个组成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b0uTC9vY-1598791362482)(assets/image-20200830090313984.png)]

小结

响应有哪三个组成部分?

  1. 响应行(状态码)
  2. 响应头:由键和值组成
  3. 响应体:服务器返回的数据,如果是网页看到的就是源码

2. 响应行、响应头、响应体的格式

目标

  1. 响应行的格式
  2. 响应头的格式
  3. 响应体的格式

响应行

组成:协议和版本 状态码 描述信息

HTTP/1.1 200 OK
HTTP/1.1 302 Found
HTTP/1.1 404 Not Found

响应头

响应头信息 说明
Location: http://www.itcast.cn 出现在302中,表示下一个要跳转的页面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DD1GdJ6I-1598791362499)(assets/image-20200830091153748.png)]
Server:Apache Tomcat 服务器的名字
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zwbdFq1P-1598791362507)(assets/image-20200830091331777.png)]
Content-Encoding: gzip 服务器端数据压缩的格式,浏览器需要知道压缩格式从而进行解压
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OS2Q7885-1598791362566)(assets/image-20200830091634611.png)]
Content-Length: 80 服务器返回的数据长度,单位是字节
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-59p8XLZN-1598791362569)(assets/image-20200830091744979.png)]
Content-Type: text/html; charset=utf-8 响应的类型和编码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V6M5Z2Bf-1598791362571)(assets/image-20200830091517641.png)]
Refresh:秒;url=/hello.html 过多久以后跳转到另一个页面
Content-Disposition: attachment; filename=文件名.扩展名 如果要实现文件的下载,必须指定这个响应头

响应体

有两种类型的数据

  1. 文本类型:网页,JS代码,CSS样式等

  2. 二进制类型:文件,图片,视频,音乐等

在Servlet中有相应的IO流:

  1. 文本类型:字符流,基类:Reader和Writer
  2. 二进制类型:字节流,基类:InputStream和OutputStream

小结:响应的组成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kvf7o1XC-1598791362574)(assets/1552645116046.png)]

3. 响应对象的方法:与状态码有关(了解)

目标

与响应行有关的方法

什么是HttpServletResponse对象

是一个接口,也就可以有不同的实现类,我们的程序就可以运行在不同的Web服务器上。

  1. 由谁提供实现类:由tomcat实现
  2. 由谁创建此对象:由tomcat创建这个对象

设置状态码的方法

HTTP/1.1 200 OK
状态码的方法 描述
setStatus(int status) 设置响应的状态码,比如:200
sendError(int sc) 发送错误码,通常400~500以上都属于错误码
sendError(int sc, String msg) 发送错误码,多了一个错误信息

演示代码

package com.itheima.servlet;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("/demo1")
public class Demo1ResponseLineServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//response.setStatus(203);  //直接写成状态码//也可以使用常量//response.setStatus(HttpServletResponse.SC_ACCEPTED);  //200//发送错误码//response.sendError(401);  //会出现错误页面response.sendError(401, "你吃早餐了吗?");  //会出现错误页面}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}
}

小结

状态码的方法 描述
setStatus(int status) 设置状态码
sendError(int sc) 发送错误码,可以带信息

4. 常见的状态码【重点】

目标

  1. 响应行中常见状态码的含义
  2. 404与405出现的原因

常见状态码的含义

200

表示服务器正常响应

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S5dnf4pp-1598791362592)(assets/image-20200830100225791.png)]

302

表示页面在浏览器端进行了页面的跳转

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OPMwBmYT-1598791362594)(assets/image-20200830100303244.png)]

304

静态页面使用了缓存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x9XUf2pa-1598791362596)(assets/image-20200830100331603.png)]

404

  1. 地址栏输入错误,注:在Tomcat是区分大小写的。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oMyRZTh5-1598791362598)(assets/image-20200830100419834.png)]

  2. WEB-INF

    不能直接访问WEB-INF目录下的资源,如果访问也会出现404错误

  3. 重定向或转发地址不正确

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0T9Jcrm6-1598791362600)(assets/image-20200830100820032.png)]

    //request.getContextPath()的作用:获取当前项目的访问地址
    System.out.println(request.getContextPath());  //地址:/day27_02_login_war_exploded
    response.sendRedirect(request.getContextPath() + "/failure.html");
    

405

  1. 如果使用GET方法提交,Servlet中没有doGet方法

  2. 如果使用POST方法提交,Servlet中没有doPost方法

如果直接在浏览器上输入访问地址,使用的是GET方法,只有一种情况是POST,就是表单提交的时候method=“POST”

500

服务器出现异常,通常是你的代码有问题

小结

状态码 含义
200 服务器正常的响应
302 页面在浏览器端进行了页面跳转
304 静态页面进行了缓存
404 找不到资源
405 没有doGet或doPost方法
500 服务器代码有错误

5. 响应对象:响应头相关方法

目标

  1. 学习响应头相关的方法
  2. 案例:过3秒跳转到其它页面

响应头的方法

响应头的方法 描述
void setHeader(String name,
String value)
向浏览器设置指定的响应头,指定名字和值
void setContentType(String type) 相当于这个方法:
setHeader(“Content-Type”,“text/html;charset=utf-8”)
因为这个响应头使用频繁,所以专门有一个方法
response.setContentType("text/html;charset=utf-8");

案例:设置响应头过3秒跳转

步骤

  1. 创建ResponseRefreshServlet
  2. 调用setHeader,设置消息头(“Refresh”,“3;url=http://www.itcast.cn”)

效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rdat1NTp-1598791362601)(assets/image-20200830102744446.png)]

代码

package com.itheima.servlet;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("/demo2")
public class Demo2ResponseRefreshServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/html;charset=utf-8");PrintWriter out = response.getWriter();out.print("过3秒跳转到传智播客的官网");//设置refresh响应头response.setHeader("refresh","3;url=http://www.itcast.cn");}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}
}

案例:使用location实现页面跳转

步骤

  1. 只设置location响应头
  2. 同时设置302状态码
  3. 使用重定向的方法跳转

效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MjvbSB3Z-1598791362604)(assets/image-20200830105759715.png)]

代码

package com.itheima.servlet;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("/demo4")
public class Demo4LocationServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {/*//设置响应头location进行页面跳转response.setHeader("location","login.html");//还要设置状态码为302response.setStatus(302);*///使用重定向跳转,重定向本质上就是上面的2行代码response.sendRedirect("login.html");}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}
}

小结

响应头的方法 描述
void setHeader(String name, String value) 设置响应头的它的值
void setContentType(String type) 设置一个特殊的响应头:content-type

6. 案例:响应头数据压缩

目标

在服务器端对数据压缩后在浏览器端显示出来

需求

使用数据压缩之后再从服务器传输数据到浏览器, 可以减少网络的传输量,提高网页的下载速度。

GZIPOutputStream类的方法

构造方法 说明
GZIPOutputStream(OutputStream out) 创建一个压缩字节输出流,参数是被压缩的输出流。
下面的案例中需要压缩响应的字节输出流
GZIPOutputStream类的方法 说明
public void write(byte[] b) 把字节数据输出,参数是要输出的字节数组
void finish() 清空缓存,结束输出

步骤

  1. 如果需要对数据进行压缩,需要使用压缩流,将响应的输出流做为参数。

  2. 使用压缩流的write方法:首先会对数据进行压缩处理,然后调用传递进去OutputStream对象的write方法把压缩的数据写出去。

  3. 完成将压缩数据写入输出流的操作,如果没有调用结束的方法,则浏览器上看不到东西。

要点

要设置相应响应头信息,告诉浏览器目前内容是以压缩包的形式传输的,不然就会以附件的方式下载下来了。

response.setHeader("Content-Encoding","gzip")

响应头

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jal35jet-1598791362605)(assets/image-20200830104008095.png)]

代码

package com.itheima.servlet;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.OutputStream;
import java.util.zip.GZIPOutputStream;@WebServlet("/demo3")
public class Demo3GzipServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//设置响应头,告诉浏览器,服务器端数据是经过了压缩,压缩格式是gzipresponse.setHeader("Content-Encoding","gzip");//创建一个比较大的字节数组StringBuilder sb = new StringBuilder();for (int i = 0; i < 5000; i++) {sb.append("abcdefg29374slkjdfsef");}//在服务器上输出数据原来的长度System.out.println("长度是:" + sb.length());//获取响应的输出流OutputStream out = response.getOutputStream();//创建压缩输出流GZIPOutputStream outputStream = new GZIPOutputStream(out);//直接使用字节输出流输出//输出到浏览器上,将字符串转成一个字节数组输出outputStream.write(sb.toString().getBytes());//输出完毕要清空缓存outputStream.finish();}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}
}

小结

需要设置一个响应头:

response.setHeader("Content-Encoding","gzip");
GZIPOutputStream类的方法 说明
public void write(byte[] b) 将压缩后的字节写到浏览器上
void finish() 清空缓存

7. 响应体:处理响应乱码的问题【重点】

目标

  1. 响应体数据的两种方式
  2. 处理汉字乱码的问题

响应体的两种数据

  1. 字符流:Writer
  2. 字节流:OutputStream
响应体的方法 描述
OutputStream getOutputStream() 获取字节输出流
PrintWriter getWriter() 获取字符输出流,字符流用来输出文本

代码

package com.itheima.servlet;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("/demo5")
public class Demo5WriterServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//告诉浏览器,我服务器使用的是什么编码//response.setContentType("text/html;charset=utf-8");//纯文本的内容,标签是不起作用的response.setContentType("text/plain;charset=utf-8");/*1. 告诉浏览器,我服务器使用的是什么编码2. 设置了响应字符输出流的编码3. 设置响应内容类型*///字符流必须设置编码//设置响应的编码,请求对象也有相同的方法,但浏览器并不知道你用的是什么编码//response.setCharacterEncoding("utf-8");PrintWriter out = response.getWriter();out.print("<h1>你好,打印流</h1>");}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}
}

小结

content-type响应头的作用:

  1. 告诉浏览器,服务器使用的编码
  2. 设置了响应字符输出流的编码
  3. 设置了响应的内容类型

8. 如何得到ServletContext对象【重点】

目标

如何得到上下文对象

什么是ServletContext

概念:每个Web模块或工程都会有一个对应的上下文对象,它们之间是一对一的关系。

实现以下三个功能:

  1. 读取web.xml中全局的配置参数
  2. 获取当前工程的资源,转成字节输入流对象
  3. 获取资源在服务器上真实地址

得到上下文对象(上下文域)的方法

ServletConfig接口中方法 描述
ServletContext getServletContext() 调用这个方法就可以获取上下文对象,
我们的Servlet中已经继承了这个方法
所以直接在类中调用就可以了。

回顾Servlet的继承结构

创建模块没有勾选web

  1. 在创建好的模块上点右键

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5CJ31WL9-1598791362607)(assets/image-20200830113230288.png)]

  2. 勾选web模块

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H2sBmPlS-1598791362608)(assets/image-20200830113256543.png)]

  3. 这时tomcat并没有自动部署,要手动去部署

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1FHHxeDC-1598791362609)(assets/image-20200830113328662.png)]

得到ServletContext的代码

package com.itheima.servlet;import javax.servlet.ServletContext;
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("/demo1")
public class Demo1ServletContextServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//获取上下文对象ServletContext application = getServletContext();//输出到服务器上: org.apache.catalina.core.ApplicationContextFacade@48086280//它是一个接口,实现类由Tomcat//public class ApplicationContextFacade implements ServletContext System.out.println(application);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}
}

小结

  1. 每个Web项目对应几个上下文对象?

    1个

  2. 如何得到ServletContext?

    getServletContext(),它由tomcat实现,由tomcat实例化对象

9. ServletContext:读取全局的配置参数【了解】

目标

获取全局的配置参数

ServletContext方法

与以前学的ServletConfig接口中方法是一样的,但ServletConfig接口中的方法只能获取当前Servlet初始化的值,其它Servlet是不能获取这个参数值的

需求

在web.xml中设置2个全局的参数,username=NewBoy, age=21 在不同的Servlet中去获取这2个值,并使用上面2个方法。

代码

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><!--配置全局的初始参数,可以供所有的servlet使用 --><context-param><param-name>user</param-name><param-value>NewBoy</param-value></context-param><context-param><param-name>age</param-name><param-value>21</param-value></context-param><servlet><servlet-name>demo2</servlet-name><servlet-class>com.itheima.servlet.Demo2InitParamServlet</servlet-class><!--局部配置参数--><init-param><param-name>user</param-name><param-value>Rose</param-value></init-param></servlet><servlet-mapping><servlet-name>demo2</servlet-name><url-pattern>/demo2</url-pattern></servlet-mapping>
</web-app>

servlet代码

package com.itheima.servlet;import javax.servlet.ServletContext;
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;
import java.util.Enumeration;/*** 使用配置的方式*/
public class Demo2InitParamServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/html;charset=utf-8");PrintWriter out = response.getWriter();//使用ServletConfig的方法可以获取自己的参数,局部参数String user = getInitParameter("user");  //ServletConfig的方法out.print("局部初始参数:" + user + "<hr/>");//获取全局的配置参数ServletContext application = getServletContext();//调用上下文对象的方法String user1 = application.getInitParameter("user");  //ServletContext的方法out.print("全局的配置参数:" + user1 + "<hr/>");//获取所有的全局参数的名字Enumeration<String> initParameterNames = application.getInitParameterNames();while (initParameterNames.hasMoreElements()) {String name = initParameterNames.nextElement();  //获取一个参数名String value = application.getInitParameter(name);  //获取参数的值out.print("全局参数名:" + name + ",参数值:" + value + "<hr/>");}}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}
}

效果

小结

方法 功能
ServletContext getServletContext() 获取上下文对象的方法
String getInitParameter(String name) 通过参数名获取参数值,全局的初始参数
所有的Servlet都可以使用
Enumeration<String> getInitParameterNames() 获取所有的参数名

10. ServletContext:获取当前工程的资源

目标

得到web目录下的某个图片资源在浏览器上显示出来

idea的bug

web目录下静态资源,如果是复制过去,idea不会自动部署,在out目录下没有,如果是自己新建的不会有问题。

解决方法

自己手动在out目录下再复制一份

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P3omkH3r-1598791362611)(assets/image-20200830144011571.png)]

案例:读取Web目录下的资源文件

执行效果

代码

package com.itheima.servlet;import org.apache.commons.io.IOUtils;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;
import java.io.InputStream;
import java.io.OutputStream;/*** application.getResourceAsStream("/") 表示web目录* 读取web目录或它的目录下资源,返回字节输入流*/
@WebServlet("/demo3")
public class Demo3ResourceServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//回顾:获取src目录下资源,转成输入流//InputStream inputStream = getClass().getResourceAsStream("/xxx");//0.获取上下文对象ServletContext application = getServletContext();//1.获取一个输入流对象,这里的/是web目录InputStream inputStream = application.getResourceAsStream("/WEB-INF/img/404.jpg");//2.获取一个响应的输出流OutputStream outputStream = response.getOutputStream();//3.把输入流写到输出流中去/*int len = 0;byte[] buf = new byte[1024];while ((len = inputStream.read(buf)) != -1) {outputStream.write(buf, 0, len);}*///使用工具IOUtils,用来操作IO流的静态方法IOUtils.copy(inputStream, outputStream);//4.响应会自动关闭流inputStream.close();outputStream.close();}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}
}
使用IOUtils工具类的方法 功能
copy(InputStream in, OutputStream out) 把输入流中的数据写到输出流中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WfomllS0-1598791362612)(assets/image-20200830145341211.png)]

小结

ServletContext的方法 功能
InputStream getResourceAsStream(String path) 读取web或它的子目录下资源,转成字节输入流

11. ServletContext:获取资源在服务器的真实地址

目标

使用上下文对象得到web资源真实的地址

代码

package com.itheima.servlet;import javax.servlet.ServletContext;
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("/demo4")
public class Demo4RealPathServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1.获取上下文对象ServletContext application = getServletContext();//2.调用方法:获取资源在服务器上部署的真实地址。参数:资源的路径名String realPath = application.getRealPath("/WEB-INF/img/404.jpg");//3.输出资源的真实地址response.setContentType("text/html;charset=utf-8");PrintWriter out = response.getWriter();out.print("真实地址是:" + realPath);}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}
}

执行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uknx153Y-1598791362614)(assets/image-20200830150352672.png)]

小结

ServletContext的方法 功能
String getRealPath(String path) 获取指定资源部署到服务器上真实地址

12. 上下文域对象的方法

目标

  1. 上下文域的操作方法
  2. 得到第几个登录用户的分析

上下文域

作用域有三个:

  1. 请求域:一个用户的一次请求起作用,范围最小。
  2. 会话域:一个用户的所有请求。
  3. 上下文域:所有用户的所有请求。都是起作用的,范围是最大。

作用范围越大,占用资源越多,原则:在满足功能的前提下,使用尽可能小的作用域。

上下文域是服务器启动就存在,服务器关闭才销毁。

上下文域的操作方法

所有的作用域都有三个相同的方法

上下文域操作有关的方法 作用
Object getAttribute(“键”) 通过键获取上下文域中值
setAttribute(“键”,Object数据) 向上下文域中添加键和值,如果存在就是覆盖
removeAttribute(“键”) 从上下文域中删除指定的键和值

需求

记录当前是第几个登录成功的用户

案例流程

  1. 计数放在上下文域中
  2. 在Servlet每一次访问的时候,向上下文域中添加1个变量count=0
  3. 每个用户登录
  4. 登录成功:从上下文域中取出count的值,加1,更新上下文域
  5. 登录失败:输出登录失败

13. 案例:得到当前是第几个登录的用户

目标

得到当前是第几个登录的用户的代码实现

步骤

  1. 在init方法中得到上下文对象

  2. 创建count=0,并且将值放入上下文域中.

  3. 登录方法中得到用户名和密码

  4. 判断用户名和密码是否正确

  5. 如果正确,得到上下文对象

  6. 从上下文域中取出count

  7. 加1再更新上下文域

  8. 显示输出人数

  9. 登录失败则跳转到登录页面

代码

HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>用户登录</title>
</head>
<body>
<h2>用户登录</h2>
<form action="login" method="post"><table><tr><td>用户名</td><td><input type="text" name="username"/></td></tr><tr><td>密码</td><td><input type="password" name="password"/></td></tr><tr><td colspan="2"><input type="submit" value="登录"/></td></tr></table>
</form>
</body>
</html>

登录的Servlet

package com.itheima.servlet;import javax.servlet.ServletContext;
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;
import java.util.concurrent.atomic.AtomicInteger;/*** 记录当前是第几个登录的用户*/
@WebServlet("/login")
public class Demo5LoginServlet extends HttpServlet {/*第一个用户登录的时候执行1次注:因为每个Servlet只有一个对象,每个用户其实是一个线程,所以Servlet中成员变量同时给多个用户使用的会有线程安全的问题,建议不要对成员变量进行修改。*/@Overridepublic void init() throws ServletException {//获取上下文对象,向上下文域中添加了一个计数为0getServletContext().setAttribute("count", 0);}protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/html;charset=utf-8");PrintWriter out = response.getWriter();//实现用户的登录String username = request.getParameter("username");  //获取用户名String password = request.getParameter("password");  //获取用户名//直接判断,登录成功if ("admin".equals(username) && "123".equals(password)) {//从上下文域中取出原有的值ServletContext application = getServletContext();//获取一个整数类型的值int count = (int) application.getAttribute("count");//计数加1,再放回到上下文域中application.setAttribute("count", ++count);//输出第几个登录的用户out.print("登录成功,您是第" + count + "个登录的用户");} else {out.print("登录失败,<a href='login.html'>请重试</a>");}}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}
}

小结

上下文域的范围是:所有用户的所有请求。服务器开启就会创建上下文域,服务器关闭才会销毁

ServletContext的方法 作用
Object getAttribute(“键”) 获取上下文域中值
void setAttribute(“键”,Object 数据) 设置上下文域中键和值
void removeAttribute(“键”) 删除上下文域中的键和值

14. URL编码和解码

目标

  1. 为什么需要URL编码和解码
  2. 如何实现URL编码和解码

为什么需要URL编码和解码

表单中提交汉字,使用GET提交,查看请求行的数据

URL的编码规则

字母数字字符 "a" 到 "z"、"A" 到 "Z" 和 "0" 到 "9" 保持不变。
特殊字符 "."、"-"、"*" 和 "_" 保持不变。
空格字符 " " 转换为一个加号 "+"。
所有其他字符都是不安全的,因此首先使用一些编码机制将它们转换为一个或多个字节。然后每个字节用一个包含 3 个字符的字符串 "%xy" 表示,其中 xy 为该字节的两位十六进制表示形式。推荐的编码机制是 UTF-8。如:张三 编码后:%E5%BC%A0%E4%B8%89

有关的方法

URL编码和解码有关的方法 功能
URLEncoder.encode(“字符串”,“utf-8”) URL编码,参数1:要编码的字符串,
参数2:编码的类型,统一使用utf-8
URLDecoder.decode(“字符串”,“utf-8”) URL解码:参数2:要解码的字符串,
参数2:编码的类型,统一使用utf-8

案例演示代码

package com.itheima.test;import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;public class TestURL {public static void main(String[] args) throws UnsupportedEncodingException {String hz = "张三";//URL编码,参数1:要编码的字符串,参数2:编码的类型,统一使用utf-8String encode = URLEncoder.encode(hz, "utf-8");System.out.println("编码前:" + hz);System.out.println("编码后:" + encode);//URL解码:参数2:要解码的字符串,参数2:编码的类型,统一使用utf-8String decode = URLDecoder.decode(encode, "utf-8");System.out.println("解码后:" + decode);}
}

小结

URL编码和解码有关的方法 功能
URLEncoder.encode(“字符串”,“utf-8”) URL的编码
URLDecoder.decode(“字符串”,“utf-8”) URL的解码

15. 案例:使用链接下载文件不足

目标

链接下载文件存在的问题

页面效果

下载页面

<!DOCTYPE html>
<html>
<head><title>资源下载列表</title><meta charset="utf-8">
</head><body>
<h2>文件下载页面列表</h2>
<h3>超链接的下载</h3>
<!--直接链接下载的资源,download是一个目录 -->
<a href="download/file.txt">文本文件</a><br/>
<a href="download/file.jpg">图片文件</a><br/>
<a href="download/file.zip">压缩文件</a><br/>
<hr/>
<h3>手动编码的下载方式</h3>
<!-- 注:down这里是下载的Servlet的访问地址 -->
<a href="down?filename=file.txt">文本文件</a><br/>
<a href="down?filename=file.jpg">图片文件</a><br/>
<a href="down?filename=file.zip">压缩文件</a><br/>
<!--以后不建议使用汉字的文件名-->
<a href="down?filename=美女.jpg">美女</a><br/>
</body>
</html>

小结:使用超链接下载的不足

  1. 有些资源是在浏览器上直接打开,不是下载。
  2. 会暴露资源的真实地址,容易被人盗链
  3. 不利于业务逻辑的控制,比较如:要登录或是会员才可以下载

16. 案例:使用Servlet下载文件【重点】

目标

  1. 实现使用Servlet的方式下载文件
  2. 文件名有汉字的处理

下载设置的响应头

设置响应头 参数说明
Content-Disposition: attachment; filename=文件名 Content-Disposition:
1. inline表示在浏览器中打开,默认值
2. attachment: 以附件的方式下载资源
filename: 下载的文件名

步骤

  1. 从链接上得到文件名
  2. 设置content-disposition头
  3. 得到文件的输入流
  4. 得到response的输出流
  5. 写出到浏览器端

汉字乱码原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gSak2te3-1598791362616)(assets/1552656145066.png)]

注:IE、Chrome下载中文采用的是URL编码,注:FireFox不能采用这种方式。

下载的Servlet代码

package com.itheima.servlet;import org.apache.commons.io.IOUtils;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.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;@WebServlet("/down")
public class DownloadServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1.获取下载的文件名String filename = request.getParameter("filename");System.out.println("下载的文件名是:" + filename);//要点:如果要下载,必须设置响应头//因为浏览器会自动对汉字进行解码的操作,所以我们在服务器端对汉字进行编码response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename,"utf-8"));//2.使用上下文对象获取文件的输入流InputStream inputStream = getServletContext().getResourceAsStream("/download/" + filename);//3.获取响应的输出流OutputStream outputStream = response.getOutputStream();//4.将输入流复制到输出流中,注:要导入io工具的jar包到lib目录下IOUtils.copy(inputStream, outputStream);//5.关闭流inputStream.close();outputStream.close();}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}
}

小结

  1. 下载需要设置如个响应头?

    content-disposition:attachment;filename=文件名
    
  2. 如果下载文件名中有汉字使用哪个方法编码?

    URLEncoder.encode("字符串","utf-8")
    

17. 验证码的使用

目标

  1. 将上面创建的验证码用在HTML页面中
  2. 点击验证码图片刷新验证码的功能

验证码的代码

package com.itheima.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("/code")
public class PicCodeServlet extends HttpServlet {//随机类private Random random = new Random();/*** 随机获取一种颜色*/private Color getRandomColor() {//随机得到r,g,b的取值,范围是0~255int r = random.nextInt(256);int g = random.nextInt(256);int b = random.nextInt(256);return new Color(r, g, b);  //red红 green绿 blue蓝}protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//设置MIME类型response.setContentType("image/jpeg");//定义宽和高的值int width = 90;int height = 30;//1. 创建一张图片,参数:宽,高,图片模式BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);//获取画笔对象Graphics graphics = image.getGraphics();//整个图片填充白色graphics.setColor(Color.WHITE);graphics.fillRect(0,0,width,height);//2. 随机绘制4个验证码char[] arr = { 'A', 'B', 'C', 'D', 'N', 'E', 'W', 'b', 'o', 'y', '1', '2', '3', '4','5','6','7','8' };//设置字体,字体对象有三个参数:字体名字,字体样式(加粗,斜体), 大小graphics.setFont(new Font(Font.DIALOG_INPUT, Font.BOLD + Font.ITALIC, 19));for (int i = 0; i < 4; i++) {//随机获取1个索引号int index = random.nextInt(arr.length);//随机获取字符数组的一个字符char c = arr[index];//每个字符的颜色不同graphics.setColor(getRandomColor());//写字符,参数:文字内容,x,y坐标  (把字符转成字符串)graphics.drawString(String.valueOf(c),10+(i*20), 20);}//3. 绘制8条干扰线for (int i = 0; i < 8; i++) {//指定颜色graphics.setColor(getRandomColor());int x1 = random.nextInt(width);int y1 = random.nextInt(height);int x2 = random.nextInt(width);int y2 = random.nextInt(height);//画线,起点和终点graphics.drawLine(x1,y1,x2,y2);}//4. 把图片输出到浏览器,参数:输出的图片对象,图片格式,响应输出流ImageIO.write(image,"jpg", response.getOutputStream());}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}
}

代码

<!--验证码,src要指定为servlet的访问地址-->
<img src="code" id="picCode" style="width: 90px; height: 30px; cursor: pointer;" title="看不清,点击刷新">
<script type="text/javascript">//点击图片的事件document.getElementById("picCode").onclick = function () {//刷新页面  location.reload();//再次访问服务器,将src的值重新赋值,默认会使用缓存//每次传递不同的参数就可以修改URL地址了,参数并没有什么用this.src = "code?t=" + new Date().getTime();}
</script>

小结

实现点击验证码刷新的功能:每次传递不同的参数

学习总结

  1. 能够使用浏览器开发工具查看响应

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YGSZOHbl-1598791362618)(assets/1552645116046.png)]

  2. 能够理解响应行(状态行)的内容

    HTTP/1.1 200 OK

    1. 协议和版本
    2. 状态码
    3. 状态描述
  3. 能够理解常见的状态码

    状态码 含义
    200 正常的响应
    302 浏览器端进行了跳转
    304 使用了缓存
    404 找不到资源
    405 没有重写doGet或doPost方法
    500 服务器代码有错误
  4. 能够使用Response对象操作HTTP响应内容

    响应头的方法 描述
    setStatus(int status) 设置状态码
    void setHeader(String name, String value) 设置某个响应头
    OutputStream getOutputStream() 获取字节输出流
    PrintWriter getWriter() 获取字符输出流
  5. 能够处理响应乱码

    处理响应乱码的方法 描述
    void setContentType(String type) 1. 告诉浏览器,服务器端使用的编码
    2. 设置响应的编码,相当于下面这个方法
    3. 设置响应的内容类型
    response.setCharacterEncoding(“字符集”) 设置响应的编码
  6. 能够完成文件下载案例

    1. 设置下载的响应头
    2. 通过上下文对象getResourceAsStream()得到输入流
    3. 获取响应的输出流
    4. IOUtils.copy(输入流,输出流)
    设置响应头 参数说明
    Content-Disposition: attachment; filename=文件名 设置浏览器以附件的方式下载文件
    而不是在浏览器中直接打开
  7. 能够使用ServletContext域对象

    ServletConfig接口中方法 描述
    ServletContext getServletContext() 获取上下文对象
    ServletContext的方法 作用
    InputStream getResourceAsStream(String path) 读取web目录下的资源文件,转成输入流
    String getRealPath(String path) 读取web目录下资源的部署到服务器上的真实地址
    setAttribute() getAttribute() removeAttribute()
    

WEB阶段3:Response响应组成常见状态码ServletContexturl编码文件下载案例相关推荐

  1. nginx服务之图解常见状态码

    常见状态码1: 301 永久移动.请求资源以被永久移动位置 302 请求的资源现在临时从不同的URL响应请求 305 使用代理.被请求的资源必须通过指定的代理才能被访问 307 临时跳转.被请求的资源 ...

  2. web应用F12查看报错(前后端bug判断、2XX/3XX/4XX/5XX常见状态码解析)

    chrom浏览器为例 (1)打开开发者工具,在浏览器菜单栏选择工具-开发者工具,快捷键是F12 (2)打开之后切换到Network页签,操作就可以看到请求响应 (3)再选择响应的链接,切换到Previ ...

  3. HTTP与HTTPS及其工作原理及三次握手、四次挥手、常见状态码

    一.HTTP与HTTPS HTTP:超文本传输协议,是一个客户端和服务器端请求和应答的标准,用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少,常基于TCP/I ...

  4. 3-3:HTTP协议之request和respond及常见请求方法和常见状态码

    文章目录 一:request (1)请求报文基本构成 (2)请求方法 二:respond (1)响应报文基本构成 (2)HTTP常见状态码 三:HTTP常见Header(字段) 注意下面需要分析请求报 ...

  5. HTTP协议、HTTP请求方法、常见状态码、HTTP消息

    HTTP协议 客户端请求,服务端响应.浏览器与服务器不建立持久连接,响应后连接失效. HTTP请求方法 一.GET GET方法用于获取请求页面的指定信息. 二.HEAD 除了服务器不能在响应里返回消息 ...

  6. SpringMVC响应的HTTP状态码

    SpringMVC响应的HTTP状态码 SpringMVC 定义的状态码 常用到的状态码 2XX 200 OK 204 206 3XX 301 302 4XX 400 401 403 5XX 500 ...

  7. HTTP常见状态码(14种)

    HTTP状态码表示客户端HTTP请求的返回结果.标记服务器端的处理是否正常或者是出现的错误,能够根据返回的状态码判断请求是否得到正确的处理很重要. 状态码由3位数字和原因短语组成,例如下图所示: 数字 ...

  8. 关于Http的常见状态码码值

    关于Http的常见状态码码值 什么是状态码? 状态码由3位数字和原因短语组成,数字中的第一位指定了响应类别,后两位无分类. 如:200,404,405,500等状态码 了解状态码: 状态码分类表: 码 ...

  9. python flask 设置 header 响应体、响应头、状态码

    需求场景 在api设计中,基于restful的设计原则,一个http的响应应该包含执行的响应信息以及状态码. 例如:一个错误信息的响应信息应该包含内容以及返回对应的设计错误码. 在flask中如何制定 ...

最新文章

  1. 【C++】C/C++ 中 static 的用法全局变量与局部变量
  2. 带你感受小而美的看板协作工具Leangoo的魅力
  3. Kali Linux NetHunter教程Kali NetHunter支持的设备和ROMs
  4. 工具04_SQL Trace/DBMS_SYSTEM
  5. 9种蔬菜吃不对胜似砒霜
  6. 功能自动化工具watiJ(转载)
  7. airodump-ng wlan0mon扫描不到网络_MySQL ProxySql 由于漏洞扫描导致的 PROXYSQL CPU 超高...
  8. C# 自定义常用代码段
  9. 【Oracle Database】Oracle GoldenGate (single-single)
  10. win10如何检测计算机性能,win10系统怎么查看自己电脑性能
  11. 使用工具(JD-GUI和APKIDE)反编译JAR的方法
  12. mysql 储存特殊符号表情报错
  13. C#渐变色方法 实例
  14. linux查看nas剩余大小,老徐玩NAS 篇二:我的群晖储存空间哪儿去了——100%破案的教程...
  15. db mysql error_list
  16. 防火墙系列(二)-----防火墙的主要技术之包过滤技术,状态检测技术
  17. 这么多编程学习网站,总有一个适合你吧
  18. c语言编程给系统加密保护,C语言:模拟输入密码系统,三次以内正常通过,三次退出程序...
  19. 毕业设计:微信小程序健康管理系统的开发与实现
  20. 如何使用爬虫一键批量采集新浪微博内容

热门文章

  1. 获取某个顶点的所有邻边
  2. 中缀表达式计算(java)
  3. 死锁与活锁、死锁与饥饿区别
  4. Java进阶1-JVM虚拟机
  5. DBCO-Benzaldehyde 二苯基环辛炔-苯甲醛
  6. FluentScheduler介绍
  7. 全球及中国轮胎行业消费格局及投资盈利预测报告2021-2027年
  8. 吉大17秋计算机应用二,吉大17秋学期《计算机应用基础》在线作业二 (100分)...
  9. 软件生日提醒需求调研
  10. CAN光端机解决泰和安TX3016C消防主机长距离联网问题 实现CAN与光纤之间的双向数据智能转换