http协议&请求对象【转发&重定向&请求参数乱码问题】&BeanUtils使用&软件开发三层架构项目结构
回顾
有时要清除浏览器的缓存,才能看到最新的结果,Chrome清除缓存的快捷键:ctrl+shift+del
-
软件架构分成哪两种,分别是什么含义
BS:浏览器服务器模式
CS: 客户端服务器模式
-
Tomcat目录结构
目录名 | 作用 |
---|---|
bin | 可执行文件,启动:startup.bat 关闭:shutdown.bat |
conf | 配置文件,核心配置文件:server.xml |
lib | 第三方的jar包 |
logs | 日志记录信息 |
temp | 临时目录 |
webapps | 项目发布目录 ROOT目录:启动tomcat以后默认的首页 在idea中开发的时候,没有这个首页 |
work | 工作目录,生成一些中间文件 |
-
tomcat部署的三种方式
直接复制到webapps
配置server.xml文件,添加Context path="/虚拟路径" docBase="真实地址" 不能有汉字,而且这个地址必须要存在
独立的xml文件 conf/catalina/localhost/文件名.xml 文件名就是访问地址 内容:Context docBase="真实地址" idea就是这种方式
-
Servlet与普通的Java程序的区别
- 实现什么接口:javax.servlet.Servlet接口,我们是继承于HttpServlet
- 运行在Tomcat的容器中
- service中的方法参数是:HttpServletRequest请求对象,HttpServletResponse响应对象
-
说出注解的作用
@WebServlet注解 | 作用 |
---|---|
name | servlet的名字,不能出现相同的名字 |
urlPatterns | 访问地址,可以指定多个 |
value | 同上 |
- Servlet的生命周期方法
方法 | 作用 | 运行次数 |
---|---|---|
void init(ServletConfig config) | 用户第一次访问的时候 | 执行1次 |
void service(ServletRequest req, ServletResponse res) | 每次请求都会执行 | 执行多次 |
void destroy() | 服务器关闭,销毁的时候 | 执行1次 |
学习目标
- HTTP请求协议的格式
- 能够理解HTTP协议请求内容
- 能够使用Request对象获取HTTP协议请求内容
- 请求对象的使用
- 能够处理HTTP请求参数的乱码问题
- 能够使用Request域对象
- 能够使用Request对象做请求转发
- 能够完成登录案例
学习内容
1. HTTP协议的概念
目标
- 什么是HTTP
- 它有什么特点
概念
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s3gnzGNV-1598790831863)(assets/1555154341398.png)]
HTTP: Hyper Text Transfer Protocol 超文本传输协议 (传输超文本的协议) (公交车),HTTP就是用来传输HTML的
HTML: Hyper Text Markup Language 超文本标记语言 (乘客)
默认端口号:80端口,不是8080,8080是Tomcat的。浏览器如果使用80端口,可以省略。
HTTP协议的特点:
- 格式比较简单,数据传输比较快
- 服务器如果接收到多次浏览器发送的请求,通过HTTP协议是不能判断这是一个用户的多次请求,还是多个用户的请求。因为HTTP协议是一个无状态的协议,不会记录用户的访问情况。
- 每次用户发送请求以后,得到了服务器的响应,连接就断开了。
- 在HTTP1.1的版本中,一次请求可以请求多个资源。1.0每次请求只会请求一个资源。
HTTPS
HTTPS:功能与HTTP是一样的,也是用来传输网页的
- 这是一个加密的传输协议
- 可以识别客户端与服务器端,防止假冒的第三方网站。
端口号是:443
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zvWzPgIe-1598790831882)(/assets/1552550764411.png)]
小结
- HTTP的作用是?用来传输HTML
- 默认的端口号是:80
- 特点:无状态的协议
2. HTTP请求的三个组成
目标
请求有哪三个组成部分
什么是请求
浏览器发送给服务器的数据
查看HTTP请求
-
在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>
-
查看浏览器与服务器的通讯,按F12打开窗口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Du23bcQR-1598790831888)(assets/image-20200828091447858.png)]
请求的组成
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LmJyHmfn-1598790831896)(assets/image-20200828091752958.png)]
小结
请求由哪三个组成部分?
- 请求行:只有一行数据
- 请求头:由多个键值对组成,键和值之间是冒号
- 请求体:浏览器发送给服务器的数据
3. 请求信息的组成:请求行
目标
- 请求行的格式
- 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
三个部分:
-
GET或POST,请求的方法
-
URI:Uniform Resource Identifer 统一资源标识符,用来标识服务器上唯一资源的名字
-
协议和版本
POST与GET的区别
POST方式 | GET方式 | |
---|---|---|
地址栏 | 没有参数 参数是在请求体中发送的 | GET后面有参数的值,地址与参数之间使用?分隔 如果有多个参数使用&分隔。数据是在请求行中发送的 |
大小 | 发送的数据没有大小限制 | 受浏览器的限制,最大是1K |
安全性 | 相对更加安全 | 参数是可以在地址栏上看到 |
缓存 | 不会使用缓存 | 浏览器会使用缓存 状态码: 200 表示从服务器上正确的获取数据 304 表示使用缓存 |
注:缓存只对静态资源起作用,动态资源是不起作用的。
浏览器上设置,让静态资源不使用缓存
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-txMD8wSQ-1598790831937)(assets/image-20200828094337482.png)]
小结
- 请求行由哪三个组成部分?
- 请求方式
- URI
- 协议和版本
- GET方法和POST方法传递数据有什么区别?
- 哪个使用缓存?GET
- 哪个在请求行中发送数据?GET
- POST在请求体中发送
- 哪个数据大小没有限制?POST
4. 请求信息的组成:请求头、请求体
目标
了解常见请求头的作用
常用请求头
格式:由键和值组成
请求头 | 描述 |
---|---|
referer | 获取从哪个页面跳转过来的 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GMcPn5qJ-1598790831939)(assets/image-20200828095003973.png)] |
if-modified-since | 缓存的页面才会有,缓存的时间,相差8小时 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q3pbcsp4-1598790831940)(assets/image-20200828095106961.png)] |
user-agent | 获取客户端操作系统和浏览器的信息 |
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)] 接受的语言:中文-中国 |
请求体
- GET方法没有请求体
- POST方法才能请求体,请求体中是浏览器发送给服务器的数据
小结
完整的HTTP请求由以下内容组成:
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对象中与请求头相关的方法
方法示例
需求
- 得到某个请求头的值
- 得到所有的请求头信息,并输出所有的请求值信息
效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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浏览器或其它
效果
分析
- 得到user-agent请求头,请求头的名字不区分大小写。
- 判断它的值是否包含指定了字符串,如果包含则表示是指定的浏览器。
- 否则就输出其它的浏览器
代码
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);
}
}
小结
案例的实现思路
- 获取user-agent请求头
- 判断它的值是否包含了指定的字符串
- 如果包含就输出浏览器的类型
8. 请求的方法:得到客户端提交的参数值【重点】
目标
使用request中的方法得到表单或地址栏上提交的参数值
获取参数的案例
需求
- 通过上面的方法得到注册页面的用户名和性别,输出到浏览器
- 得到所有的爱好,输出到浏览器
- 得到所有表单的名字和值,输出到浏览器
- 得到封装好的表单数据Map对象,输出到浏览器
效果
代码
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的使用
需求
- 得到表单所有的数据封装成Map
- 使用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封装成一个实体对象 |
补充:如何打开模块并执行
- 如图所示,选择从存在的源代码中导入模块
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r0EAQzu0-1598790831964)(assets/image-20191121143408593-1598588486139.png)]
- 找到需要导入的文件或目录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0tQs96ZA-1598790831965)(assets/image-20191121143420608-1598588486139.png)]
- 刚导入的模块不能部署,点Run->Edit Configurations,点右边的Artifact,可能会找不到要部署的项目。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DSeMjBgH-1598790831968)(assets/image-20191121143434732-1598588486139.png)]
- 点File->Project Structure 选择Artifacts,点上面的加号。选择从模块中创建Web Application
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3cstpdmu-1598790831970)(assets/image-20191121143453713-1598588486139.png)]
- 选择新导入的模块
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ehgZlqUU-1598790831972)(assets/image-20191121143503069-1598588486140.png)]
- 再次进入部署页面,可以选择Artifact
10. 请求参数值汉字乱码的问题【重点】
目标
解决POST方法提交乱码的问题
请求参数值产生乱码的原因
Tomcat中请求默认的编码:iso-8859-1,这是欧洲码表,不支持汉字。我们需要将请求的编码改成utf-8,才会支持汉字。
POST方法乱码解决方法
-
方法:设置请求体的编码
request.setCharacterEncoding("utf-8")
-
上面这条语句一定要放在所有获取参数的方法前面
-
必须与HTML的编码相同
<meta charset="UTF-8">
Tomcat8.0中GET方法乱码的问题
以前Tomcat7 GET方法也是有乱码
8.0以后不用考虑汉字乱码的问题
小结
- POST乱码如何解决?setCharacterEncoding()
- GET乱码如何解决? 8.0以后没有乱码
11. 请求域有关的方法【重点】
目标
- 有哪三个作用域
- 操作作用域的方法
什么是作用域
Servlet中的3个作用域
请求域的范围
请求域 < 会话域 < 上下文域
作用域的操作方法
request与域有关的方法 | 作用 |
---|---|
Object getAttribute(“键”) | 通过键从请求域中获取值 |
void setAttribute(“键”,Object 值) | 向请求域中添加键和值,如果键存在就是修改 |
void removeAttribute(“键”) | 通过键删除键和值 |
案例:作用域的操作方法
需求
- Servlet创建一个键和值,在同一个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;
@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);
}
}
小结
-
有哪三个作用域?
- 请求域:只在一次请求中起作用
- 会话域
- 上下文域
-
说说作用域方法的作用:
request域有关的方法 作用 Object getAttribute(“键”) 查询 void setAttribute(“键”,Object 数据) 添加或修改 void removeAttribute(“键”) 删除
12. 页面的跳转:转发
目标
- 转发的原理
- 转发的方法
疑问
-
能否在OneServlet中保存值到请求域中,在另一个TwoServlet中打印出来?
可以,但必须是同一次请求。使用转发可以在一次请求中访问多个Servlet,并且保证请求域中数据不丢失。
转发与重定向的作用
作用:就是进行页面的跳转
什么是转发
概念
案例
需求
实现从OneServlet中转发到TwoServlet
步骤
- OneServlet向请求域中添加了一个键和值,转发给TwoServlet
- TwoServlet就从请求域中取出键和值,打印到浏览器上。
效果
代码
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);
}
}
小结转发的特点
-
地址栏:不会发生变化,显示的是上一个Servlet的地址
-
请求次数:只有一次请求
-
请求域中数据:不会丢失
-
转发使用哪个方法?
request.getRequestDispatcher("跳转的地址").forward(请求,响应)
13. 页面的跳转:重定向
目标
- 重定向原理
- 重定向的方法
什么是重定向
概念
重定向方法
响应对象方法
response.sendRedirect("要跳转的地址")
重定向案例
需求
从OneServlet重定向到TwoServlet
步骤
- 在OneServlet中向请求域中添加键和值
- 使用重定向到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. 经验:查询使用转发,增删改使用重定向。
小结:重定向和转发的区别
区别 | 请求对象转发forward() | 响应对象重定向sendRedirect() |
---|---|---|
地址栏会不会发生变化 | 不会 | 会 |
由哪一方进行跳转 | 服务器端进行跳转 | 浏览器端进行跳转 |
请求域中数据会不会丢失 | 不会 | 会 |
14. 登录案例part1:项目搭建
目标
搭建项目的结构
需求
- 用户名和密码正确,将用户成功的信息保存在请求域中,转发到另一个页面,显示用户登录成功
- 用户名和密码错误,重定向到另一个html页面,显示登录失败。
- 使用表示层,业务层,数据访问层的三层结构实现
案例流程图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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
模板
-
在new菜单下选择
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Fhs2G6q-1598790831979)(assets/image-20200828160111042.png)]
-
创建一个模板
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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:数据访问层和业务层
目标
- 编写UserDao代码
- 编写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
目标
- 登录的LoginServlet
- 登录成功的SuccessServlet
LoginServlet
步骤
- 解决汉字乱码问题
- 得到用户提交的用户名和密码
- 调用业务层实现登录操作
- 如果用户名不为空表示登录成功
- 把当前的用户信息放在请求域中,以后放在会话域中
- 转发到success
- 如果登录失败重定向到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
步骤
- 设置响应的类型和字符集
- 得到打印流对象
- 从请求域中取出用户信息
- 输出登录成功的信息
代码
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. 服务器端代码的调试
-
查看错误信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l52PhPIQ-1598790831986)(assets/image-20200828165226580.png)]
-
设置服务器上Java代码的断点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5jTC6aSb-1598790831988)(assets/image-20200828165337510.png)]
-
在调试模式下运行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1JXQEup7-1598790831989)(assets/image-20200828165415377.png)]
学习总结
- 能够理解HTTP协议请求内容
- 请求行
- 请求头
- 请求体:POST才有请求体
- 能够使用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对象 |
- 能够处理HTTP请求参数的乱码问题
GET: 没有乱码
POST:
setCharacterEncoding("utf-8")
- 能够使用Request域对象
request与域有关的方法 | 作用 |
---|---|
Object getAttribute(“键”) | 通过键获取值 |
void setAttribute(“键”,Object 数据) | 设置键和值 |
void removeAttribute(“键”) | 删除键和值 |
- 能够使用Request对象做请求转发
转发的方法:
request.getRequestDispatcher("地址").forward(request, response)
重定向的方法:
response.sendRedirect("地址")
- 能够完成登录案例