HTTP协议

概念:

Hyper Text Transfer Protocol 超文本传输协议

传输协议:

定义了,客户端和服务器端通信时,发送数据的格式

特点:

  1. 基于TCP/IP的高级协议
  2. 默认端口号:80
  3. 基于请求/响应模型的:一次请求对应一次响应
  4. 无状态的:每次请求之间相互独立,不能交互数据

历史版本:

  • 1.0:每一次请求响应都会建立新的连接
  • 1.1:复用连接

request

运算

字符串格式:

1
2
3
4
5
6
7
8
9
10
11
POST /login.html HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost/login.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1

username=zhangsan

请求行

请求方式 请求url 请求协议/版本
GET /login.html HTTP/1.1

请求方式:

HTTP协议有7中请求方式,常用的有2

  • GET
    1. 请求参数在请求行中,在url后。
    2. 请求的url长度有限制的
    3. 不太安全
  • POST
    1. 请求参数在请求体中
    2. 请求的url长度没有限制的
    3. 相对安全

请求头:

客户端浏览器告诉服务器一些信息

请求头名称: 请求头值

常见的请求头:
  1. User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息

    • 可以在服务器端获取该头的信息,解决浏览器的兼容性问题
  2. Refererhttp://localhost/login.html

    告诉服务器,我(当前请求)从哪里来?

    作用:

    1. 防盗链:

    2. 统计工作:

请求空行

空行,就是用于分割POST请求的请求头和请求体的。

请求体(正文):

  • 封装POST请求消息的请求参数的

Request对象

request对象和response对象的原理

  1. request和response对象是由服务器创建的。我们来使用它们

  2. request对象是来获取请求消息,response对象是来设置响应消息

request对象继承体系结构:

1
2
3
4
5
ServletRequest		--	接口
| 继承
HttpServletRequest -- 接口
| 实现
org.apache.catalina.connector.RequestFacade 类(tomcat)

request功能:

获取请求消息数据
获取请求行数据
1
GET /day14/demo1?name=zhangsan HTTP/1.1
方法:
  1. 获取请求方式 :GET

    String getMethod()

  2. 获取虚拟目录:/day14

    String getContextPath()

  3. 获取Servlet路径: /demo1

    String getServletPath()

  4. 获取get方式请求参数:name=zhangsan

    String getQueryString()

  5. 获取请求URI/day14/demo1

    String getRequestURI(): /day14/demo1

    StringBuffer getRequestURL() :http://localhost/day14/demo1

    URL:统一资源定位符 : http://localhost/day14/demo1 中华人民共和国

    URI:统一资源标识符 : /day14/demo1 共和国

  6. 获取协议及版本:HTTP/1.1

    String getProtocol()

  7. 获取客户机的IP地址:

    String getRemoteAddr()

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.uestc.web.servlet02;
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({"/demo"})
public class ServletDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 获取请求方式 :`GET`
String method=req.getMethod();
System.out.println(method);

//2. 获取虚拟目录:`/day14`
String contextPath=req.getContextPath();
System.out.println(contextPath);

//获取Servlet路径: `/demo1`
String Servletpath=req.getServletPath();
System.out.println(Servletpath);

//获取请求参数
String queryString=req.getQueryString();
System.out.println(queryString);

//获取URI
String uri=req.getRequestURI();
System.out.println(uri);

//获取URI
StringBuffer url=req.getRequestURL();
System.out.println(url.toString());

//获取协议版本
String protocal=req.getProtocol();
System.out.println(protocal);

//获取ip
String remoteAddr=req.getRemoteAddr();
System.out.println(remoteAddr);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost...");
}

}

获取请求头数据
方法:

String getHeader(String name):通过请求头的名称获取请求头的值

Enumeration<String> getHeaderNames():获取所有的请求头名称

示例1:打印所有的请求头的名称以及对于的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.uestc.web.servlet02;
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.util.Enumeration;

@WebServlet({"/demo01"})
public class ServletDemo01 extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//演示获取请求头数据
//1.获取所有请求头名称
Enumeration<String> headerNames= req.getHeaderNames();
//2.遍历
while(headerNames.hasMoreElements()){
String name=headerNames.nextElement();
//根据名称来获取请求头的值
String value=req.getHeader(name);
System.out.println(name+"--"+value);
}
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost...");
}

}

示例2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@WebServlet({"/demo01"})
public class ServletDemo01 extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//演示获取use-agent
String user_agent=req.getHeader("user-agent");
System.out.println(user_agent);
if(user_agent.contains("Chrome")){
System.out.println("谷歌。。");
}else if(user_agent.contains("IE")){
System.out.println("IE。。");
}

}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost...");
}

}
获取请求体数据:

请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数

步骤:
  1. 获取流对象

    BufferedReader getReader():获取字符输入流,只能操作字符数据

    ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据,在文件上传知识点后讲解

  2. 再从流对象中拿数据

示例:

  1. 首先创建regist.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="/servlet02/requestDemo01" method="post">
<input type="text" placeholder="请输入用户名" name="username"><br>
<input type="text" placeholder="请输出密码" name="password"><br>
<input type="submit" type="注册">


</form>
</body>
</html>
  1. 然后创建RequestDemo01.java文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@WebServlet("/requestDemo01")
public class RequestDemo01 extends HttpServlet{

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求消息题体--请求参数
//1.获取字符流
BufferedReader br=req.getReader();
//2.读取数据
String line=null;
while((line=br.readLine())!=null){
System.out.println(line);
}
}
}
  1. 启动tomcat服务器,在浏览区中访问http://localhost:8080/servlet02/regist.html

其他功能:

获取请求参数通用方式:

不论get还是post请求方式都可以使用下列方法来获取请求参数

  1. String getParameter(String name):根据参数名称获取参数值 username=zs&password=123

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @WebServlet("/requestDemo01")
    public class RequestDemo01 extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //get 获取请求参数
    //根据参数的名称获取参数值
    String username=req.getParameter("username");
    System.out.println("get");
    System.out.println(username);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //post 获取请求参数
    //根据参数的名称获取参数值
    String username=req.getParameter("username");
    System.out.println("post");
    System.out.println(username);
    }
    }

  1. String[] getParameterValues(String name):根据参数名称获取参数值的数组

    hobby=xx&hobby=game

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //servlet类
    @WebServlet("/requestDemo01")
    public class RequestDemo01 extends HttpServlet{
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //post 获取请求参数
    //根据参数名称获取参数值的数组
    String [] hobbies=req.getParameterValues("hobby");
    for(String hobby:hobbies){
    System.out.println(hobby);
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <!-- regist.html-->
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>注册页面</title>
    </head>
    <body>
    <form action="/servlet02/requestDemo01" method="post">
    <input type="text" placeholder="请输入用户名" name="username"><br>
    <input type="text" placeholder="请输出密码" name="password"><br>
    <input type="checkbox" name="hobby" value="game">游戏
    <input type="checkbox" name="hobby" value="study">学习
    <input type="submit" type="注册">

    </form>
    </body>
    </html>

  1. Enumeration<String> getParameterNames():获取所有请求的参数名称

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @WebServlet("/requestDemo01")
    public class RequestDemo01 extends HttpServlet{
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //post 获取请求参数
    //根据参数名称获取参数值的数组
    Enumeration<String> parameterNames=req.getParameterNames();
    while(parameterNames.hasMoreElements()){
    String name=parameterNames.nextElement();
    System.out.println(name);
    String value=req.getParameter(name);
    System.out.println(value);
    System.out.println("---------------------------");
    }
    }

  2. Map<String,String[]> getParameterMap():获取所有参数的map集合

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @WebServlet("/requestDemo01")
    public class RequestDemo01 extends HttpServlet{
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //post 获取请求参数
    //根据参数名称获取参数值的数组
    Map<String, String []> map=req.getParameterMap();
    Set<String> set=map.keySet();
    for(String name:set){
    String [] values=map.get(name);
    System.out.println(name);
    for(String value:values){
    System.out.println(value);
    }
    System.out.println("--------------");
    }
    }
    }

中文乱码问题:

  • get方式:tomcat 8已经将get方式乱码问题解决了
  • post方式:会乱码
    • 解决:在获取参数前,设置request的编码request.setCharacterEncoding("utf-8");
1.2242 请求转发:

一种在服务器内部的资源跳转方式

步骤:
  1. 通过request对象获取请求转发器对象:RequestDispatcher

    1
    getRequestDispatcher(String path)
  2. 使用RequestDispatcher对象来进行转发:

    1
    forward(ServletRequest request, ServletResponse response)
特点:
  1. 浏览器地址栏路径不发生变化
  2. 只能转发到当前服务器内部资源中。
  3. 转发是一次请求

示例:

新建RequestDemo02.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@WebServlet("/requestDemo02")
public class RequestDemo02 extends HttpServlet {

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("访问了requestDemo02...");
// RequestDispatcher requestDispatcher= req.getRequestDispatcher("/requestDemo01");
// requestDispatcher.forward(req,resp);
req.getRequestDispatcher("/requestDemo01").forward(req,resp);
}

}

1.2243 共享数据:
域对象:

一个有作用范围的对象,可以在范围内共享数据

request域:

代表一次请求的范围,一般用于请求转发的多个资源中共享数据

方法:
  1. void setAttribute(String name,Object obj):存储数据

  2. Object getAttitude(String name):通过键获取值

  3. void removeAttribute(String name):通过键移除键值对

  4. 获取ServletContext

    ServletContext getServletContext()

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@WebServlet("/requestDemo02")
public class RequestDemo02 extends HttpServlet {

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("访问了requestDemo02...");

//存储数据到Request域中
req.setAttribute("msg","hello");

req.getRequestDispatcher("/requestDemo01").forward(req,resp);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@WebServlet("/requestDemo01")
public class RequestDemo01 extends HttpServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("访问了requestDemo01...");
Object msg=req.getAttribute("msg");
System.out.println(msg);
}
}

案例1:用户登录

用户登录案例需求:

  1. 编写login.html登录页面
    username & password 两个输入框

    1. 使用Druid数据库连接池技术,操作mysql

      day14数据库中user表

    2. 使用JdbcTemplate技术封装JDBC

    3. 登录成功跳转到SuccessServlet

      展示:登录成功!用户名,欢迎您

    4. 登录失败跳转到FailServlet展示:登录失败,用户名或密码错误

开发步骤

  1. 创建项目,导入html页面,配置文件,jar

导入jar包之后需要Add as Library

  1. 创建数据库环境

    1
    2
    3
    4
    5
    6
    7
    CREATE DATABASE day14;
    USE day14;
    CREATE TABLE USER(
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(32) UNIQUE NOT NULL,
    password VARCHAR(32) NOT NULL
    );

  1. 创建包com.uestc.domin,创建类User

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    package com.uestc.domin;

    /**
    * 用户的实体类
    */
    public class User {
    private int id;
    private String username;
    private String password;

    public int getId() {
    return id;
    }

    public void setId(int id) {
    this.id = id;
    }

    public String getUsername() {
    return username;
    }

    public void setUsername(String username) {
    this.username = username;
    }

    public String getPassword() {
    return password;
    }

    public void setPassword(String password) {
    this.password = password;
    }

    @Override
    public String toString() {
    return "User{" +
    "id=" + id +
    ", username='" + username + '\'' +
    ", password='" + password + '\'' +
    '}';
    }
    }
  2. 创建包com.uestc.util,编写工具类JDBCUtils`

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    package com.uestc.util;

    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.pool.DruidDataSourceFactory;

    import javax.sql.DataSource;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Properties;

    /**
    * JDBC工具类,使用Durid连接池
    */
    public class JDBCUtils {
    private static DataSource ds;

    static {

    try {
    //1.加载配置文件
    Properties pro=new Properties();
    //使用classLoader加载配置文件,获取字节输入流
    InputStream is=JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
    pro.load(is);
    //2.初始化连接池对象
    ds=DruidDataSourceFactory.createDataSource(pro);

    } catch (IOException e) {
    e.printStackTrace();
    } catch (Exception e) {
    e.printStackTrace();
    }

    }
    /**
    * 获取连接池对象
    */
    public static DataSource getDataSource(){
    return ds;
    }

    /**
    * 获取连接Connection对象
    */
    }
  3. 创建包com.uestc.dao,创建类UserDao,提供login方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    package com.uestc.dao;

    import com.uestc.domin.User;
    import com.uestc.util.JDBCUtils;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;

    /**
    *操作数据库中User表的类
    */
    public class UserDao {

    //生命JDBCTemplate对象共用
    private JdbcTemplate template=new JdbcTemplate(JDBCUtils.getDataSource());
    /**
    * 登陆方法
    * @param loginUser 只有用户名和密码
    * @return user包含用户全部数据
    */
    public User login(User loginUser){
    //1.编写sql
    String sql="select * from user where username=? and password=?";
    //2.调用query方法
    User user=template.queryForObject(sql,
    new BeanPropertyRowMapper<User>(User.class),
    loginUser.getUsername(),loginUser.getPassword());
    return user;
    }

    }
  4. 单元测试login方法,新建包com.uestc.test , 创建UserDaoTest

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package com.uestc.test;
    import com.uestc.dao.UserDao;
    import com.uestc.domin.User;
    import org.junit.Test;
    public class UserDaoTest {
    @Test
    public void testLogin(){
    User loginUser=new User();
    loginUser.setUsername("liufei");
    loginUser.setPassword("123456");

    UserDao dao=new UserDao();
    User user=dao.login(loginUser);

    System.out.println(user);
    }
    }
  1. 编写cn.uestc.web.servlet.LoginServlet

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    package com.uestc.web.servlet;

    import com.uestc.dao.UserDao;
    import com.uestc.domin.User;

    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("/loginServlet")
    public class LoginServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //1.设置编码
    req.setCharacterEncoding("utf-8");
    //2.获取请求参数
    String username=req.getParameter("username");
    String password=req.getParameter("password");
    //3.封装user对象
    User loginUser=new User();
    loginUser.setUsername(username);
    loginUser.setPassword(password);
    //4.调用UserDao的login的方法
    UserDao dao=new UserDao();
    User user=dao.login(loginUser);

    //5.判断user
    if(user==null){
    //登录失败
    req.getRequestDispatcher("/failServlet").forward(req,resp);
    }else{
    //登录成功
    //存储数据
    req.setAttribute("user",user);
    //转发
    req.getRequestDispatcher("/successServlet").forward(req,resp);
    }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doGet(req,resp);
    }
    }
  2. 编写FailServletSuccessServlet

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    //FailServlet类
    package com.uestc.web.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("/failServlet")
    public class FailedServlet extends HttpServlet{
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //给页面写一句话

    //设置编码
    resp.setContentType("text/html;charset=utf-8");

    //输出
    resp.getWriter().write("登录失败, 用户名或密码错误");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    super.doPost(req, resp);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    //SuccessServlet类
    package com.uestc.web.servlet;
    import com.uestc.domin.User;
    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("/successServlet")
    public class SuccessServlet extends HttpServlet{
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //给页面写一句话
    //获取request域中共享的user对象
    User user=(User)req.getAttribute("user");
    if(user!=null){
    //设置编码
    resp.setContentType("text/html;charset=utf-8");

    //输出
    resp.getWriter().write("登录成功!"+user.getUsername()+"欢迎您!");
    }

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    super.doPost(req, resp);
    }
    }
  3. login.htmlform表单的action路径的写法

    1
    虚拟目录+Servlet的资源路径

    示例:

    ![image-20201018151952299](C:\Users\Liu Fei\AppData\Roaming\Typora\typora-user-images\image-20201018151952299.png)

    ![image-20201018152115095](C:\Users\Liu Fei\AppData\Roaming\Typora\typora-user-images\image-20201018152115095.png)

  4. BeanUtils工具类,简化数据封装

    首先导入beanUtilsjar

    ​ 然后使用beanUtils![image-20201018154652738](C:\Users\Liu Fei\AppData\Roaming\Typora\typora-user-images\image-20201018154652738.png)

BeanUtils工具类

BeanUtils用于封装JavaBean

JavaBean:标准的Java类, 用于封装数据

  1. 类必须被public修饰
  2. 必须提供空参的构造器
  3. 成员变量必须使用private修饰
  4. 提供公共setter和getter方法

注意的成员变量和属性的区别:

属性:settergetter方法截取后的产物
例如:getUsername() –> Username–> username

有时候属性和成员变量不相同

方法:

  1. setProperty()
  2. getProperty()
  3. populate(Object obj , Map map):将map集合的键值对信息,封装到对应的JavaBean对象中

示例:

response

响应消息:服务器端发送给客户端的数据

响应消息的数据格式

字符串格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
#响应字符串格式
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 101
Date: Wed, 06 Jun 2018 07:08:42 GMT
<html>
<head>
<title>$Title$</title>
</head>
<body>
hello , response
</body>
</html>

响应行

组成:

协议/版本 响应状态码 状态码描述

响应状态码:

服务器告诉客户端浏览器本次请求和响应的一个状态。

状态码都是3位数字

状态码分类:

  1. 1xx:服务器就收客户端消息,但没有接受完成,等待一段时间后,发送1xx多状态码

  2. 2xx:成功。代表:200

  3. 3xx:重定向。代表:302(重定向),304(访问缓存)

  4. 4xx:客户端错误。

    代表:

    • 404(请求路径没有对应的资源)
    • 405:请求方式没有对应的doXxx方法
  5. 5xx:服务器端错误。代表:500(服务器内部出现异常)

响应头:

格式:

头名称: 值

常见的响应头:

头名称:

  1. Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式

  2. Content-disposition:服务器告诉客户端以什么格式打开响应体数据

    值:

    • in-line: 默认值,在当前页面内打开

    • attachment;filename=xxx:以附件形式打开响应体。文件下载

响应空行

响应体:

传输的数据

Response对象

功能:设置响应消息

设置响应行

格式:HTTP/1.1 200 ok

设置状态码setStatus(int sc)

设置响应头:

setHeader(String name, String value)

设置响应体:

使用步骤:

  1. 获取输出流

    • 字符输出流:PrintWriter getWriter()

    • 字节输出流:ServletOutputStream getOutputStream()

  2. 使用输出流,将数据输出到客户端浏览器

案例:

  1. 完成重定向

  2. 服务器输出字符数据到浏览器

  3. 服务器输出字节数据到浏览器

  4. 验证码

    完成重定向

重定向:资源跳转的方式

代码实现:
1
2
3
4
//1. 设置状态码为302
response.setStatus(302);
//2.设置响应头location
response.setHeader("location","/servlet03/responseDemo02");
  1. 创建ResponseDemo01 Servlet类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    package com.uestc.web.servlet;

    import com.sun.org.apache.xpath.internal.SourceTree;

    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;

    /**
    * 重定向
    * 访问/responseDemo01,会自动跳转到/responseDemo02
    */
    @WebServlet("/responseDemo01")
    public class ResponseDemo01 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println("responseDemo01启动。。。");
    //1.设置状态码302
    resp.setStatus(302);
    //设置响应头location
    resp.setHeader("location","/servlet03/responseDemo02");

    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doPost(req, resp);
    }
    }
  2. 创建ResponseDemo02 Servlet类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package com.uestc.web.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("/responseDemo02")
    public class ResponseDemo02 extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println("responseDemo02启动。。。");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doPost(req, resp);
    }
    }

简单的重定向方法:

1
response.sendRedirect("/day15/responseDemo2");
1.4311 forward 和 redirect 区别
1.43111 重定向的特点: redirect
  1. 地址栏发生变化
  2. 重定向可以访问其他站点(服务器)的资源
  3. 重定向是两次请求。不能使用request对象来共享数据
1.43112 转发的特点:forward
  1. 转发地址栏路径不变
    1. 转发只能访问当前服务器下的资源
    2. 转发是一次请求,可以使用request对象来共享数据
1.4312 路径写法:

路径分类

  1. 相对路径:通过相对路径不可以确定唯一资源

    • 如:./index.html
      • 不以/开头,以.开头路径

    规则:找到当前资源目标资源之间的相对位置关系
    ./:当前目录
    ../: 后退一级目录

  2. 绝对路径:通过绝对路径可以确定唯一资源

    规则:判断定义的路径是给谁用的?判断请求将来从哪儿发出

    • 客户端浏览器使用:需要加虚拟目录(项目的访问路径)

      建议:虚拟目录动态获取:request.getContextPath()

      <a> , <form> 重定向

    • 服务器使用:不需要加虚拟目录

      转发路径

服务器输出字符数据到浏览器

步骤:
  1. 获取字符输出流

  2. 输出数据

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package com.uestc.web.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("/responseDemo03")
    public class ResponseDemo03 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //1.获取字符输出流
    PrintWriter pw=resp.getWriter();
    //2.输出数据
    pw.write("<h1>hello response</h2>");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doPost(req,resp);
    }
    }

乱码问题:

1. `PrintWriter pw = response.getWriter()`;获取的流的默认编码是`ISO-8859-1`
2. 设置该流的默认编码
3. 告诉浏览器响应体使用的编码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.uestc.web.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("/responseDemo03")
public class ResponseDemo03 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取流对象之前,设置流的默认编码:ISO-8859-1 设置为:GBK
//resp.setCharacterEncoding("utf-8");
//告诉浏览器,服务器发送的消息体数据的编码,建议浏览器使用该编码解码
//resp.setHeader("content-type", "text/html;charset=utf-8");
//简单的形式,设置编码
resp.setContentType("text/html;charset=utf-8");
//1.获取字符输出流
PrintWriter pw=resp.getWriter();
//2.输出数据
pw.write("<h1>您好啊, response</h2>");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
}

服务器输出字节数据到浏览器

步骤:
  1. 获取字节输出流

  2. 输出数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package com.uestc.web.servlet; 
    import javax.servlet.Servlet;
    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;
    @WebServlet("/responseDemo04")
    public class ResponseDemo04 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.setContentType("text/html;charset=utf-8");
    //1.获取字节输出流
    ServletOutputStream sos=resp.getOutputStream();

    //2.输出数据
    sos.write("你好".getBytes("utf-8"));
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doPost(req,resp);
    }
    }

验证码

  1. 本质:图片

  2. 目的:防止恶意表单注册

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    package com.uestc.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 CheckCodeServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    int width=100;
    int height=50;
    //1.创建对象,在内存中的图片(验证码图片对象)
    BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
    //2.美化图片
    //2.1 背景填充
    //获取画笔对象
    Graphics g=image.getGraphics();
    //设置画笔颜色
    g.setColor(Color.PINK);
    //填充颜色
    g.fillRect(0,0,width,height);

    //2.2 画边框
    g.setColor(Color.BLUE);
    g.drawRect(0,0,width-1,height-1);

    String str="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    //生成随机角标
    Random rd=new Random();
    for(int i=0;i<4;i++){
    int index=rd.nextInt(str.length());
    //获取字符
    char ch=str.charAt(index);
    //2.3 写验证码
    g.drawString(ch+"",(width/5)*(i+1),25);
    }

    //2.4 画干扰线
    //生成随机坐标点

    for(int i=0;i<5;i++){
    int x1=rd.nextInt(width);
    int y1=rd.nextInt(height);

    int x2=rd.nextInt(width);
    int y2=rd.nextInt(height);
    g.drawLine(x1,y1,x2,y2);
    }

    //3.将图片输出到页面展示
    ImageIO.write(image,"jpg",resp.getOutputStream());
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doPost(req,resp);
    }
    }

ServletContext对象

概念:

代表整个web应用,可以和程序的容器(服务器)来通信

获取:

  1. 通过request对象获取

    1
    request.getServletContext();
  2. 通过HttpServlet获取

    1
    this.getServletContext();

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @WebServlet("/servletContextDemo1")
    public class ServletContextDemo1 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //通过request对象获取
    ServletContext context1=req.getServletContext();
    //通过HttpServlet获取
    ServletContext context2=this.getServletContext();
    System.out.println(context1);
    System.out.println(context2);
    System.out.println(context1.equals(context2));
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doPost(req,resp);
    }
    }

功能:

获取MIME类型:

MIME类型:在互联网通信过程中定义的一种文件数据类型

格式: 大类型/小类型 text/html image/jpeg

获取:

1
String getMimeType(String file)

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@WebServlet("/servletContextDemo1")
public class ServletContextDemo1 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context=this.getServletContext();
String fileName="a.jpg";
String minType=context.getMimeType(fileName);
System.out.println(minType);
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
}

域对象:共享数据

1
2
3
1. setAttribute(String name,Object value)
2. getAttribute(String name)
3. removeAttribute(String name)

ServletContext对象范围:所有用户所有请求的数据

示例:

  1. 新建servletContextDemo1.java,通过ServletContext域对象赋值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @WebServlet("/servletContextDemo2")
    public class ServletContextDemo2 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ServletContext context=this.getServletContext();
    String msg=(String)context.getAttribute("msg");
    System.out.println(msg);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doPost(req,resp);
    }
    }
  2. 新建servletContextDemo2.java,获取ServletContext域对象的值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @WebServlet("/servletContextDemo2")
    public class ServletContextDemo2 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ServletContext context=this.getServletContext();
    String msg=(String)context.getAttribute("msg");
    System.out.println(msg);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doPost(req,resp);
    }
    }

获取文件的真实(服务器)路径

方法:

1
String getRealPath(String path)

示例: 想要获取文件的真实路径(区别与工作空间的目录)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@WebServlet("/servletContextDemo3")
public class ServletContextDemo03 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context=this.getServletContext();
String path0=context.getRealPath("/a.txt");//web目录下资源访问
System.out.println(path0);

String path1=context.getRealPath("/WEB-INF/b.txt");//WEB-INF目录下资源访问
System.out.println(path1);

String path2=context.getRealPath("/WEB-INF/classes/c.txt");
System.out.println(path2);
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
}

案例 文件下载

需求

  1. 页面显示超链接
  2. 点击超链接后弹出下载提示框
  3. 完成图片文件下载

分析:

  1. 超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求

  2. 任何资源都必须弹出下载提示框

  3. 使用响应头设置资源的打开方式:

    1
    content-disposition:attachment;filename=xxx

    步骤

  4. 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filenam

  5. 定义Servlet

    1. 获取文件名称
    2. 使用字节输入流加载文件进内存
    3. 指定response的响应头: content-disposition:attachment;filename=xxx
    4. 将数据写出到response输出流

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.uestc.web.download;

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 {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取请求参数,文件名称
String filename=req.getParameter("filename");
//2.使用字节输入流加载文件进内存
//2.1 找到文件服务器路径
ServletContext context=this.getServletContext();
String realpath=context.getRealPath("/img/"+filename);
//2.2 用字节流关联
FileInputStream fis=new FileInputStream(realpath);

//3.设置response的响应头
//3.1设置相应头类型:contend-type
//获取文件的mimeType
String minType=context.getMimeType(filename);
resp.setHeader("content-type",minType);
//3.2 设置相应头的打开方式:contend-disposition
resp.setHeader("content-disposition","attachment;filename="+filename);
//4.将输入流的数据写出到输出流中
ServletOutputStream sos=resp.getOutputStream();
byte [] buff=new byte[1024*8];
int len=0;
while ((len=fis.read(buff))!=-1){
sos.write(buff,0,len);
}
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
}

中文乱码问题

解决思路:

  1. 获取客户端使用的浏览器版本信息

  2. 根据不同的版本信息,设置filename的编码方式不同

步骤:

  1. 新建utils工具类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package com.uestc.web.utils;

    import sun.misc.BASE64Encoder;
    import java.io.UnsupportedEncodingException;
    import java.net.URLEncoder;

    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;
    }
    }
  2. 解决乱码问题

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    package com.uestc.web.download;

    import com.uestc.web.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 {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //1.获取请求参数,文件名称
    String filename=req.getParameter("filename");
    //2.使用字节输入流加载文件进内存
    //2.1 找到文件服务器路径
    ServletContext context=this.getServletContext();
    String realpath=context.getRealPath("/img/"+filename);
    //2.2 用字节流关联
    FileInputStream fis=new FileInputStream(realpath);

    //3.设置response的响应头
    //3.1设置相应头类型:contend-type
    //获取文件的mimeType
    String minType=context.getMimeType(filename);
    resp.setHeader("content-type",minType);
    //解决中文文件名问题
    //1.获取user-agent请求头
    String agent=req.getHeader("user-agent");
    //2.使用工具类方法编码文件名即可
    filename=DownLoadUtils.getFileName(agent,filename);

    //3.2 设置相应头的打开方式:contend-disposition
    resp.setHeader("content-disposition","attachment;filename="+filename);

    //4.将输入流的数据写出到输出流中
    ServletOutputStream sos=resp.getOutputStream();
    byte [] buff=new byte[1024*8];
    int len=0;
    while ((len=fis.read(buff))!=-1){
    sos.write(buff,0,len);
    }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doPost(req,resp);
    }
    }