1、Request和Response的概述
- Request是请求对象,Response是响应对象
- request:获取请求数据
- 浏览器会发送HTTP请求到后台服务器[Tomcat]
- HTTP的请求中会包含很多请求数据[请求行+请求头+请求体]
- 后台服务器[Tomcat]会对HTTP请求中的数据进行解析并把解析结果存入到一个对象中
- 所存入的对象即为request对象,所以我们可以从request对象中获取请求的相关参数
- 获取到数据后就可以继续后续的业务,比如获取用户名和密码就可以实现登录操作的相关业务
- response:设置响应数据
- 业务处理完后,后台就需要给前端返回业务处理的结果即响应数据
- 把响应数据封装到response对象中
- 后台服务器[Tomcat]会解析response对象,按照[响应行+响应头+响应体]格式拼接结果
- 浏览器最终解析结果,把内容展示在浏览器给用户浏览
2、Request对象
-
Request继承体系
RequestFacade
类实现了HttpServletRequest接口,也间接实现了ServletRequest接口。- Servlet类中的service方法、doGet方法或者是doPost方法最终都是由Web服务器[Tomcat]来调用的,所以Tomcat提供了方法参数接口的具体实现类,并完成了对象的创建
- 要想了解RequestFacade中都提供了哪些方法,我们可以直接查看JavaEE的API文档中关于ServletRequest和HttpServletRequest的接口文档,因为RequestFacade实现了其接口就需要重写接口中的方法
-
Request获取请求数据
-
获取请求行数据
请求行包含三块内容,分别是请求方式
、请求资源路径
、HTTP协议及版本
// (1) String getMethod():获取请求方式: GET String method = req.getMethod(); System.out.println(method);//GET // (2)String getContextPath():获取虚拟目录(项目访问路径):/request-demo String contextPath = req.getContextPath(); System.out.println(contextPath); // (3)StringBuffer getRequestURL(): 获取URL(统一资源定位符):http://localhost:8080/request-demo/req1 StringBuffer url = req.getRequestURL(); System.out.println(url.toString()); // (4)String getRequestURI():获取URI(统一资源标识符): /request-demo/req1 String uri = req.getRequestURI(); System.out.println(uri); // (5)String getQueryString():获取请求参数(GET方式): username=zhangsan String queryString = req.getQueryString(); System.out.println(queryString);
启动服务器,访问:
http://localhost:8080/request-demo/req1?username=zhangsan&passwrod=123
获取的结果如下:
-
获取请求头数据
请求头数据格式key: value
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取请求头: user-agent: 浏览器的版本信息 String agent = req.getHeader("user-agent"); System.out.println(agent); }
-
获取请求体数据
- 浏览器在发送GET请求的时候是没有请求体的,所以需要把请求方式变更为POST
- Request对象提供了两种方式来获取请求体数据
// (1)获取字节输入流 //如果前端发送的是字节数据,比如传递的是文件数据,则使用该方法 ServletInputStream getInputStream() // (2)获取字符输入流 //如果前端发送的是纯文本数据,则使用该方法 BufferedReader getReader() //会自动关闭,不需要手动关闭流
-
获取请求参数的通用方式
通用指POST和GET都适用// (1)获取所有参数Map集合 Map<String,String[]> getParameterMap() // (2)根据名称获取参数值(数组) String[] getParameterValues(String name) // (3)根据名称获取参数值(单个值) String getParameter(String name)
@WebServlet("/req2") public class RequestDemo2 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1. 获取所有参数的Map集合 Map<String, String[]> map = req.getParameterMap(); for (String key : map.keySet()) { // username:zhangsan lisi System.out.print(key+":"); //获取值 String[] values = map.get(key); for (String value : values) { System.out.print(value + " "); } System.out.println(); System.out.println("------------"); String[] hobbies = req.getParameterValues("hobby"); for (String hobby : hobbies) { System.out.println(hobby); } String username = req.getParameter("username"); String password = req.getParameter("password"); System.out.println(username); System.out.println(password); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req,resp);//代码一样不用再重复写 } }
-
-
IDEA快速创建Servlet
- 按照自己的需求,修改Servlet创建的模板内容
- 使用servlet模板创建Servlet类
- 按照自己的需求,修改Servlet创建的模板内容
-
请求参数中文乱码问题
(1)Tomcat8.0之后,已将GET请求乱码问题解决,设置默认的解码方式为UTF-8
(2)POST请求解决方案是:设置输入流的编码request.setCharacterEncoding("UTF-8");//(注意:设置的字符集要和页面保持一致)
(3)通用方式(GET/POST):需要先解码,再编码
new String(username.getBytes("ISO-8859-1"),"UTF-8");
-
Request请求转发
-
请求转发(forward):一种在服务器内部的资源跳转方式
-
请求转发的实现方式:
req.getRequestDispatcher("资源B路径").forward(req,resp);
-
request对象提供的三个方法:
// (1)存储数据到request域[范围,数据是存储在request对象]中 void setAttribute(String name,Object o); // (2)根据key获取值 Object getAttribute(String name); // (3)根据key删除该键值对 void removeAttribute(String name);
-
请求转发的特点
- 浏览器地址栏路径不发生变化
- 只能转发到当前服务器的内部资源
- 一次请求,可以在转发资源间使用request共享数据
@WebServlet("/req5") public class RequestDemo5 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("demo5..."); //存储数据 request.setAttribute("msg","hello"); //请求转发 request.getRequestDispatcher("/req6").forward(request,response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } } /** * 请求转发 */ @WebServlet("/req6") public class RequestDemo6 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("demo6..."); //获取数据 Object msg = request.getAttribute("msg"); System.out.println(msg); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
访问
http://localhost:8080/request-demo/req5
,就可以在控制台看到如下内容:
-
3、Response对象
-
Response继承体系
-
Response设置响应数据功能介绍
HTTP响应数据总共分为三部分内容,分别是响应行、响应头、响应体- 响应行
常用设置响应状态码:void setStatus(int sc);
- 响应头
设置响应头键值对:void setHeader(String name,String value);
- 响应体
- 获取字符输出流:
PrintWriter getWriter();
- 获取字节输出流:
ServletOutputStream getOutputStream();
- 获取字符输出流:
- 响应行
-
Respones请求重定向
-
Response重定向(redirect):一种资源跳转方式
(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求
(2)资源A现在无法处理该请求,就会给浏览器响应一个302的状态码+location的一个访问资源B的路径
(3)浏览器接收到响应状态码为302就会重新发送请求到location对应的访问地址去访问资源B
(4)资源B接收到请求后进行处理并最终给浏览器响应结果,这整个过程就是重定向 -
重定向的实现方式:
resp.setStatus(302); resp.setHeader("location","资源B的访问路径"); resposne.sendRedirect("资源B的访问路径")//简化方法
-
重定向的特点
- 浏览器地址栏路径发送变化
- 可以重定向到任何位置的资源(服务内容、外部均可)
- 两次请求,不能在多个资源使用request共享数据
-
-
路径问题
- 浏览器使用:需要加虚拟目录(项目访问路径)
- 服务端使用:不需要加虚拟目录
//简化方式完成重定向 //动态获取虚拟目录 String contextPath = request.getContextPath(); response.sendRedirect(contextPath+"/resp2");
-
Response响应字符数据
- 步骤
- 通过Response对象获取字符输出流: PrintWriter writer = resp.getWriter();
- 通过字符输出流写数据: writer.write(“aaa”);
@WebServlet("/resp3") public class ResponseDemo3 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置响应的数据格式及数据的编码 response.setContentType("text/html;charset=utf-8"); //(1)通过Response对象获取字符输出流 PrintWriter writer = response.getWriter(); //(2)通过字符输出流写数据 writer.write("aaa"); //告诉浏览器返回的数据类型是HTML类型数据,浏览器才会解析HTML标签 response.setHeader("content-type","text/html"); writer.write("<h1>aaa</h1>"); writer.write("你好"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
- 步骤
-
Response响应字节数据
- 步骤
- 通过Response对象获取字节输出流:ServletOutputStream outputStream = resp.getOutputStream();
- 通过字节输出流写数据: outputStream.write(字节数据);
@WebServlet("/resp4") public class ResponseDemo4 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1. 读取文件 FileInputStream fis = new FileInputStream("d://a.jpg"); //2. 获取response字节输出流 ServletOutputStream os = response.getOutputStream(); //3. 完成流的copy byte[] buff = new byte[1024]; int len = 0; while ((len = fis.read(buff))!= -1){ os.write(buff,0,len); } fis.close(); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
使用别人提供好的方法来简化代码的开发
(1)pom.xml添加依赖<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency>
(2)调用工具类方法
//3. 完成流的copy IOUtils.copy(fis,os);//fis:输入流 os:输出流 fis.close();
- 步骤
4、用户注册登录案例
-
环境准备
-
(1)前端页面
复制资料中的静态页面到项目的webapp目录下
-
(2)数据库
创建db1数据库,创建tb_user表
-
(3)项目依赖
在项目的pom.xml导入Mybatis和Mysql驱动坐标<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.34</version> </dependency>
-
(4)MyBatis配置
创建mybatis-config.xml核心配置文件<?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> <!--起别名--> <typeAliases> <package name="com.itheima.pojo"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <!-- useSSL:关闭SSL安全连接 性能更高 useServerPrepStmts:开启预编译功能 & 等同于 & ,xml配置文件中不能直接写 &符号 --> <property name="url" value="jdbc:mysql:///db1?useSSL=false&useServerPrepStmts=true"/> <property name="username" value="root"/> <property name="password" value="1234"/> </dataSource> </environment> </environments> <mappers> <!--扫描mapper--> <package name="com.itheima.mapper"/> </mappers> </configuration>
-
(5)数据库表对象类
创建User实体类
-
(6)SQL语句编写
创建UserMapper.xml映射文件,UserMapper接口
-
-
用户登录
-
需求分析
- 用户在登录页面输入用户名和密码,提交请求给LoginServlet
- 在LoginServlet中接收请求和数据[用户名和密码]
- 在LoginServlt中通过Mybatis实现调用UserMapper来根据用户名和密码查询数据库表
- 将查询的结果封装到User对象中进行返回
- 在LoginServlet中判断返回的User对象是否为null
- 如果为nul,说明根据用户名和密码没有查询到用户,则登录失败,返回"登录失败"数据给前端
- 如果不为null,则说明用户存在并且密码正确,则登录成功,返回"登录成功"数据给前端
-
代码实现
- (1)编写接口(SQL语句)
在UserMapper接口中提供一个根据用户名和密码查询用户对象的方法@Select("select * from tb_user where username = #{username} and password = #{password}") User select(@Param("username") String username,@Param("password") String password);
- (2)修改前端代码
修改loign.html<form action="/request-demo/loginServlet" method="post" id="form">
- (3)编写服务代码
编写LoginServlet@WebServlet("/loginServlet") public class LoginServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1. 接收用户名和密码 String username = request.getParameter("username"); String password = request.getParameter("password"); //2. 调用MyBatis完成查询 //2.1 获取SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2.2 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //2.3 获取Mapper UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //2.4 调用方法 User user = userMapper.select(username, password); //2.5 释放资源 sqlSession.close(); //获取字符输出流,并设置content type response.setContentType("text/html;charset=utf-8"); PrintWriter writer = response.getWriter(); //3. 判断user释放为null if(user != null){ // 登陆成功 writer.write("登陆成功"); }else { // 登陆失败 writer.write("登陆失败"); } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
- (4)完成
启动服务器测试
- (1)编写接口(SQL语句)
-
-
用户注册
- 需求分析
- 用户在注册页面输入用户名和密码,提交请求给RegisterServlet
- 在RegisterServlet中接收请求和数据[用户名和密码]
- 在RegisterServlet中通过Mybatis实现调用UserMapper来根据用户名查询数据库表
- 将查询的结果封装到User对象中进行返回
- 在RegisterServlet中判断返回的User对象是否为null
- 如果为nul,说明根据用户名可用,则调用UserMapper来实现添加用户
- 如果不为null,则说明用户不可以,返回"用户名已存在"数据给前端
- 代码实现
-
(1)编写接口(SQL语句)
编写UserMapper提供根据用户名查询用户数据方法和添加用户方法@Select("select * from tb_user where username = #{username}") User selectByUsername(String username); @Insert("insert into tb_user values(null,#{username},#{password})") void add(User user);
-
(2)修改前端代码
修改register.html<form id="reg-form" action="/request-demo/registerServlet" method="post">
-
(3)编写服务代码
创建RegisterServlet类@WebServlet("/registerServlet") public class RegisterServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1. 接收用户数据 String username = request.getParameter("username"); String password = request.getParameter("password"); //封装用户对象 User user = new User(); user.setUsername(username); user.setPassword(password); //2. 调用mapper 根据用户名查询用户对象 //2.1 获取SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2.2 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //2.3 获取Mapper UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //2.4 调用方法 User u = userMapper.selectByUsername(username); //3. 判断用户对象释放为null if( u == null){ // 用户名不存在,添加用户 userMapper.add(user); // 提交事务 sqlSession.commit(); // 释放资源 sqlSession.close(); }else { // 用户名存在,给出提示信息 response.setContentType("text/html;charset=utf-8"); response.getWriter().write("用户名已存在"); } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
- (4)完成
启动服务器测试
- (4)完成
-
- 需求分析
-
工具类抽取
-
问题:代码重复、资源浪费(SqlSessionFactory工厂类重复创建)
-
编写SqlSessionFactory工具类
public class SqlSessionFactoryUtils { private static SqlSessionFactory sqlSessionFactory; static { //静态代码块会随着类的加载而自动执行,且只执行一次 try { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSessionFactory getSqlSessionFactory(){ return sqlSessionFactory; } }
-
使用
SqlSessionFactory sqlSessionFactory =SqlSessionFactoryUtils.getSqlSessionFactory();
-