typora-copy-images-to: img

request&response

学习目标

  • 能够使用Request对象获取HTTP协议请求内容
  • 能够处理HTTP请求参数的乱码问题
  • 能够使用Request域对象
  • 能够使用Request对象做请求转发
  • 能够使用Response对象操作HTTP响应内容
  • 能够处理响应乱码
  • 能够完成文件下载案例
  • 能够完成注册案例
  • 能够完成登录案例

第一章-request

知识点-request概述

1.目标

  • 知道什么是request以及作用

2.讲解

2.1什么是request

在Servlet API中,定义了一个HttpServletRequest接口,它继承自ServletRequest接口,专门用来封装HTTP请求消息。由于HTTP请求消息分为请求行、请求头和请求体三部分,因此,在HttpServletRequest接口中定义了获取请求行、请求头和请求消息体的相关方法.

  1. ![](https://gitee.com/tgl_bug/typora-table/raw/master/img/20210121132240.png#alt=img)
  2. Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。

2.2request作用

  • 操作请求三部分(行,头,体)
  • 请求转发
  • 作为域对象存数据

3.小结

  1. request代表请求对象. 原型是HttpServletRequest, 服务器创建好的, 以形参的方式存在doGet()/doPost()方法里面
  2. Request作用

    • 操作请求的三部分(行,头,体)
    • 转发
    • 作为域对象存取数据

知识点-操作请求行和请求头

1.目标

  • 掌握使用request对象获取客户机信息(操作请求行)和获得请求头信息(操作请求头)

2.路径

  • 获取客户机信息(操作请求行)
  • 获得请求头信息(操作请求头)

3.讲解

3.1获取客户机信息(操作请求行)

  1. 请求方式 请求路径(URI) 协议版本
  2. GET /day17Request/WEB01/register.htm?username=zs&password=123456 HTTP/1.1
  • getMethod();获取请求方式
  • getRemoteAddr() ;获取客户机的IP地址(知道是谁请求的)
  • getContextPath(); 获得当前应用工程名(部署的路径);
  • getRequestURI();获得请求地址,不带主机名
  • getRequestURL();获得请求地址,带主机名
  • getServerPort();获得服务端的端口
  • getQueryString();获的请求参数(get请求的,URL的?后面的. eg:username=zs&password=123456)
  1. /**
  2. * 包名:${PACKAGE_NAME}
  3. *
  4. * @author Leevi
  5. * 日期2020-07-12 08:58
  6. * 获取请求行的信息
  7. */
  8. @WebServlet("/demo01")
  9. public class ServletDemo01 extends HttpServlet {
  10. @Override
  11. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  12. doGet(request, response);
  13. }
  14. @Override
  15. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  16. //使用request对象获取请求行的信息:
  17. //1. 获取请求的方式(可能会用)
  18. String method = request.getMethod();
  19. //System.out.println("请求方式为:" + method);
  20. //2. 获取客户端的ip地址
  21. String remoteAddr = request.getRemoteAddr();
  22. //System.out.println("客户端的ip地址是:" + remoteAddr);
  23. //3. 获取项目部署的路径(以后可能用到)
  24. String contextPath = request.getContextPath();
  25. //System.out.println("项目部署路径是:" + contextPath);
  26. //4. 获取请求的url: 统一资源定位符 http://localhost:8080/requestDemo/demo01
  27. String url = request.getRequestURL().toString();
  28. //System.out.println("此次请求的url是:" + url);
  29. //5. 获取请求的uri: 统一资源标识符,在url的基础上省略了服务器路径"http://loaclhost:8080"
  30. String uri = request.getRequestURI();
  31. System.out.println(uri);
  32. }
  33. }

3.2.获得请求头信息(操作请求头)

请求头: 浏览器告诉服务器自己的属性,配置的, 以key value存在, 可能一个key对应多个value

request&response - 图1

  1. getHeader(String name);
  • User-Agent: 浏览器信息
  • Referer:来自哪个网站(防盗链)
  1. /**
  2. * 包名:${PACKAGE_NAME}
  3. *
  4. * @author Leevi
  5. * 日期2020-07-12 09:14
  6. * 使用request获取请求头的信息
  7. */
  8. @WebServlet("/demo02")
  9. public class ServletDemo02 extends HttpServlet {
  10. @Override
  11. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  12. doGet(request, response);
  13. }
  14. @Override
  15. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  16. //根据请求头的name获取value
  17. //目标:获取name为user-agent的请求头的信息
  18. String header = request.getHeader("user-agent");
  19. System.out.println("获取的请求头agent为:" + header);
  20. }
  21. }

4.小结

  1. 操作请求行(获得客户机信息)

    • getMethod(); 获得请求方式
    • getRemoteAddr(); 获得客户机的IP地址
    • getContextPath(); 获得项目的部署的路径
    • getRequestURI(); 获得URI(不带http,主机,端口)
    • getRequestURL(); 获得URL(带http,主机,端口)
  2. 操作请求头

    • getHeader(String name);

      • User-Agent: 浏览器信息
      • Referer:来自哪个网站(防盗链)

知识点-操作请求体(获得请求参数)【重点】

1.目标

  • 掌握获得请求参数, 以及乱码的解决

2.路径

  • 获得请求参数
  • 请求参数乱码的处理
  • 使用BeanUtils封装请求参数到JavaBean

3.讲解

3.1获得请求参数

法名 描述
String getParameter(String name) 获得指定参数名对应的值。如果没有则返回null,如果有多个获得第一个。 例如:username=jack
String[] getParameterValues(String name) 获得指定参数名对应的所有的值。此方法专业为复选框提供的。 例如:hobby=抽烟&hobby=喝酒&hobby=敲代码
Map getParameterMap() 获得所有的请求参数。key为参数名,value为key对应的所有的值。

3.2 请求参数乱码处理

  1. 我们在输入一些中文数据提交给服务器的时候,服务器解析显示出来的一堆无意义的字符,就是乱码。<br />

那么这个乱码是如何出现的呢?如下图所示:

request&response - 图2

  1. get方式, 我们现在使用的tomcat>=8.0了, 乱码tomcat已经处理好了
  2. post方式, 就需要自己处理
  1. void setCharacterEncoding(String env); //设置请求体的编码
  1. /**
  2. * 包名:${PACKAGE_NAME}
  3. *
  4. * @author Leevi
  5. * 日期2020-07-12 09:33
  6. * 使用request获取请求参数
  7. *
  8. * 请求参数的乱码问题:
  9. * 在tomcat8之前,无论是get请求还是post请求,请求参数都会发生乱码
  10. * 在tomcat8之后(包含8),只有post请求才会发生中文乱码
  11. *
  12. * 怎么解决乱码: 只要在获取请求参数之前,调用request.setCharacterEncoding(UTF-8);
  13. */
  14. @WebServlet("/demo03")
  15. public class ServletDemo03 extends HttpServlet {
  16. @Override
  17. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  18. doGet(request, response);
  19. }
  20. @Override
  21. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  22. request.setCharacterEncoding("UTF-8");
  23. //1. 根据参数名,获取一个参数值
  24. String username = request.getParameter("username");
  25. //System.out.println("获取的请求参数username=" + username);
  26. //2. 根据参数名,获取多个参数值:比如说注册时候的兴趣爱好的复选框
  27. String[] hobbies = request.getParameterValues("hobby");
  28. /*for (String hobby : hobbies) {
  29. System.out.println(hobby);
  30. }*/
  31. //3. 获取所有的请求参数
  32. //getParameterMap()获取所有请求参数,请求参数的参数名就是map的key,请求参数的参数值就是map的value
  33. Map<String, String[]> parameterMap = request.getParameterMap();
  34. //遍历出每一个请求参数
  35. Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();
  36. for (Map.Entry<String, String[]> entry : entries) {
  37. String parameterName = entry.getKey();
  38. String[] values = entry.getValue();
  39. for (String value : values) {
  40. System.out.println("参数名:" + parameterName + ",参数值:" + value);
  41. }
  42. }
  43. }
  44. }

3.3使用BeanUtils封装

  1. 现在我们已经可以使用request对象来获取请求参数,但是,如果参数过多,我们就需要将数据封装到对象。
  2. 以前封装数据的时候,实体类有多少个字段,我们就需要手动编码调用多少次setXXX方法,因此,我们需要BeanUtils来解决这个问题。
  3. BeanUtilsApache Commons组件的成员之一,主要用于简化JavaBean封装数据的操作。

使用步骤:

  1. 导入jar
  2. 使用BeanUtils.populate(user,map)
  • 表单
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>注册页面</title>
  6. </head>
  7. <body>
  8. <form action="/requestDemo/demo04" method="post">
  9. 用户名<input type="text" name="username"><br>
  10. 密码<input type="text" name="password"><br>
  11. 性别<input type="radio" name="gender" value="male">
  12. <input type="radio" name="gender" value="female">
  13. <br>
  14. 兴趣爱好
  15. <input type="checkbox" name="hobby" value="basketball">篮球
  16. <input type="checkbox" name="hobby" value="football">足球
  17. <input type="checkbox" name="hobby" value="ppball">乒乓球
  18. <input type="checkbox" name="hobby" value="yumaoball">羽毛球
  19. <br>
  20. <input type="submit" value="注册">
  21. </form>
  22. </body>
  23. </html>
  • User
  1. /**
  2. * 包名:com.itheima.pojo
  3. *
  4. * @author Leevi
  5. * 日期2020-07-12 10:19
  6. */
  7. public class User implements Serializable {
  8. private String username;
  9. private String password;
  10. private String gender;
  11. private String[] hobby;
  12. public User() {
  13. }
  14. public User(String username, String password, String gender, String[] hobby) {
  15. this.username = username;
  16. this.password = password;
  17. this.gender = gender;
  18. this.hobby = hobby;
  19. }
  20. @Override
  21. public String toString() {
  22. return "User{" +
  23. "username='" + username + '\'' +
  24. ", password='" + password + '\'' +
  25. ", gender='" + gender + '\'' +
  26. ", hobby=" + Arrays.toString(hobby) +
  27. '}';
  28. }
  29. public String getUsername() {
  30. return username;
  31. }
  32. public void setUsername(String username) {
  33. this.username = username;
  34. }
  35. public String getPassword() {
  36. return password;
  37. }
  38. public void setPassword(String password) {
  39. this.password = password;
  40. }
  41. public String getGender() {
  42. return gender;
  43. }
  44. public void setGender(String gender) {
  45. this.gender = gender;
  46. }
  47. public String[] getHobby() {
  48. return hobby;
  49. }
  50. public void setHobby(String[] hobby) {
  51. this.hobby = hobby;
  52. }
  53. }
  • ServletDemo04
  1. /**
  2. * 包名:${PACKAGE_NAME}
  3. *
  4. * @author Leevi
  5. * 日期2020-07-12 10:18
  6. * 我们使用request.getParameterMap()方法获取所有请求参数,是封装在map对象中
  7. *
  8. * 我的需求是将获取到的所有请求参数封装到POJO对象中
  9. * 1. 创建一个POJO类,类中的属性名要和请求参数名对应
  10. * 2. 使用BeanUtils框架,将map中的数据封装到POJO对象中
  11. */
  12. @WebServlet("/demo04")
  13. public class ServletDemo04 extends HttpServlet {
  14. @Override
  15. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  16. doGet(request, response);
  17. }
  18. @Override
  19. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  20. request.setCharacterEncoding("UTF-8");
  21. //1. 获取所有的请求参数
  22. Map<String, String[]> parameterMap = request.getParameterMap();
  23. //2. 将请求参数封装到User对象中
  24. User user = new User();
  25. //3. 使用BeanUtils框架自动将map中的数据封装到对象中
  26. try {
  27. BeanUtils.populate(user,parameterMap);
  28. System.out.println(user);
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. }

4.小结

  1. 获取请求参数的方法

    1. getParameter(name),根据参数名获取一个参数值
    2. getParameterValues(name),根据参数名获取多个参数值
    3. getParameterMap(),获取所有的请求参数封装到map中
  2. 使用BeanUtils封装

    • 如果请求参数有多个需要封装到JavaBean里面, 建议先获得Map, 再使用BeanUtils封装到JavaBean对象

      注意: JavaBean属性需要和Map的key一致 说白了也就是JavaBean属性需要和表单的name一致

  3. 解决请求参数的中文乱码问题

    1. Tomcat8之后,使用get方式提交的请求参数不会发生中文乱码
    2. 解决post请求中的中文参数乱码问题:在获取请求参数之前添加一句代码:request.setCharacterEncoding(“UTF-8”)

知识点-请求转发【重点】

1.目标

  • 掌握请求转发

2.讲解

  1. request.getRequestDispatcher(url).forward(request, response);//转发

3.小结

  • 请求转发的作用:跳转页面,比如说添加完数据之后跳转到数据的展示页面,删除完数据之后跳转到展示页面

  • 请求转发的代码

  1. request.getRequestDispatcher("转发的路径").forward(request,response);
  • 请求转发的特征

    • 跳转操作是由服务器执行的,所以客户端地址栏不会发生变化
    • 跳转操作不会发起新的请求
    • 可以跳转到WEB-INF中的资源,但是不能跳转到其它项目的资源

知识点-作为域对象存取值

1.目标

  • 掌握requet作为域对象存取值

2.讲解

  1. ServletContext: 范围 整个应用(无论多少次请求,只要是这个应用里面的都是可以共享的)
  2. request范围: 一次请求有效
  3. 域对象是一个容器,这种容器主要用于ServletServlet/JSP之间的数据传输使用的。
  • Object getAttribute(String name) ;
  • void setAttribute(String name,Object object) ;
  • void removeAttribute(String name) ;

3.小结

  1. 作为域对象存取数据

    • Object getAttribute(String name) ; 取
    • void setAttribute(String name,Object object) ; 存
    • void removeAttribute(String name) ; 移除
  2. 范围: 一次请求有效(转发可以使用)
    request&response - 图3
  1. /**
  2. * 包名:${PACKAGE_NAME}
  3. *
  4. * @author Leevi
  5. * 日期2020-07-12 10:50
  6. * request作为域对象在不同的Servlet之间进行数据的共享,它只能在同一次请求中进行数据共享
  7. *
  8. * request域对象只有和请求转发一起使用才有意义
  9. */
  10. @WebServlet("/demo06")
  11. public class ServletDemo06 extends HttpServlet {
  12. @Override
  13. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  14. doGet(request, response);
  15. }
  16. @Override
  17. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  18. // 目标: 在ServletDemo07中获取ServletDemo06中的变量username的值
  19. // 要求只能是由ServletDemo06跳转到ServletDemo07的时候才能获取
  20. String username = "周杰棍";
  21. //将username存储到request域对象中
  22. request.setAttribute("name",username);
  23. //请求转发跳转到ServletDemo07
  24. request.getRequestDispatcher("/demo07").forward(request, response);
  25. }
  26. }

ServletDemo07的代码

  1. /**
  2. * 包名:${PACKAGE_NAME}
  3. *
  4. * @author Leevi
  5. * 日期2020-07-12 10:52
  6. */
  7. @WebServlet("/demo07")
  8. public class ServletDemo07 extends HttpServlet {
  9. @Override
  10. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  11. doGet(request, response);
  12. }
  13. @Override
  14. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  15. //使用request域对象中取出name的值
  16. System.out.println("在ServletDemo07中获取username的值:"+request.getAttribute("name"));
  17. }
  18. }

Request部分的小结

  1. 获取请求行的信息

    1. 获取请求方式:getMethod() 掌握
    2. 获取请求的客户端的ip地址:getRemoteAddr() 掌握
    3. 获取项目部署的路径:getContextPath() 掌握
    4. 获取uri:统一资源标识符 掌握
    5. 获取url:统一资源定位符
    6. 获取服务器的端口号(了解)
    7. 获取请求参数的字符串:getQueryString() (了解)
  2. 获取请求头的信息:getHeader(name) 掌握
  3. 获取请求参数(全部要掌握,最重要)

    1. getParameter(name)
    2. getParameterValues(name)
    3. getParameterMap()
  4. 使用BeanUtils将map中的数据存储到JavaBean对象中(掌握)

    1. map的key要和JavaBean的属性名保持一致,如果不一致那么该字段的值就无法存储
    2. BeanUtils中默认内置一些基本类型的转换器(如果map中的数据是string类型,JavaBean的属性还是int类型那么会自动转换)
  5. 使用request做请求转发: request.getRequestDispatcher(“跳转路径”).forward(request,response); 掌握
  6. request对象作为域对象向存取数据,它的作用范围是一次请求中,和请求转发一起使用 掌握

第二章-Response

知识点-Response概述

1. 目标

  • 掌握什么是Response,以及Response的作用

2. 讲解

2.1 HttpServletResponse概述

  1. Servlet API中,定义了一个HttpServletResponse接口(doGet,doPost方法的参数),它继承自ServletResponse接口,专门用来封装HTTP响应消息。由于HTTP响应消息分为响应行、响应头、响应体三部分,因此,在HttpServletResponse接口中定义了向客户端发送响应状态码、响应头、响应体的方法

2.2作用

  • 操作响应的三部分(响应行,响应头,响应体)

3.小结

  1. Response代表响应对象. 原型是HttpServletResponse, 服务器创建的, 以形参的形式存在doGet()/doPost()方法
  2. Response的作用

    • 操作响应的三部分(行, 头, 体)

知识点-操作响应行

1.目标

  • 掌握操作响应行的方法

2.讲解

  1. HTTP/1.1 200
  1. ![](https://gitee.com/tgl_bug/typora-table/raw/master/img/20210121132244.png#alt=img%2F)
  2. 常用的状态码:
  3. 200:成功
  4. 302:重定向
  5. 304:访问缓存
  6. 404:客户端错误
  7. 500:服务器错误

3.小结

  1. 设置的API: response.setStatus(int code);

  2. 一般不需要设置, 可能302 重定向需要设置

  3. 常见的响应状态码

    • 200 成功
    • 302 重定向
    • 304 读缓存
    • 404 客户端错误
    • 500 服务器错误

知识点-操作响应头

1.目标

  • 掌握操作响应头的方法, 能够进行定时刷新和重定向

2.路径

  • 操作响应头的API介绍
  • 定时刷新
  • 重定向

3.讲解

3.1操作响应头的API

响应头: 是服务器指示浏览器去做什么

  1. 一个key对应一个value
  2. ![](https://gitee.com/tgl_bug/typora-table/raw/master/img/20210121132245.png#alt=img%2F)
  3. 一个key对应多个value
  4. ![](https://gitee.com/tgl_bug/typora-table/raw/master/img/20210121132246.png#alt=img%2F)

关注的方法: setHeader(String name,String value);

  1. 常用的响应头
  2. Refresh:定时跳转 (eg:服务器告诉浏览器5s之后跳转到百度)
  3. Location:重定向地址(eg: 服务器告诉浏览器跳转到xxx)
  4. Content-Disposition: 告诉浏览器下载
  5. Content-Type:设置响应内容的MIME类型(服务器告诉浏览器内容的类型)

3.2 定时刷新

  1. response.setHeader("refresh","秒数;url=跳转的路径"); //几秒之后跳转到指定的路径上

3.3 重定向【重点】

request&response - 图4

  1. 重定向两次请求
  2. 重定向的地址栏路径改变
  3. 重定向的路径写绝对路径(带域名/ip地址的, 如果是同一个项目里面的,域名/ip地址可以省略)
  4. 重定向的路径可以是项目内部的,也可以是项目以外的(eg:百度)
  5. 重定向不能重定向到WEB-INF下的资源
  6. 把数据存到request里面, 重定向不可用
  1. //方式一: 重定向
  2. //1.设置状态码
  3. //response.setStatus(302);
  4. //2.设置重定向的路径(绝对路径,带域名/ip地址的,如果是同一个项目里面的,域名/ip地址可以省略)
  5. //response.setHeader("Location","http://localhost:8080/day28/demo08");
  6. //response.setHeader("Location","/day28/demo08");
  7. //response.setHeader("Location","http://www.baidu.com");
  8. //方式二: 直接调用sendRedirect方法, 内部封装了上面两行
  9. response.sendRedirect("http://localhost:8080/day28/demo08");
  • 重定向
  1. response.sendRedirect("重定向的路径");

重定向和请求转发的对比

request&response - 图5

重定向的特点:

  1. 重定向的跳转是由浏览器发起的,在这个过程中浏览器会发起两次请求
  2. 重定向跳转可以跳转到任意服务器的资源,但是无法跳转到WEB-INF中的资源
  3. 重定向跳转不能和request域对象一起使用
  4. 重定向跳转浏览器的地址栏中的地址会变成跳转到的路径

请求转发的特点:

  1. 1. 请求转发的跳转是由服务器发起的,在这个过程中浏览器只会发起一次请求
  2. 2. 请求转发只能跳转到本项目的资源,但是可以跳转到WEB-INF中的资源
  3. 3. 请求转发可以和request域对象一起使用

4.小结

4.1操作响应头

知识点-操作响应体

1.目标

  • 掌握操作响应体以及响应乱码的解决

2.步骤

  • 操作响应体的API介绍
  • 响应乱码的解决

3.讲解

操作响应体的API

  1. ![](https://gitee.com/tgl_bug/typora-table/raw/master/img/20210121132249.png#alt=img%2F)
  2. 页面输出只能使用其中的一个流实现,两个流是互斥的.

3.1 使用字符输出流输出字符串

  • 解决字符流输出中文乱码问题
  1. response.setContentType("text/html;charset=utf-8");
  • 使用字符输出流
  1. import javax.servlet.ServletException;
  2. import javax.servlet.annotation.WebServlet;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. import java.io.PrintWriter;
  8. /**
  9. * 包名:${PACKAGE_NAME}
  10. *
  11. * @author Leevi
  12. * 日期2020-07-12 12:12
  13. * response设置响应体的信息
  14. * 直接给浏览器要展示的数据
  15. *
  16. * 解决响应体的乱码问题
  17. * response.setContentType("text/html;charset=UTF-8");
  18. * 这句代码底层做了什么?
  19. * 1. 设置服务器响应的字符集为UTF-8
  20. * 2. 设置Content-Type响应头的信息为 "text/html;charset=UTF-8"
  21. * 让浏览器知道了服务器的响应字符集UTF-8,那么浏览器也会使用UTF-8解码
  22. */
  23. @WebServlet("/demo04")
  24. public class ServletDemo04 extends HttpServlet {
  25. @Override
  26. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  27. doGet(request, response);
  28. }
  29. @Override
  30. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  31. response.setContentType("text/html;charset=UTF-8");
  32. //要向浏览器输出响应体的信息,需要通过流来进行操作
  33. //第一种:字符串,输出文本内容
  34. PrintWriter writer = response.getWriter();
  35. //使用字符流往浏览器输出文本
  36. //1. writer()方法,只能输出字符串,如果输出int、float等等类型的话,则会有问题
  37. writer.write("你好世界");
  38. //2. print()方法,可以输出数字、字符串
  39. //writer.print(8);
  40. }
  41. }

3.2 使用字节输出流向浏览器输出文件

目标是:向浏览器输出一张图片

注意:需要引入commons-io的jar包

  1. /**
  2. * 包名:${PACKAGE_NAME}
  3. *
  4. * @author Leevi
  5. * 日期2020-07-12 14:29
  6. * 使用response的字节流向浏览器输出一个文件(一张图片)
  7. * 目标:浏览器访问ServletDemo05,会在浏览器上显示一张图片
  8. */
  9. @WebServlet("/demo05")
  10. public class ServletDemo05 extends HttpServlet {
  11. @Override
  12. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  13. doGet(request, response);
  14. }
  15. @Override
  16. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  17. //1. 读取b.jpg图片,将其转换成字节输入流,使用ServletContext
  18. ServletContext servletContext = getServletContext();
  19. InputStream is = servletContext.getResourceAsStream("b.jpg");
  20. //2. 使用字节输出流,将is中的字节都输出到浏览器
  21. ServletOutputStream os = response.getOutputStream();
  22. /*byte[] buffer = new byte[1024];
  23. int len = 0;
  24. while ((len = is.read(buffer)) != -1){
  25. os.write(buffer,0,len);
  26. }*/
  27. IOUtils.copy(is,os);
  28. os.close();
  29. is.close();
  30. }
  31. }

response部分的总结

  1. 设置响应状态码:setStatus()
  2. 设置响应头:setHeader(name,value)

    1. refresh响应头,用于隔几秒钟之后跳转到某个页面
    2. location响应头,用于重定向到某个页面
  3. 重定向的写法: sendRedirect(地址)
  4. 设置响应体的内容

    1. 使用字符输出流输出文本内容

      1. response.getWriter()获取字符输出流
      2. writer.write()/print()输出字符串
      3. 解决响应数据的中文乱码:response.setContentType(“text/html;charset=UTF-8”)
    2. 使用字节输出流输出文件

      1. response.getOutputStream()获取字节输出流
  5. 使用IO流的框架进行边读边写

路径问题

一、完整url地址

url的组成部分:

  1. 协议 http://
  2. 服务器主机地址 localhost
  3. 服务器的端口号 :8080
  4. 项目的虚拟路径(部署路径) responseDemo
  5. 具体的项目上资源路径 /pages/hello.html 或者 /demo02 Servlet的映射路径

什么时候会使用完整的url

  1. 浏览器地址栏直接访问
  2. 一个项目中,访问另一个项目中的资源

二、相对路径

相对路径的概念

不以”/“开头的路径写法,它是以目标路径相对当前文件的路径,其中”..”表示上一级目录

它是以目标资源的url,相对当前资源的url

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <h1>hello world....</h1>
  9. <!--
  10. 目标资源的url: http://localhost:8080/responseDemo/demo05
  11. 当前资源的url: http://localhost:8080/responseDemo/pages/demo.html
  12. 相对路径的优劣:
  13. 1. 优势: 无论部署的项目名怎么改变,我的路径都不需要改变
  14. 2. 劣势: 如果当前资源的位置发生改变,那么相对路径就必定要发生改变
  15. -->
  16. <a href="../demo05">访问ServletDemo05</a>
  17. </body>
  18. </html>

三、绝对路径

绝对路径的概念

绝对路径就是以”/“开头的路径写法,它有如下两种情况

  1. 请求转发的绝对路径写法 “/资源的路径”,不需要写项目路径,例如”/hello.html”
  2. 不是请求转发的绝对路径写法”/项目部署路径/资源路径” 例如 “/responseDemo/hello.html”

案例-完成文件下载

1.需求分析

  • 创建文件下载的列表的页面,点击列表中的某些链接,下载文件.

request&response - 图6

2.文件下载分析

2.1什么是文件下载

  1. 将服务器上已经存在的文件,输出到客户端浏览器.
  2. 说白了就是把服务器端的文件拷贝一份到客户端, 文件的拷贝---> 流(输入流和输出流)的拷贝

2.2文件下载的方式

  • 第一种:超链接方式(不推荐)
    链接的方式:直接将服务器上的文件的路径写到href属性中.如果浏览器不支持该格式文件,那么就会提示进行下载, 如果 浏览器支持这个格式(eg: png, jpg….)的文件,那么直接打开,不再下载了

  • 第二种:手动编码方式(推荐)
    手动编写代码实现下载.无论浏览器是否识别该格式的文件,都会下载.

3.思路分析

3.1超链接方式

  1. 准备下载的资源(文件)
  2. 编写一个下载页面
  3. 在这个页面上定义超链接,指定href

3.2编码方式

3.2.1手动编码方式要求
  1. 设置两个头和一个流
  2. 设置的两个头:

Content-Dispostion: 服务器告诉浏览器去下载

Content-Type: 告诉浏览器文件类型.(MIME的类型)

  1. 设置一个流:

获得要下载的文件的输入流.

3.2.2思路

request&response - 图7

4.代码实现

  1. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2. request.setCharacterEncoding("utf-8");
  3. response.setContentType("text/html;charset=utf-8");
  4. //1.获得文件名fileName
  5. String fileName = request.getParameter("fileName");
  6. System.out.println("fileName="+fileName); //eg: a.jpg
  7. //2.设置两头一流
  8. //2.1 一流, 根据文件名获得文件的输入流
  9. InputStream is = getServletContext().getResourceAsStream("download/" + fileName);
  10. //2.2 一头, 告诉浏览器文件的MIME类型(可以不写)
  11. String mimeType = getServletContext().getMimeType(fileName);
  12. response.setHeader("Content-Type",mimeType);
  13. //2.3 一头, 告诉浏览器去下载
  14. response.setHeader("Content-Disposition","attachment;filename="+fileName);
  15. //3.通过response获得输出流(字节流)
  16. OutputStream os = response.getOutputStream();
  17. //4.流的操作
  18. byte[] b = new byte[1024];
  19. int len = 0;
  20. while ( (len=is.read(b))!=-1 ){
  21. os.write(b,0,len);
  22. }
  23. is.close();
  24. os.close();
  25. }

5.细节处理

  • 告诉浏览器设置的响应头里面不支持中文的, 抓包来看:

request&response - 图8

  • 解决方案: 手动进行编码再设置进去就ok了

中文文件在不同的浏览器中编码方式不同:火狐是Base64编码, 其它浏览器(谷歌)是URL的utf-8编码

  1. if(agent.contains("Firefox")){
  2. // 火狐浏览器
  3. filename = base64EncodeFileName(filename);
  4. }else{
  5. // IE,其他浏览器
  6. filename = URLEncoder.encode(filename, "UTF-8");
  7. }
  8. public static String base64EncodeFileName(String fileName) {
  9. BASE64Encoder base64Encoder = new BASE64Encoder();
  10. try {
  11. return "=?UTF-8?B?"
  12. + new String(base64Encoder.encode(fileName
  13. .getBytes("UTF-8"))) + "?=";
  14. } catch (UnsupportedEncodingException e) {
  15. e.printStackTrace();
  16. throw new RuntimeException(e);
  17. }
  18. }

第三章-综合案例

案例-注册

1. 需求

request&response - 图9

2. 路径

  1. 三层结构的讲解
  2. 完成注册功能

3. 代码实现

3.1 三层架构(今天先不讲)

  • 软件中分层:按照不同功能分为不同层,通常分为三层:表现层(web层),业务层,持久(数据库)层。
    request&response - 图10

  • 不同层次包名的命名

分层 包名(公司域名倒写)
表现层(web层) com.itheima.web
业务层(service层) com.itheima.service
持久层(数据库访问层) com.itheima.dao
JavaBean com.itheima.bean
工具类 com.itheima.utils
  • 分层的意义:

    1. 解耦:降低层与层之间的耦合性。
    2. 可维护性:提高软件的可维护性,对现有的功能进行修改和更新时不会影响原有的功能。
    3. 可扩展性:提升软件的可扩展性,添加新的功能的时候不会影响到现有的功能。
    4. 可重用性:不同层之间进行功能调用时,相同的功能可以重复使用。
  • 程序设计的宗旨:

    • 高内聚低耦合
    • 可扩展性强
    • 可维护性强
    • 可重用性强

3.2 完成注册案例

3.2.1 注册案例思路

request&response - 图11

3.2.2准备工作
  • 数据库
  1. create database day25;
  2. use day25;
  3. DROP TABLE IF EXISTS `user`;
  4. CREATE TABLE `user` (
  5. `id` int(11) NOT NULL AUTO_INCREMENT,
  6. `username` varchar(40) DEFAULT NULL,
  7. `password` varchar(40) DEFAULT NULL,
  8. `address` varchar(40) DEFAULT NULL,
  9. `nickname` varchar(40) DEFAULT NULL,
  10. `gender` varchar(10) DEFAULT NULL,
  11. `email` varchar(20) DEFAULT NULL,
  12. `status` varchar(10) DEFAULT NULL,
  13. PRIMARY KEY (`id`)
  14. ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8
  • JavaBean
  1. public class User implements Serializable{
  2. private Integer id;
  3. private String username;
  4. private String password;
  5. private String address;
  6. private String nickname;
  7. private String gender;
  8. private String email;
  9. private String status;//1 表示已激活 0表示未激活
  10. //...
  11. }
  • 导入jar

    • mysql驱动
    • druid
    • dbutils
    • beanutils
  • 工具类和配置文件

    • DruidUtil
    • druid.properties

3.2.3 注册案例实现

注册页面

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>注册页面</title>
  6. </head>
  7. <body>
  8. <form action="/userDemo/register" method="post">
  9. 用户名<input type="text" name="username"><br>
  10. 密码<input type="text" name="password"><br>
  11. 昵称<input type="text" name="nickname"><br>
  12. 地址<input type="text" name="address"><br>
  13. 邮箱<input type="text" name="email"><br>
  14. 性别<input type="radio" name="gender" value="male">
  15. <input type="radio" name="gender" value="female">
  16. <br>
  17. <input type="submit" value="注册">
  18. </form>
  19. </body>
  20. </html>

RegisterServlet的代码

  1. /**
  2. * 包名:${PACKAGE_NAME}
  3. *
  4. * @author Leevi
  5. * 日期2020-07-12 16:15
  6. */
  7. @WebServlet("/register")
  8. public class RegisterServlet extends HttpServlet {
  9. @Override
  10. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  11. doGet(request, response);
  12. }
  13. @Override
  14. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  15. //在最前面解决乱码问题:请求参数的中文乱码,响应的中文乱码
  16. request.setCharacterEncoding("UTF-8");
  17. response.setContentType("text/html;charset=UTF-8");
  18. //1. 获取所有的请求参数
  19. Map<String, String[]> parameterMap = request.getParameterMap();
  20. //2. 使用BeanUtils 将parameterMap中的数据,存储到User对象中
  21. User user = new User();
  22. //设置默认的status为"0"
  23. user.setStatus("0");
  24. try {
  25. BeanUtils.populate(user,parameterMap);
  26. //3. 使用DBUtils将用户信息存储到数据库
  27. //这里需要mysql驱动、druid、dbutils的jar包
  28. QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());
  29. String sql = "insert into user values (null,?,?,?,?,?,?,?)";
  30. queryRunner.update(sql,user.getUsername(),user.getPassword(),user.getAddress(),
  31. user.getNickname(),user.getGender(),user.getEmail(),user.getStatus());
  32. //如果存储的时候没有出现问题,则说明注册成功,使用重定向跳转到登录页面
  33. response.sendRedirect("/userDemo/login.html");
  34. } catch (Exception e) {
  35. e.printStackTrace();
  36. //如果注册失败,则向浏览器响应一句"注册失败"
  37. response.getWriter().write("注册失败");
  38. }
  39. }
  40. }

4. 小结

  1. 注册本质: 向数据库插入一条记录
  2. 思路(在RegisterServlet)

    • 获得用户提交的数据, 使用BeanUtils封装成User对象
    • 补全User对象(状态)
    • 使用DBUtils向数据库里面插入一条记录
    • 响应

案例-登录

1.需求

request&response - 图12

  • 点击登录按钮, 进行登录.
  • 登录成功,显示login Success
  • 登录失败,显示login failed

2.思路

request&response - 图13

3.代码实现

3,1准备工作

  • 页面的准备 login.html
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>登录页面</title>
  6. </head>
  7. <body>
  8. <form action="/userDemo/login" method="post">
  9. 用户名<input type="text" name="username"><br>
  10. 密码<input type="text" name="password"><br>
  11. <input type="submit" value="登录">
  12. </form>
  13. </body>
  14. </html>

3.2代码实现

  1. /**
  2. * 包名:${PACKAGE_NAME}
  3. *
  4. * @author Leevi
  5. * 日期2020-07-12 16:44
  6. */
  7. @WebServlet("/login")
  8. public class LoginServlet extends HttpServlet {
  9. @Override
  10. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  11. doGet(request, response);
  12. }
  13. @Override
  14. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  15. try {
  16. //1. 解决乱码
  17. request.setCharacterEncoding("UTF-8");
  18. response.setContentType("text/html;charset=UTF-8");
  19. //2. 获取请求参数username和password
  20. String username = request.getParameter("username");
  21. String password = request.getParameter("password");
  22. //3. 连接数据库校验用户名和密码,也就是执行查询的SQL语句
  23. QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());
  24. String sql = "select * from user where username=? and password=?";
  25. //执行查询,查询一条数据,封装到User中
  26. User user = queryRunner.query(sql, new BeanHandler<>(User.class), username, password);
  27. //判断是否登录成功
  28. if (user != null) {
  29. //登录成功
  30. //跳转到成功页面success.html
  31. response.sendRedirect("/userDemo/success.html");
  32. }else {
  33. //登陆失败,直接向浏览器输出"登陆失败"
  34. response.getWriter().write("登陆失败");
  35. }
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. //登陆失败,直接向浏览器输出"登陆失败"
  39. response.getWriter().write("登陆失败");
  40. }
  41. }
  42. }

4.小结

  1. 本质: 就是根据用户名和密码查询数据库
  2. 思路(LoginServlet)

    • 获得用户输入用户名和密码
    • 使用DBUtils根据用户名和密码查询数据库 封装成User对象
    • 判断是否登录成功(判断User是否为null)
    • 响应