JavaWeb - Servlet详解、HTTP协议、Request
1.Servlet的体系结构
Servlet - - 接口
|
GenericServlet - - 抽象类
|
HttpServlet - - 抽象类
* GenericServlet:将Servlet接口中其他的方法做了默认空实现,只将service()方法做了抽象
*将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可
* HttpServlet:对http协议的一种封装,简化操作
1. 定义类继承HttpServlet
2. 复写doGet/doPost方法
2.Servlet相关配置
1.urlpattern:Servlet访问路径
1. 一个Servlet可以定义多个访问路径:@WebServlet({"/demo1","/d1","ddd1"})
2. 路径定义规则:
1. /xxx
2. /xxx/xxx:多层路径
3. *.do
3.HTTP
* 概念:Hyper Text Transfer Protocol 超文本传输协议
* 传输协议:定义了客户端和服务器端通信时,发送数据的格式
* 特点:
1. 基于TCP/IP的高级协议
2. 默认端口:80
3. 基于请求响应模型的:一次请求对应一次响应
4. 无状态的:每次请求之间相互独立,不能交互数据
* 历史版本:
* 1.0:每一次请求响应都会建立新的连接
* 1.1:复用连接
* 请求消息数据格式
1.请求行
请求方式 请求url 请求协议/版本
GET /login.html HTTP/1.1
* 请求方式:
* HTTP协议有7中请求方式,常用的有2种
* GET:
1. 请求参数在请求行中,在url后
2. 请求的url长度是有限制的
3. 不太安全
* POST
1. 请求参数在请求体中
2. 请求的url长度没有限制
3. 相对安全
2.请求头:客户端浏览器告诉服务器一些信息
请求头名称:请求头值
* 常见的请求头:
1. User - Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息
* 可以在服务器端获取该头的信息,解决浏览器兼容性问题
2. Referer:告诉服务器,我(当前请求)从哪里来?
*作用:
1. 防盗链
2. 统计工作
3.请求空行
空行,用与分割POST请求的请求头和请求体的
4.请求体(正文):封装POST请求消息的请求参数的 (GET请求没有)
* 字符串格式:
* 响应消息数据格式
4.Request
1. request对象和response对象的原理
1. request和response是由服务器创建的。我们来使用它们
2. request对象是来获取请求消息,response对象是来设置响应消息
2. request对象继承体系结构:
ServletRequest - - 接口
| 继承
HttpServletRequest - - 接口
| 实现
org.apache.catalina.connector.RequestFacade 类(tomcat)
3. request功能:
1. 获取请求消息数据
1. 获取请求行数据
* GET /Web_servlet/demo02?name=zhangsan HTTP/1.1
* 方法:
1. 获取请求方式:GET
* String getMethod()
2. (*)获取虚拟目录:/Web_servlet
* String getContextPath()
3. 获取Servlet路径:/demo02
* String getServletPath()
4. 获取get方式的请求参数:name=zhangsan
* String getQueryString()
5. (*)获取请求的URI:/Web_servlet/demo02
* String getRequestURI():/Web_servlet/demo02
* StringBuffer getRequestURL(): http://localhost/Web_servlet/demo02
* URI:统一资源标识符 : /Web_servlet/demo02 如:共和国
* URL:统一资源定位符 :http://localhost/Web_servlet/demo02 如:中华人民共和国
6. 获取协议及版本:HTTP/1.1
* String getProtocol()
7. 获取客户机的IP地址:
* String getRemoteAddr()
2. 获取请求头数据
* String getHeader(String name):通过请求头的名称来获取请求头的值
String referer = request.getHeader("referer"); if(referer!=null){ if(referer.contains("youku")){ response.setContentType("text/html;charset=utf-8"); response.getWriter().write("正在播放优酷高清电影"); }else{ response.setContentType("text/html;charset=utf-8"); response.getWriter().write("想看高清电影来优酷吧"); } }
* Enumeration<String> getHeaderNames(String name):获取所有的请求头名称
3. 获取请求体数据
* 请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数
* 步骤:
1. 获取流对象
* BufferedReader getReader():获取字符输入流,只能操作字符数据
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { BufferedReader br = request.getReader(); String line; while ((line = br.readLine())!=null){ System.out.println(line); } }
* ServletInputStream getInputStream():获取字节输入流,可以操作所有类型的数据
*在文件上传知识点中讲解
2. 再从流对象中拿数据
2. 其他功能
1. 获取请求参数通用方式:不论get还是post请求方式都可以使用下列方法来获取请求参数
1. String getParameter(String name):根据参数名称来获取参数值 username=zhangsan&password=123
2. String【】 getParameterValues(String name):根据参数名称获取参数值的数组 hobby=study&hobby=game 多用于复选框
3. Enumeration<String> getParameterNames():获取所有请求的参数名称
4. Map<String,Sring[ ]> getParameterMap():获取所有参数的map集合
package web.request; 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; import java.util.Map; import java.util.Set; @WebServlet("/RequestDemo02") public class RequestDemo02 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //post获取请求参数 //根据请求参数的名称获取值 String username_value = request.getParameter("username"); System.out.println("post方法....."); System.out.println(username_value); //获取所有参数的名称 Enumeration<String> parameterNames = request.getParameterNames(); while (parameterNames.hasMoreElements()){ String name = parameterNames.nextElement(); System.out.println(name); String name_value = request.getParameter(name); System.out.println(name_value); System.out.println("--------"); } //获取所有参数的map集合 Map<String, String[]> parameterMap = request.getParameterMap(); Set<String> keySet = parameterMap.keySet(); for (String name : keySet) { System.out.println(name); //根据key键获取值 String[] values = parameterMap.get(name); for (String value : values) { System.out.println(value); } System.out.println("========================="); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //get获取请求参数 this.doPost(request,response); } }
* 中文乱码问题:
* get方式:tomcat 8 已经将get方式乱码问题解决了
* post方式:会乱码
解决:在获取参数前,设置请求参数的编码 request.setCharacterEncoding("utf-8");
2. 请求转发:一种在服务器内部的资源跳转方式
1. 步骤
1. 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
2. 使用RequestDispatcher对象来进行转发: forward(ServletRequest request,ServletRespnse response)
2.特点:(面试题)
1. 浏览器地址栏路径没有发生变化
2. 只能转发到当前的服务器内部资源中
3. 转发是一次请求,即使跳转多个资源,使用的是同一次请求
3. 共享数据
* 域对象:一个有作用范围的对象,可以在范围内共享数据
* request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
* 方法:
1. void setAttribute(String name,Object obj):存储数据
2. Object getAttribute(String name):通过键获取值
3. void removeAttribute(String name):通过键移除键值对
4. 获取ServletContext
* ServletContext getServletContext()
##案例:用户登录
需求:
1.编写login.html登录页面 username & password 两个输入框
2.使用Druid数据库连接池技术,操作mysql,day14数据库中user表
3.使用JdbcTemplate技术封装JDBC
4.登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您
5.登录失败跳转到FailServlet展示:登录失败,用户名或密码错误
* 开发步骤
1. 创建项目,写好login.html页面,导入配置文件,jar包
2. 创建数据库环境
CREATE DATABASE case_login; USE case_login; CREATE TABLE USER( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) UNIQUE NOT NULL, PASSWORD VARCHAR(32) NOT NULL );
3. 在src目录下创建domain包,创建User类(实体类)
package domain; /* * 用户的实体类 * */ 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 + '\'' + '}'; } }
4. 在src目录下创建dao包(用于操作数据库),创建UserDao类,提供login的方法
5. 编写checkServlet类
6. login.html中form表单的action的写法: 虚拟目录+资源路径
7. BeanUtils工具类,简化数据封装
* 用于封装JavaBean
1. JavaBean:标准的Java类
1.要求:
1. 类必须被public修饰
2. 必须提供空参的构造器
3. 成员变量必须使用private修饰
4. 提供公共setter和getter方法
2.功能:封装数据
2. 概念:
成员变量:
属性:setter和getter方法截取后的产物,既其方法名
例如:getUsername() - - > Username - - > username
3. 方法:
1. setProperty():操作的是其属性,既方法setHehe中的Hehe,Hehe可以指向成员变量
2. getProperty()
3. populate(Object,map):将map集合的键值对信息,封装到对应的JavaBean对象中
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>login</title> </head> <body> <form action="/case_login/checkServlet" method="post"> <input type="text" name="username" placeholder="请输入用户名"> <input type="password" name="password" placeholder="请输入密码"> <input type="submit" value="登录"> </form> </body> </html>
CREATE DATABASE case_login; USE case_login; CREATE TABLE USER( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) UNIQUE NOT NULL, PASSWORD VARCHAR(32) NOT NULL );
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/case_login username=root password=woaini1314 initialSize=5 maxActive=10 maxWait=3000
package domain; /* * 用户的实体类 * */ 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 + '\'' + '}'; } }
package util; /* *JDBC工具类 使用Druid连接池 * */ import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; 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对象 * */ public static Connection getConnection() throws SQLException { return ds.getConnection(); } }
package dao; import domain.User; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import util.JDBCUtils; /* * 操作数据库中user表的类 * */ public class UserDao { //声明JDBCTemplate对象共用 private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource()); /** * 登录方法 * @param loginUser 只有用户名和密码 * @return user 包含用户全部数据,没有查询到则返回null */ public User login(User loginUser){ try { //loginUser是网页上登录传过来的user对象,返回的user对象是数据库中含有全部信息的user对象 //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; } catch (DataAccessException e) { e.printStackTrace(); return null; } } }
package test; import dao.UserDao; import domain.User; import org.junit.Test; public class UserDaoTest { @Test public void testLogin(){ User loginuser = new User(); loginuser.setUsername("chris"); loginuser.setPassword("woaini1314"); UserDao dao = new UserDao(); User user = dao.login(loginuser); System.out.println(user); } }
package web.servlet; import dao.UserDao; import domain.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.lang.reflect.InvocationTargetException; import java.util.Map; @WebServlet("/checkServlet") public class checkServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.设置编码 request.setCharacterEncoding("utf-8"); //2.获取请求参数 //String username = request.getParameter("username"); //String password = request.getParameter("password"); //3.封装user对象 //User login_user = new User(); //login_user.setUsername(username); //login_user.setPassword(password); // Bean高级方法 //2.获取所有请求参数 Map<String, String[]> map = request.getParameterMap(); //3.1创建User对象 User login_user = new User(); //3.2使用apache包下的BeanUtils封装 try { BeanUtils.populate(login_user,map); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } //4.调用UserDao的login方法 UserDao dao = new UserDao(); User user = dao.login(login_user);//此处的user是从数据库返回的user //5.判断user if(user==null){ //登录失败 request.getRequestDispatcher("/failServlet").forward(request,response); }else { //登录成功 //存储数据 request.setAttribute("user",user); // /转发 request.getRequestDispatcher("/successServlet").forward(request,response); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request,response); } }
package web.servlet; import domain.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 { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取request域中共享的user对象 User user = (User) request.getAttribute("user"); //给页面写一句话 if(user!=null){ //设置编码 response.setContentType("text/html;charset=utf-8"); //输出 response.getWriter().write("登录成功!"+user.getUsername()+"欢迎您"); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request,response); } }
package 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 failServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //给页面写一句话 //设置编码 response.setContentType("text/html;charset=utf-8"); //输出 response.getWriter().write("用户名密码错误,登录失败"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request,response); } }