WEB阶段2:http协议&请求对象【转发&重定向&请求参数乱码问题】&BeanUtils使用&软件开发三层架构项目结构

http协议&请求对象【转发&重定向&请求参数乱码问题】&BeanUtils使用&软件开发三层架构项目结构

回顾

有时要清除浏览器的缓存,才能看到最新的结果,Chrome清除缓存的快捷键:ctrl+shift+del

  1. 软件架构分成哪两种,分别是什么含义

    BS:浏览器服务器模式

    CS: 客户端服务器模式

  2. Tomcat目录结构

目录名作用
bin可执行文件,启动:startup.bat 关闭:shutdown.bat
conf配置文件,核心配置文件:server.xml
lib第三方的jar包
logs日志记录信息
temp临时目录
webapps项目发布目录
ROOT目录:启动tomcat以后默认的首页
在idea中开发的时候,没有这个首页
work工作目录,生成一些中间文件
  1. tomcat部署的三种方式

    直接复制到webapps
    
    配置server.xml文件,添加Context path="/虚拟路径" docBase="真实地址"
    不能有汉字,而且这个地址必须要存在
    
    独立的xml文件
    conf/catalina/localhost/文件名.xml
    文件名就是访问地址
    内容:Context docBase="真实地址"
    
    idea就是这种方式
    
  2. Servlet与普通的Java程序的区别

    1. 实现什么接口:javax.servlet.Servlet接口,我们是继承于HttpServlet
    2. 运行在Tomcat的容器中
    3. service中的方法参数是:HttpServletRequest请求对象,HttpServletResponse响应对象
  3. 说出注解的作用

@WebServlet注解作用
nameservlet的名字,不能出现相同的名字
urlPatterns访问地址,可以指定多个
value同上
  1. Servlet的生命周期方法
方法作用运行次数
void init(ServletConfig config)用户第一次访问的时候执行1次
void service(ServletRequest req, ServletResponse res)每次请求都会执行执行多次
void destroy()服务器关闭,销毁的时候执行1次

学习目标

  1. HTTP请求协议的格式
    1. 能够理解HTTP协议请求内容
    2. 能够使用Request对象获取HTTP协议请求内容
  2. 请求对象的使用
    1. 能够处理HTTP请求参数的乱码问题
    2. 能够使用Request域对象
    3. 能够使用Request对象做请求转发
  3. 能够完成登录案例

学习内容

1. HTTP协议的概念

目标

  1. 什么是HTTP
  2. 它有什么特点

概念

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

HTTP: Hyper Text Transfer Protocol 超文本传输协议 (传输超文本的协议) (公交车),HTTP就是用来传输HTML的

HTML: Hyper Text Markup Language 超文本标记语言 (乘客)

默认端口号:80端口,不是8080,8080是Tomcat的。浏览器如果使用80端口,可以省略。

HTTP协议的特点:

  1. 格式比较简单,数据传输比较快
  2. 服务器如果接收到多次浏览器发送的请求,通过HTTP协议是不能判断这是一个用户的多次请求,还是多个用户的请求。因为HTTP协议是一个无状态的协议,不会记录用户的访问情况。
  3. 每次用户发送请求以后,得到了服务器的响应,连接就断开了。
  4. 在HTTP1.1的版本中,一次请求可以请求多个资源。1.0每次请求只会请求一个资源。

HTTPS

HTTPS:功能与HTTP是一样的,也是用来传输网页的

  1. 这是一个加密的传输协议
  2. 可以识别客户端与服务器端,防止假冒的第三方网站。

端口号是:443

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

小结

  1. HTTP的作用是?用来传输HTML
  2. 默认的端口号是:80
  3. 特点:无状态的协议

2. HTTP请求的三个组成

目标

请求有哪三个组成部分

什么是请求

浏览器发送给服务器的数据

查看HTTP请求

  1. 在HTML页面上,制作2个表单提交页面,用户名和密码,get提交和post提交按钮,查看HTTP请求。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>登录页面</title>
    </head>
    <body>
    <h2>GET请求</h2>
    <form action="demo1" method="get">
        用户名:<input type="text" name="username"><br>
        密码:<input type="password" name="password"><br>
        <input type="submit" value="登录">
    </form>
    
    <h2>POST请求</h2>
    <form action="demo1" method="post">
        用户名:<input type="text" name="username"><br>
        密码:<input type="password" name="password"><br>
        <input type="submit" value="登录">
    </form>
    </body>
    </html>
    
  2. 查看浏览器与服务器的通讯,按F12打开窗口

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

请求的组成

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

小结

请求由哪三个组成部分?

  1. 请求行:只有一行数据
  2. 请求头:由多个键值对组成,键和值之间是冒号
  3. 请求体:浏览器发送给服务器的数据

3. 请求信息的组成:请求行

目标

  1. 请求行的格式
  2. POST和GET请求的区别

请求行组成

POST /day27_01_request_war_exploded/demo1 HTTP/1.1
GET /day27_01_request_war_exploded/demo1?username=jack&password=123 HTTP/1.1

三个部分:

  1. GET或POST,请求的方法

  2. URI:Uniform Resource Identifer 统一资源标识符,用来标识服务器上唯一资源的名字

  3. 协议和版本

POST与GET的区别

POST方式GET方式
地址栏没有参数
参数是在请求体中发送的
GET后面有参数的值,地址与参数之间使用?分隔
如果有多个参数使用&分隔。数据是在请求行中发送的
大小发送的数据没有大小限制受浏览器的限制,最大是1K
安全性相对更加安全参数是可以在地址栏上看到
缓存不会使用缓存浏览器会使用缓存
状态码:
200 表示从服务器上正确的获取数据
304 表示使用缓存

注:缓存只对静态资源起作用,动态资源是不起作用的。

浏览器上设置,让静态资源不使用缓存

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

小结

  1. 请求行由哪三个组成部分?
    1. 请求方式
    2. URI
    3. 协议和版本
  2. GET方法和POST方法传递数据有什么区别?
    1. 哪个使用缓存?GET
    2. 哪个在请求行中发送数据?GET
    3. POST在请求体中发送
    4. 哪个数据大小没有限制?POST

4. 请求信息的组成:请求头、请求体

目标

了解常见请求头的作用

常用请求头

格式:由键和值组成

请求头描述
referer获取从哪个页面跳转过来的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GMcPn5qJ-1598790831939)(assets/image-20200828095003973.png)]
if-modified-since缓存的页面才会有,缓存的时间,相差8小时
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q3pbcsp4-1598790831940)(assets/image-20200828095106961.png)]
user-agent获取客户端操作系统和浏览器的信息
image-20200828095146498
image-20200828095526511
image-20200828095721329
connection显示传输层TCP协议的连接状态:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-844bjkXw-1598790831942)(assets/image-20200828095845105.png)]
host访问主机的名字和端口号:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CFQDpuu1-1598790831943)(assets/image-20200828095907489.png)]
Accept-Language[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ijjty6Wh-1598790831946)(assets/image-20200828100016558.png)] 接受的语言:中文-中国

请求体

  1. GET方法没有请求体
  2. POST方法才能请求体,请求体中是浏览器发送给服务器的数据

小结

完整的HTTP请求由以下内容组成:

1552551974236

5. 请求的方法:与请求行有关的方法

目标

与请求行相关的方法

什么是HttpServletRequest对象

请求对象,可以在doGet或doPost方法中直接使用这个对象。

HttpServletRequest是一个接口,它的实现类是由Tomcat实现的,对象由Tomcat创建。

请求对象:org.apache.catalina.connector.RequestFacade@26559f7c

查看Tomcat的源码:
public class RequestFacade implements HttpServletRequest

如何修改Servlet的模板

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

如果没有重写doGet或doPost方法就会出现405的错误

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

案例

需求

创建一个Servlet,用于获取请求行中相关信息的方法,并且输出到网页上。

效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gQaWDYdF-1598790831951)(assets/image-20200828101707764.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;

/**
 * 与请求行有关的方法
 * POST /day27_01_request_war_exploded/demo1 HTTP/1.1
 * GET /day27_01_request_war_exploded/demo1?username=jack&password=123 HTTP/1.1
 */
@WebServlet("/demo1")
public class Demo1RequestLineServlet 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("获取请求的方式:" + request.getMethod() + "<br/>");
        //URI: Uniform Resource Identifer 统一资源标识符,用来标识服务器上唯一资源的名字
        out.print("获取请求的URI:" + request.getRequestURI() + "<br/>");
        //URL: Uniform Resource Locator 统一资源定位符,这是一个可以直接访问的资源
        out.print("获取请求的URL:" + request.getRequestURL() + "<br/>");
        out.print("获取协议和版本:" + request.getProtocol() + "<br/>");
        out.print("获取?后面的参数,称为查询字符串:" + request.getQueryString() + "<br/>");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

小结

HttpServletRequest对象的方法功能描述
String getMethod()获取请求的方式,GET或POST
String getRequestURI()获取URI (标识符)
StringBuffer getRequestURL()获取URL (定位符) 比较长
String getProtocol()获取协议和版本
String getQueryString()获取?后面的参数

6. 请求的方法:与请求头有关的方法

目标

request对象中与请求头相关的方法

方法示例

需求
  1. 得到某个请求头的值
  2. 得到所有的请求头信息,并输出所有的请求值信息
效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dMuAQHBr-1598790831953)(assets/image-20200828102515943.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;
import java.util.Enumeration;

/**
 * 与请求头有关的方法
 */
@WebServlet("/demo2")
public class Demo2HeaderServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        //通过请求头的键,获取请求头的值
        String host = request.getHeader("host");
        out.print("主机名和端口号:" + host + "<hr/>");
        //获取上一个页面的地址
        String referer = request.getHeader("referer");
        out.print("上一个页面的地址:" + referer + "<hr/>");
        //获取所有请求头的键(名字)
        Enumeration<String> headerNames = request.getHeaderNames();
        //遍历这个枚举类型
        while(headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();  //表示其中的一个名字
            String value = request.getHeader(name);  //通过名字获取值
            out.print("请求头名字:" + name + ",值:" + value + "<hr/>");
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

小结

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

7. 请求头应用案例:判断浏览器的类型

目标

判断客户端是什么类型的浏览器:Edg、OPR、Chrome、Safari、Firefox、IE浏览器或其它

效果

1552556601532

分析

  1. 得到user-agent请求头,请求头的名字不区分大小写。
  2. 判断它的值是否包含指定了字符串,如果包含则表示是指定的浏览器。
  3. 否则就输出其它的浏览器

代码

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("/demo3")
public class Demo3BrowserServlet 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("您的浏览器是:");
        //1.获取请求头:user-agent
        String agent = request.getHeader("user-agent");
        //2.判断它的值是否包含以下字符串:Edg、OPR、Chrome、Safari、Firefox、IE浏览器或其它
        //3.使用方法contains(),判断一个字符串是否包含另一个字符串
        //4.如果包含就输出相应的浏览器类型
        if (agent.contains("Edg")) {
            out.print("Microsoft Edge");
        }
        else if (agent.contains("OPR")) {
            out.print("Opera");
        }
        else if (agent.contains("Chrome")) {
            out.print("Google Chrome");
        }
        else if (agent.contains("Safari")) {
            out.print("Apple Safari");
        }
        else if (agent.contains("Firefox")) {
            out.print("Firefox");
        }
        else {
            out.print("IE浏览器或其它");
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

小结

案例的实现思路

  1. 获取user-agent请求头
  2. 判断它的值是否包含了指定的字符串
  3. 如果包含就输出浏览器的类型

8. 请求的方法:得到客户端提交的参数值【重点】

目标

使用request中的方法得到表单或地址栏上提交的参数值

获取参数的案例

需求
  1. 通过上面的方法得到注册页面的用户名和性别,输出到浏览器
  2. 得到所有的爱好,输出到浏览器
  3. 得到所有表单的名字和值,输出到浏览器
  4. 得到封装好的表单数据Map对象,输出到浏览器
效果
image-20200828112315511
代码
HTML
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>这是登录页面</title>
</head>
<body>
<h2>用户注册</h2>
<form action="demo4" method="get">
    用户名: <input type="text" name="name"><br/>
    性别: <input type="radio" name="gender" value="" checked="checked"/><input type="radio" name="gender" value=""/><br/>
    城市:
    <select name="city">
        <option value="广州">广州</option>
        <option value="深圳">深圳</option>
        <option value="珠海">珠海</option>
    </select>
    <br/>
    爱好:
    <input type="checkbox" name="hobby" value="上网"/>上网
    <input type="checkbox" name="hobby" value="上学"/>上学
    <input type="checkbox" name="hobby" value="上车"/>上车
    <input type="checkbox" name="hobby" value="上吊"/>上吊
    <br/>
    <input type="submit" value="注册"/>
</form>
</body>
</html>
Servlet代码
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;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;

/**
 * 获取页面提交的参数
 */
@WebServlet("/demo4")
public class Demo4RegisterServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        //得到注册页面的用户名和性别,输出到浏览器
        //1. 通过参数名获取参数值,方法的参数是:表单项的name或地址栏上提交的参数名
        String name = request.getParameter("name");
        String gender = request.getParameter("gender");
        out.print("名字:" + name + "<br/>");
        out.print("性别:" + gender + "<br/>");

        //2. 通过一个参数名,获取多个参数值,返回字符串数组
        //得到所有的爱好,输出到浏览器
        String[] hobbies = request.getParameterValues("hobby");
        out.print("爱好是:" + Arrays.toString(hobbies) + "<hr/>");

        //3. 得到所有表单的名字和值,输出到浏览器
        //获取所有表单项或参数的名字
        Enumeration<String> parameterNames = request.getParameterNames();
        //遍历每个元素
        while (parameterNames.hasMoreElements()) {
            String paramName = parameterNames.nextElement();  //获取其中一个参数名
            String value = request.getParameter(paramName);  //获取一个参数值
            out.print("参数名:" + paramName + ",值:" + value + "<hr/>");
        }

        out.print("封装成Map:<br/>");
        //4.一次获取整个表单所有的参数名和值,并且封装成一个Map<String, String[]>对象,其中键是参数名,值是参数值
        Map<String, String[]> parameterMap = request.getParameterMap();
        parameterMap.forEach((paramName,value) -> {
            out.print("参数名:" + paramName + ",值:" + Arrays.toString(value) + "<hr/>");
        });
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

小结

方法名描述
String getParameter(String name)通过参数名字,获取一个参数值
String[] getParameterValues(String name)通过参数名字,获取一组参数值,返回字符串数组。用于复选框
Enumeration<String> getParameterNames()获得所有的参数名
Map<String,String[]> getParameterMap()获取表单所有的参数名和值,封装成Map对象
其中键是参数名,值是参数值,是一个字符串的数组

扩展:设置默认打开的首页

每次开启项目,默认是打开index.jsp,index.html这个文件,我们可以设置自己的首页。

修改web.xml文件,设置欢迎页面

<!--设置欢迎页面列表-->
<welcome-file-list>
    <welcome-file>login.html</welcome-file>
    <welcome-file>register.html</welcome-file>
</welcome-file-list>

9. BeanUtils工具类的使用【重点】

目标

BeanUtils工具类中方法的使用

什么是BeanUtils

BeanUtils是Apache Commons组件的成员之一,主要用于简化JavaBean封装数据的操作。

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

BeanUtils是第三方组织写的工具类,要使用它,得先下载对应的工具jar包。

下载地址:http://commons.apache.org/

相关的jar包

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

BeanUtils常用方法

BeanUtils方法说明
public static void populate
(Object target, Map source)
作用:将map中的键和值复制到一个实体对象中
只会复制相同的属性,并且自动进行类型的转换。
参数1:目标对象,实体类
参数2:源对象,有数据的map

案例:BeanUtils的使用

需求
  1. 得到表单所有的数据封装成Map
  2. 使用BeanUtils封装成一个User对象,并且输出到浏览器。
步骤

jar包必须放在web/WEB-INF/lib这个目录下

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

代码
JavaBean
package com.itheima.entity;

import java.util.Arrays;

/**
 * 属性名与表单的名字相同
 * Map中键的名字与表单的名字一样
 */
public class User {

    private String name;
    private String gender;
    private String city;
    private String hobby[];

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", city='" + city + '\'' +
                ", hobby=" + Arrays.toString(hobby) +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String[] getHobby() {
        return hobby;
    }

    public void setHobby(String[] hobby) {
        this.hobby = hobby;
    }
}
页面地址
<form action="demo5" method="get">
Servlet
package com.itheima.servlet;

import com.itheima.entity.User;
import org.apache.commons.beanutils.BeanUtils;

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.lang.reflect.InvocationTargetException;
import java.util.Map;

@WebServlet("/demo5")
public class Demo5BeanServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();

       //1. 得到表单所有的数据封装成Map
        Map<String, String[]> parameterMap = request.getParameterMap();

        //创建一个实体类对象
        User user = new User();

        //2. 使用BeanUtils封装成一个User对象,并且输出到浏览器。
        try {
            BeanUtils.populate(user,parameterMap);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        out.print("封装后的对象:" + user);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

小结

BeanUtils常用方法说明
public static void populate(Object target, Map source)将一个Map封装成一个实体对象

补充:如何打开模块并执行

  1. 如图所示,选择从存在的源代码中导入模块

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

  1. 找到需要导入的文件或目录

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

  1. 刚导入的模块不能部署,点Run->Edit Configurations,点右边的Artifact,可能会找不到要部署的项目。

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

  1. 点File->Project Structure 选择Artifacts,点上面的加号。选择从模块中创建Web Application

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

  1. 选择新导入的模块

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

  1. 再次进入部署页面,可以选择Artifact
image-20191121143511400

10. 请求参数值汉字乱码的问题【重点】

目标

解决POST方法提交乱码的问题

请求参数值产生乱码的原因

​ Tomcat中请求默认的编码:iso-8859-1,这是欧洲码表,不支持汉字。我们需要将请求的编码改成utf-8,才会支持汉字。

1552557912477

POST方法乱码解决方法

  1. 方法:设置请求体的编码

    request.setCharacterEncoding("utf-8")
    
  2. 上面这条语句一定要放在所有获取参数的方法前面

  3. 必须与HTML的编码相同

    <meta charset="UTF-8">
    

Tomcat8.0中GET方法乱码的问题

以前Tomcat7 GET方法也是有乱码
8.0以后不用考虑汉字乱码的问题

小结

  1. POST乱码如何解决?setCharacterEncoding()
  2. GET乱码如何解决? 8.0以后没有乱码

11. 请求域有关的方法【重点】

目标

  1. 有哪三个作用域
  2. 操作作用域的方法

什么是作用域

image-20200828145151658

Servlet中的3个作用域

请求域的范围

请求域 < 会话域 < 上下文域

作用域的操作方法

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

案例:作用域的操作方法

需求
  1. Servlet创建一个键和值,在同一个Servlet中从请求域中取出信息,打印到浏览器上。
  2. 删除请求域中的键值对,再打印到浏览器上。
代码
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("/demo6")
public class Demo6AttributeServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();

        //向请求域中添加一个键和值
        request.setAttribute("user", "孙悟空");
        //从请求域中取出值
        String user = (String) request.getAttribute("user");
        //打印输出
        out.print("用户名:" + user + "<hr/>");

        //从请求域中删除键和值
        request.removeAttribute("user");

        //从请求域中取出值
        user = (String) request.getAttribute("user");
        //打印输出
        out.print("删除后用户名:" + user);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

小结

  1. 有哪三个作用域?

    1. 请求域:只在一次请求中起作用
    2. 会话域
    3. 上下文域
  2. 说说作用域方法的作用:

    request域有关的方法作用
    Object getAttribute(“键”)查询
    void setAttribute(“键”,Object 数据)添加或修改
    void removeAttribute(“键”)删除

12. 页面的跳转:转发

目标

  1. 转发的原理
  2. 转发的方法

疑问

  • 能否在OneServlet中保存值到请求域中,在另一个TwoServlet中打印出来?

    可以,但必须是同一次请求。使用转发可以在一次请求中访问多个Servlet,并且保证请求域中数据不丢失。
    

转发与重定向的作用

作用:就是进行页面的跳转

什么是转发

概念
image-20200828150842371

案例

需求

实现从OneServlet中转发到TwoServlet

步骤
  1. OneServlet向请求域中添加了一个键和值,转发给TwoServlet
  2. TwoServlet就从请求域中取出键和值,打印到浏览器上。
效果
image-20200828151400730
代码
OneServlet
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("/one")
public class Demo7OneServlet 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("向请求域中添加了键和值");
        //向请求域中添加键和值
        request.setAttribute("user", "老王");

        /*
        转发的步骤:
        1. 获取转发器,提供参数:要转发的地址
        2. 通过转发器进行跳转: forward(请求,响应)
         */
        request.getRequestDispatcher("two").forward(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

TwoServlet
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("/two")
public class Demo8TwoServlet 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("<h2>这是Two</h2>");
        //从请求域中获取数据
        String user = (String) request.getAttribute("user");
        out.print("请求域中数据:" + user);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

小结转发的特点

  1. 地址栏:不会发生变化,显示的是上一个Servlet的地址

  2. 请求次数:只有一次请求

  3. 请求域中数据:不会丢失

  4. 转发使用哪个方法?

    request.getRequestDispatcher("跳转的地址").forward(请求,响应)
    

13. 页面的跳转:重定向

目标

  1. 重定向原理
  2. 重定向的方法

什么是重定向

概念
image-20200828153351244
重定向方法
响应对象方法
response.sendRedirect("要跳转的地址")

重定向案例

需求

从OneServlet重定向到TwoServlet

步骤
  1. 在OneServlet中向请求域中添加键和值
  2. 使用重定向到TwoServlet,在TwoServlet中看能否取出请求域的值
效果

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

代码
OneServlet
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("/one")
public class Demo7OneServlet 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("向请求域中添加了键和值");
        //向请求域中添加键和值
        request.setAttribute("user", "老王");

        /*
        转发的步骤:
        1. 获取转发器,提供参数:要转发的地址
        2. 通过转发器进行跳转: forward(请求,响应)
        request.getRequestDispatcher("two").forward(request, response);
         */

        //使用重定向进行页面跳转
        response.sendRedirect("two");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

TwoServlet
代码与前面相同,没有变化

重定向的特点

  1. 地址栏发生了变化,显示的是跳转后的地址
  2. 请求次数是两次
  3. 请求域中的数据丢失了

疑问

问:什么时候使用转发,什么时候使用重定向?

1. 如果要保留请求域中数据,使用转发,否则使用重定向。
2. 经验:查询使用转发,增删改使用重定向。

小结:重定向和转发的区别

区别请求对象转发forward()响应对象重定向sendRedirect()
地址栏会不会发生变化不会
由哪一方进行跳转服务器端进行跳转浏览器端进行跳转
请求域中数据会不会丢失不会

14. 登录案例part1:项目搭建

目标

搭建项目的结构

需求

  1. 用户名和密码正确,将用户成功的信息保存在请求域中,转发到另一个页面,显示用户登录成功
  2. 用户名和密码错误,重定向到另一个html页面,显示登录失败。
  3. 使用表示层,业务层,数据访问层的三层结构实现

案例流程图

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

实现步骤

创建用户表
CREATE DATABASE day27;
USE day27;

create table `user`(
    id int primary key auto_increment,
    username varchar(20),
    password varchar(32)
 );
 
 insert into `user`(username, password) values ('Jack','123'),('张三','456');
 
 select * from `user`;
登录页面
<!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>
创建WEB-INF\lib文件夹,导入jar包

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

实体类User
package com.itheima.entity;

public class User {

    private int id;
    private String username;
    private String password;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + 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;
    }
}
sqlMapConfig.xml

模板

  1. 在new菜单下选择

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

  2. 创建一个模板

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

文件放在src目录下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
        <!--在控制台显示SQL语句-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <!--定义实体类别名-->
    <typeAliases>
        <package name="com.itheima.entity"/>
    </typeAliases>

    <environments default="default">
        <!--环境变量-->
        <environment id="default">
            <!--事务管理器-->
            <transactionManager type="JDBC"/>
            <!--数据源-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/day27"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <!--加载其它映射文件-->
    <mappers>
        <package name="com.itheima.dao"/>
    </mappers>
</configuration>
会话工具类

通过工具类获取一个会话对象

定义成模板

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

package com.itheima.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

/**
 * 会话工具类
 */
public class SqlSessionUtils {

    //会话工厂的创建类->会话工厂->会话
    private static SqlSessionFactory factory;

    static {
        //1.会话工厂的创建类
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //2.得到配置文件的输入流
        try (InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml")) {
            //3.创建会话工厂
            factory = builder.build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 得到会话对象
     * @return
     */
    public static SqlSession getSession() {
        return factory.openSession();
    }


}

15. 登录案例part2:数据访问层和业务层

目标

  1. 编写UserDao代码
  2. 编写UserService代码

步骤

UserDao

通过用户名和密码查询一个用户

UserService

业务类直接调用DAO类中相应的方法返回用户对象即可

代码

UserDao

新的知识点,如果访问mybatis的方法有2个以上的参数,必须使用@Param(“参数名”)

package com.itheima.dao;

import com.itheima.entity.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

/**
 * 用户的数据访问类
 */
public interface UserDao {

    /**
     * 通过用户名和密码查询1个用户
     * 如果2两个以上的参数:必须指定参数的名字,使用@Param("参数名")
     */
    @Select("SELECT * FROM `user` WHERE username=#{username} AND PASSWORD=#{password}")
    User findUser(@Param("username") String username, @Param("password") String password);
}

UserDaoImpl实现类
package com.itheima.dao.impl;

import com.itheima.dao.UserDao;
import com.itheima.entity.User;
import com.itheima.utils.SqlSessionUtils;
import org.apache.ibatis.session.SqlSession;

/**
 * DAO的实现类
 */
public class UserDaoImpl implements UserDao {
    @Override
    public User findUser(String username, String password) {
        //1.获取会话对象
        SqlSession session = SqlSessionUtils.getSession();
        //2.得到DAO的代理对象
        UserDao userDao = session.getMapper(UserDao.class);
        //3.调用接口中方法
        User user = userDao.findUser(username, password);
        //4.关闭会话
        session.close();
        //5.返回查询的结果
        return user;
    }
}
测试DAO
package com.itheima.test;

import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import com.itheima.entity.User;
import org.junit.Test;

//alter+enter自动导入junit
public class TestUserDao {

    @Test
    public void testUser() {
        //多态
        UserDao userDao = new UserDaoImpl();
        User user = userDao.findUser("Jack", "123");
        System.out.println(user);
    }
}

UserService接口
package com.itheima.service;

import com.itheima.entity.User;

/**
 * 业务类
 */
public interface UserService {

    /**
     * 登录的方法
     */
    User login(String username, String password);
}

UserServiceImpl实现类
package com.itheima.service.impl;

import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import com.itheima.entity.User;
import com.itheima.service.UserService;

/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {

    //调用DAO方法
    private UserDao userDao = new UserDaoImpl();

    @Override
    public User login(String username, String password) {
        return userDao.findUser(username, password);
    }
}

16. 登录案例part3:Servlet

目标

  1. 登录的LoginServlet
  2. 登录成功的SuccessServlet

LoginServlet

步骤
  1. 解决汉字乱码问题
  2. 得到用户提交的用户名和密码
  3. 调用业务层实现登录操作
  4. 如果用户名不为空表示登录成功
  5. 把当前的用户信息放在请求域中,以后放在会话域中
  6. 转发到success
  7. 如果登录失败重定向到failure.html页面
代码
package com.itheima.servlet;

import com.itheima.entity.User;
import com.itheima.service.UserService;
import com.itheima.service.impl.UserServiceImpl;

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("/login")
public class LoginServlet extends HttpServlet {
    //创建业务层
    private UserService userService = new UserServiceImpl();

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置汉字乱码的问题
        request.setCharacterEncoding("utf-8");

        //1. 获取用户名和密码
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        //2. 调用业务层实现登录
        User user = userService.login(username, password);

        //3. 如果登录成功跳转到成功页面
        if (user != null) {
            //把用户信息放在请求域中
            request.setAttribute("user", user);
            //转发到另一个页面,指定跳转的地址success
            request.getRequestDispatcher("success").forward(request, response);
        }
        //4. 如果登录失败跳转到失败页面
        else {
            response.sendRedirect("failure.html");
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

SuccessServlet

步骤
  1. 设置响应的类型和字符集
  2. 得到打印流对象
  3. 从请求域中取出用户信息
  4. 输出登录成功的信息
代码
package com.itheima.servlet;

import com.itheima.entity.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;
import java.io.PrintWriter;

/**
 * 显示登录成功
 */
@WebServlet("/success")
public class SuccessServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        //1.从请求域中获取用户的信息
        User user = (User) request.getAttribute("user");
        //2.打印到浏览器上
        out.print("<h1>欢迎您!" + user.getUsername() + "</h1>");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
整个项目结构

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

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">

    <welcome-file-list>
        <welcome-file>login.html</welcome-file>
    </welcome-file-list>
</web-app>

17. 服务器端代码的调试

  1. 查看错误信息

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

  2. 设置服务器上Java代码的断点

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

  3. 在调试模式下运行

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

学习总结

  1. 能够理解HTTP协议请求内容
    1. 请求行
    2. 请求头
    3. 请求体:POST才有请求体
1552564604092
  1. 能够使用Request对象获取HTTP协议请求内容
请求方法功能描述
String getHeader(String headName)通过请求头的名字获取值
String getMethod()获取请求的方法GET或POST
String getRequestURI()获取请求的URI
方法名描述
String getParameter(String name)通过参数名获取参数值
String[] getParameterValues(String name)通过参数名获取一组参数值
Map<String,String[]> getParameterMap()获取所有的参数名和值,封装成Map对象
  1. 能够处理HTTP请求参数的乱码问题

GET: 没有乱码

POST:

setCharacterEncoding("utf-8")
  1. 能够使用Request域对象
request与域有关的方法作用
Object getAttribute(“键”)通过键获取值
void setAttribute(“键”,Object 数据)设置键和值
void removeAttribute(“键”)删除键和值
  1. 能够使用Request对象做请求转发

转发的方法:

request.getRequestDispatcher("地址").forward(request, response)

重定向的方法:

response.sendRedirect("地址")
  1. 能够完成登录案例
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值