7.Request(请求) 和 Response(响应)

Request(请求) 和 Response(响应)

笔记目录:(https://www.cnblogs.com/wenjie2000/p/16378441.html)

视频教程(P99~P111)

  • Request:获取请求数据

  • Response:设置响应数据

Request

Request继承体系

  1. Tomcat需要解析请求数据,封装为request对象,并且创建request对象传递到service方法中
  2. 使用request对象,查阅JavaEE API文档的HttpServletRequest接口

Request获取请求数据

获取请求数据

  • 请求数据分为3部分:

    1. 请求行:GET /request-demo/req1?username=zhangsan HTTP/1.1

      • String getMethod():获取请求方式:GET
      • String getContextPath():获取虚拟目录(项目访问路径):/request-demo
      • StringBuffer getRequestURL():获取URL(统一资源定位符): http://localhost:8080/request-demo/req1
      • String getRequestURI():获取URI(统一资源标识符):/request-demo/req1
      • String getQueryString():获取请求参数(GET方式) :username=zhangsan&password=123
    2. 请求头:User-Agent:Mozilla/5.0 Chrome/91.0.4472.106

      • String getHeader(String name):根据请求头名称,获取值
    3. 请求体:username=superbaby&password=123

      • ServletlnputStream getlnputStream():获取字节输入流
      • BufferedReader getReader():获取字符输入流

Request通用方式获取请求参数

  • 请求参数获取方式:

    • GET方式:

      String getQueryString()

    • POST方式

      BufferedReader getReader()

思考:

GET请求方式和POST请求方式区别主要在于获取请求参数的方式不一样,是否可以提供一种统一获取请求参数的方式,从而统一doGet和doPost方法内的代码?

有了通用的请求参数的方式,那么就可以将任务都交给doPost和doGet方法中的一个处理,另一个只需要调用它就行了。

例如:先在doGet方法中实现原本doPost和doGet的所有功能。再在doPost方法中直接调用doGet(this. doGet(req,resp);

  • 使用通用方式获取请求参数后,屏蔽了GET和POST的请求方式代码的不同,则代码可以定义为如下格式:

    @WebServlet("/aaaa")
    public class Servlet4 extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request, response);
        }
    }
    
  • 可以使用Servlet模板创建Servlet更高效(设置模板:Setting-->File and Code Templates -->Other-->Web-->Servlet Annotated Class.java)

    对模板修改成下面这样:

    #if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
    #parse("File Header.java")
    @javax.servlet.annotation.WebServlet("/${Entity_Name}")
    public class ${Class_Name} extends javax.servlet.http.HttpServlet {
        protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
    
        }
    
        protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
            this.doPost(request,response);
        }
    }
    

    在项目目录中就可以根据模板创建Servlet了(要创建servlet的目录处 右键--> New-->CreateNewServlet-->设置类名-->创建成功)

请求参数中文乱码处理

tomcat10以下版本会出现POST乱码问题。tomcat8以下会出现GET乱码问题。(注意,如果使用tomcat-maven插件,属于是tomcat7)

  • 请求参数如果存在中文数据,则会乱码

  • 解决方案:

    POST:设置输入流的编码(这里的字符流默认编码为iso-8859-1)

    req.setCharacterEncoding("UTF-8");
    

    通用方式(GET/POST)︰先编码,再解码(将乱码的内容转为字节,再转为utf-8)

    String string = req.getParameter("aaaa");
    new String(string.getBytes("ISO-8859-1"),"UTF-8");
    

Request请求转发

  • 请求转发(forward):一种在服务器内部的资源跳转方式

  • 实现方式:

    req.getRequestDispatcher("资源B路径").forward(req,resp);//资源b路径格式为"/req2"或"req2"
    
  • 请求转发资源间共享数据:使用Request对象(例:req.getAttribute("aaa"))

    • void setAttribute(String name, Object o):存储数据到request域中(放到request中一个专用于传递的集合)
    • Object getAttribute(String name):根据key,获取值
    • void removeAttribute(String name):根据key,删除该键值对
  • 请求转发特点:

    浏览器地址栏路径不发生变化

    只能转发到当前服务器的内部资源

    —次请求,可以在转发的资源间使用request共享数据

Response

Response继承体系

和request结构差不多

Response设置响应数据功能介绍

  • 响应数据分为3部分:
    1. 响应行:HTTP/1.1200 OK
      • void setStatus(int sc):设置响应状态码
    2. 响应头:Content-Type: text/html
      • void setHeader(String name, String value):设置响应头键值对
    3. 响应体:<html><head>head><body></body></html>
      • PrintWriter getWriter():获取字符输出流
      • ServletOutputStream getOutputStream():获取字节输出流

Response完成重定向

  • 重定向(Redirect):—种资源跳转方式

  • 实现方式:

    resp.setStatus(302);
    resp.setHeader("location","资源B的路径");//路径可使用"/request-demo/resp2"或"resp2"的方式,建议用后者"resp2",能降低耦合度
    

    以上两步为底层逻辑,也有简化的一步完成的实现方式:

    response.sendRedirect("资源B的路径");
    //路径可使用"/request-demo/resp2"或"resp2"的方式,建议用后者"resp2",能降低耦合度。
    //如果强行要用前者,建议使用request.getContextPath()动态获取虚拟目录"/request-demo"
    
  • 重定向特点:

    浏览器地址栏路径发生变化

    可以重定向到任意位置的资源(服务器内部、外部均可)

    两次请求,不能在多个资源使用request共享数据

补充:路径问题

  • 明确路径谁使用?

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

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

  • 练习:

    <a href='路径'>	//加虚拟目录
    <form action='路径'>	//加虚拟目录
    req.getRequestDispatcher("路径")	//不加虚拟目录
    resp.sendRedirect("路径")	//加虚拟目录
    

Response响应字符数据

  • 使用

    1. 通过Response对象获取字符输出流

      PrintWriter writer = response.getWriter();
      
    2. 写数据

      writer.write("aaaaaa一二三");
      
  • 注意:

    • 该流不需要关闭,随着响应结束,response对象销毁,由服务器关闭(手动关闭也不会报错)

    • 中文数据乱码:原因通过Response获取的字符输出流默认编码:ContentType:ISO-8859-1

      response.setContentType("text/html;charset=utf-8");//如果utf-8也乱码,就用GBK
      //通过设置响应头中ContentType中的“text/html;”表示writer.write()的也就是响应内容的数据类型
      //也同时设置charset=utf-8 代表编码
      //注意,设置编码要在获取字符输出流之前
      

Response响应字节数据

  • 使用

    1. 通过Response对象获取字节输出流

      ServletOutputStream outputStream = response.getOutputStream();
      
    2. 写数据

      outputStream.write(字节数据);
      
  • 实例演示

    //实现在网页上显示图片(字节数据)
    FileInputStream fis = new FileInputStream("C:\\Users\\ZWJ\\Desktop\\材料.png");
    ServletOutputStream os = response.getOutputStream();
    byte[] buff = new byte[1024];
    int len;
    while ( (len = fis.read(buff)) != -1){
        os.write(buff, 0, len);
    }
    fis.close();
    
  • IOUtils工具使用(方便数据流的拷贝)

    1. 导入坐标

      <dependency>
          <groupId>commons-io</groupId>
          <artifactId>commons-io</artifactId>
          <version>2.6</version>
      </dependency>
      
    2. 使用(org.apache.commons.io.IOUtils)

      //将输入流数据拷贝到输出流
      IOUtils.copy(输入流,输出流);
      

    使用上面的工具,就能简化之前的代码

     FileInputStream fis = new FileInputStream("C:\\Users\\ZWJ\\Desktop\\材料.png");
     ServletOutputStream os = response.getOutputStream();
    /* byte[] buff = new byte[1024];
     int len;
     while ( (len = fis.read(buff)) != -1){
         os.write(buff, 0, len);
     }*/
     IOUtils.copy(fis,os);
     fis.close();
    

练习

用户登录

实现网页登录功能(要使用到mybatis和servlet的知识)

  • 流程说明:

    1. 用户填写用户名密码,提交到LoginServlet
    2. 在LoginServlet中使用MyBatis查询数据库,验证用户名密码是否正确
    3. 如果正确,响应“登录成功”,如果错误,响应“登录失败”

  • 准备环境:

    1. 编写静态页面login.html
    2. 创建db1数据库,创建tb_user表,创建User实体类
    3. 导入MyBatis坐标,MySQL驱动坐标
    4. 创建mybatis-config.xml核心配置文件,UserMapper.xml映射文件,UserMapper接口
    5. 编写Servlet

自己先试着做,做不出来,再看代码

项目文件结构(圈出的部分内容我后面会给出)

pom.xml依赖

<dependencies>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.9</version><!--注意驱动版本,多半和我不一样-->
    </dependency>
</dependencies>

前端页面(login.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录界面</title>
</head>
<body>
<form action="/login-web/loginServlet" method="post">
    用户名:<input name="username">
    密码:<input type="password" name="password">
    <input type="submit" value="登录">
</form>
</body>
</html>

数据库表

-- 创建表
CREATE TABLE tb_user(
	id int PRIMARY KEY auto_increment,
	username VARCHAR(20) UNIQUE,
	password VARCHAR(32)
);
-- 添加数据
INSERT INTO tb_user(username,password) VALUES ('zhangsan','123'),('lisi','234');
SELECT * FROM tb_user;

User实体类(参考数据库表,我就不给了)

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.itwen.pojo"/>
    </typeAliases>

    <!--设置连接数据库的环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/db1"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入映射文件-->
    <mappers>
        <package name="com.itwen.mapper"/>
    </mappers>
</configuration>

映射器接口(UserMapper.java)

package com.itwen.mapper;

import com.itwen.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

public interface UserMapper {

    /**
     * 根据用户名和密码查找用户对象
     * @param username
     * @param password
     * @return
     */
    @Select("select * from tb_user where username=#{un} and password=#{pd}")
    public User selectUser(@Param("un") String username, @Param("pd") String password);
}

映射文件(UserMapper.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itwen.mapper.UserMapper">
</mapper>

LoginServlet.java

package com.itwen.web;

import com.itwen.mapper.UserMapper;
import com.itwen.pojo.User;
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 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.InputStream;
import java.io.PrintWriter;

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.接收用户名密码
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        System.out.println(username);
        System.out.println(password);

        //2.使用MyBatis查询(MyBatis部分学过)
        //2.1 获取sqlSessionFactory对象(mybatis官网有)
        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 mapper = sqlSession.getMapper(UserMapper.class);
        //2.4调用方法查询信息
        User user = mapper.selectUser(username, password);
        //2.5释放资源
        sqlSession.close();

        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        System.out.println(user);
        //3.判断user是否为空
        if(user!=null){
            //登录成功
            writer.write("登陆成功");
        }else {
            //登录失败
            writer.write("登录失败");
        }

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

用户注册

  • 流程说明:
  1. 用户填写用户名、密码等信息,点击注册按钮,提交到RegisterServlet

  2. 在RegisterServlet中使用MyBatis保存数据

  3. 保存前,需要判断用户名是否已经存在:根据用户名查询数据库

代码和登录功能类似,我就不给了

代码优化

每个用户登录和注册都会创建SqlSessionFactory,类似的内容一多就会照成资源浪费。(他们可以共用一个SqlSessionFactory)

  • 创建SqlSessionFactory 代码优化

    需要时只需使用SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();获取工厂

    package com.itwen.util;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    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;
        }
    }
    

思考:获取sqlSession的代码也是重复的,要不要也放到工具类中?

SqlSession sqlSession = sqlSessionFactory.openSession();

所有用户和所有事务都共用同一个连接,会导致不能正常管理事务,并且多个用户多个功能之间产生影响

posted @ 2023-01-09 09:06  文杰2000  阅读(126)  评论(0编辑  收藏  举报