Request&Response

Request是请求对象,Response是响应对象。这两个对象在我们使用Servlet的时候有看到

  • request:==获取==请求数据
    • 浏览器会发送HTTP请求到后台服务器[Tomcat]
    • HTTP的请求中会包含很多请求数据[请求行+请求头+请求体]
    • 后台服务器[Tomcat]会对HTTP请求中的数据进行解析并把解析结果存入到一个对象中
    • 所存入的对象即为request对象,所以我们可以从request对象中获取请求的相关参数
    • 获取到数据后就可以继续后续的业务,比如获取用户名和密码就可以实现登录操作的相关业务
  • response:==设置==响应数据
    • 业务处理完后,后台就需要给前端返回业务处理的结果即响应数据
    • 把响应数据封装到response对象中
    • 后台服务器[Tomcat]会解析response对象,按照[响应行+响应头+响应体]格式拼接结果
    • 浏览器最终解析结果,把内容展示在浏览器给用户浏览

1. Request

HTTP请求数据总共分为三部分内容,分别是==请求行、请求头、请求体==,对于这三部分内容的数据,分别该如何获取,首先我们先来学习请求行数据如何获取?

1.1 获取请求行数据

请求行包含三块内容,分别是请求方式请求资源路径HTTP协议及版本

对于这三部分内容,request对象都提供了对应的API方法来获取,具体如下:

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

介绍完上述方法后,咱们通过代码把上述方法都使用下:

案例

@WebServlet("/request01")
public class Request01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        response.setHeader("content-type","text/html;charset=utf-8");
        response.getWriter().write("<h1>"+username+",欢迎您!</h1>");
//        通过request 获取请求体信息
        //获取请求方式:
        String method = request.getMethod();
        System.out.println("method = " + method);
        //获取虚拟目录(项目访问路径):当前项目
        String contextPath = request.getContextPath();
        System.out.println("contextPath = " + contextPath);
        //获取URL(统一资源定位符): 全路径
        StringBuffer requestURL = request.getRequestURL();
        System.out.println("requestURL = " + requestURL.toString());
        //获取URI(统一资源标识符):不带域名的项目路径和目录路径
        String requestURI = request.getRequestURI();
        System.out.println("requestURI = " + requestURI);
        //获取请求参数(GET方式):
        String getParams = request.getQueryString();
        System.out.println("getParams = " + getParams);
      	
      	/**
         *  执行结果如下:
         * method = GET
         * contextPath = /07RequestResponse
         * requestURL = http://localhost:8080/07RequestResponse/request01
         * requestURI = /07RequestResponse/request01
         * getParams = username=Hades
         */
    }

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

1.2 获取请求头数据

请求头信息包括像CookieUser-Agent浏览器信息等。

  • 获取请求头信息

    String getHeader(String name)
    

案例

Enumeration<String> headerNames = request.getHeaderNames();
        System.out.println("请求头信息——————start");
        while (headerNames.hasMoreElements()){
            System.out.println(headerNames.nextElement()+":"+request.getHeader(headerNames.nextElement()));
        }
        System.out.println("请求头信息——————end");
        /**
         * 执行结果如下:
         * 请求头信息——————start
         * host:keep-alive
         * upgrade-insecure-requests:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
         * sec-fetch-user:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng;
         * sec-fetch-site:navigate
         * referer:gzip, deflate, br
         * accept-language:Idea-8efd91f8=448175f3-de4a-4bb9-8c6e-25bab5b3328a
         * 请求头信息——————end
         */

1.3 获取请求参数

浏览器默认是发出get请求,所以需要更改一下html中的from method= post

对于请求参数的获取,常用的有以下两种:

  • GET方式:
String getQueryString()
  • POST方式:
BufferedReader getReader();

post请求体是以键值对&键值对的方式传递

username=zhangsan&password=1234

注意:因为参数的值可能是一个,也可能有多个,所以Map的值的类型为String数组。

基于上述理论,request对象为我们提供了如下方法:

  • 获取所有参数Map集合
Map<String,String[]> getParameterMap()
  • 根据名称获取参数值(数组)
String[] getParameterValues(String name)
  • 根据名称获取参数值(单个值)
String getParameter(String name)

案例

html

<form action="/07RequestResponse/request01" method="post">
    用户名<input name="username" type="text"></br>
    密  码<input name="password" type="password"></br>
    <input type="submit">
</form>
//获取请求参数
        Map<String, String[]> map = request.getParameterMap();
        for (String key : map.keySet()) {
            System.out.print(key+":");
            //获取值
            String[] values = map.get(key);
            for (String value : values) {
                System.out.print(value + " ");
            }

            System.out.println();
        }
				/**
         * 所有参数结果:
         * username:zhangsan 
         * password:12344 
         */

1.4请求参数中文乱码问题

  • 分析出现中文乱码的原因:

    • POST的请求参数是通过request的getReader()来获取流中的数据
    • TOMCAT 7.x在获取流的时候采用的编码是ISO-8859-1
    • ISO-8859-1编码是不支持中文的,所以会出现乱码
  • Post解决方案:

    • 页面设置的编码格式为UTF-8
    • 把TOMCAT在获取流数据之前的编码设置为UTF-8
    • 通过request.setCharacterEncoding("UTF-8")设置编码,UTF-8也可以写成小写
    request.setCharacterEncoding("utf-8");//post 解决乱码问题
    
  • get的解决方案

    • GET请求获取请求参数的方式是request.getQueryString()
    • POST请求获取请求参数的方式是request.getReader()
    • request.setCharacterEncoding("utf-8")是设置request处理流的编码
    • getQueryString方法并没有通过流的方式获取数据

    所以GET请求不能用设置编码的方式来解决中文乱码问题,只能在获取数据后,对数据进行字节转换处理。

    String username = request.getParameter("username");
    //get 解决乱码,相对麻烦一点,不过也适用于post,在tomcat8.*,默认编码是utf-8,所以不需要处理编码问题
    username = new String(username.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
    

    注意

    • request.setCharacterEncoding("UTF-8")代码注释掉后,会发现GET请求参数乱码解决方案同时也可也把POST请求参数乱码的问题也解决了
    • 只不过对于POST请求参数一般都会比较多,采用这种方式解决乱码起来比较麻烦,所以对于POST请求还是建议使用设置编码的方式进行。

    另外需要说明一点的是Tomcat8.0之后,已将GET请求乱码问题解决,设置默认的解码方式为UTF-8。

    总结

    1. 中文乱码解决方案
    • POST请求和GET请求的参数中如果有中文,后台接收数据就会出现中文乱码问题

      GET请求在Tomcat8.0以后的版本就不会出现了

    • POST请求解决方案是:设置输入流的编码

      request.setCharacterEncoding("UTF-8");
      注意:设置的字符集要和页面保持一致
      
    • 通用方式(GET/POST):需要先解码,再编码

      new String(username.getBytes("ISO-8859-1"),"UTF-8");
      
    1. URL编码实现方式:
    • 编码:

      URLEncoder.encode(str,"UTF-8");
      
    • 解码:

      URLDecoder.decode(s,"ISO-8859-1");
      

1.5 Request请求转发

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

请求转发的实现方式:

req.getRequestDispatcher("资源B路径").forward(req,resp);
  • 转发存储数据到request域[范围,数据是存储在request对象]中
void setAttribute(String name,Object o);
  • 根据key获取值
Object getAttribute(String name);
  • 根据key删除该键值对
void removeAttribute(String name);
  • 只能转发到当前服务器的内部资源

    不能从一个服务器通过转发访问另一台服务器

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

代码演示

request.setAttribute("username",username);
request.getRequestDispatcher("/request02").forward(request,response);

@WebServlet("/request02")
public class Request02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = (String) request.getAttribute("username");
        System.out.println("资源转发:"+username);
    }

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

2. Response

HTTP响应数据总共分为三部分内容,分别是响应行响应头响应体,对于这三部分内容的数据,respone对象都提供了哪些方法来进行设置

  1. 对于响应头,比较常用的就是设置响应状态码:

    void setStatus(int sc);//200
    
  2. 响应头

    void setHeader(String name,String value);
    response.setHeader("content-type","text/html;charset=utf-8");
    
  3. 响应体

    <html><head></head><body></body></html>
    response.getWriter().write("<h1>"+username+",欢迎您!</h1>");
    
  4. 对于响应体,是通过字符、字节输出流的方式往浏览器写,

    获取字符输出流:

    PrintWriter getWriter();
    

    获取字节输出流

    ServletOutputStream getOutputStream();
    

2.1 Respones请求重定向

Response重定向(redirect):一种资源跳转方式。

1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求

(2)资源A现在无法处理该请求,就会给浏览器响应一个302的状态码+location的一个访问资源B的路径

(3)浏览器接收到响应状态码为302就会重新发送请求到location对应的访问地址去访问资源B

(4)资源B接收到请求后进行处理并最终给浏览器响应结果,这整个过程就叫重定向

重定向的实现方式:

resp.setStatus(302);
resp.setHeader("location","资源B的访问路径");

代码演示

  1. 访问Servlet
@WebServlet("/responseDemo1")
public class ResponseDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("ResponseDemo1");
//        response.setStatus(302);
//        response.setHeader("Location","https://www.baidu.com");
        String route = request.getContextPath();
        System.out.println("route = " + route);
//        response.setHeader("Location",route+"/responseDemo2");
//        请求转发的简写方式
        response.sendRedirect(request.getContextPath()+"/responseDemo2");
    }

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

但是从设置重定向的两行代码来看,会发现除了重定向的地址不一样,其他的内容都是一模一样,所以request对象给我们提供了简化的编写方式为:

resposne.sendRedirect("绝对路径")
  1. 重定向Servlet
@WebServlet("/responseDemo2")
public class ResponseDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("ResponseDemo2");
    }

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

2.1.1 重定向的特点

重定向请求转发
地址栏发生变化浏览器地址栏路径不发生变化
可以重定向到任意资源(服务器内部、外部)只能转发到当前服务器内部资源
两次请求,不能在多个资源使用request共享数据一次请求,可以在资源转发间使用request共享数据
response.sendRedirect("path")request.getRequestDispatcher("/path").forward(request,response);

2.2 Response响应字符数据

要想将字符数据写回到浏览器,我们需要两个步骤:

  • 通过Response对象获取字符输出流: PrintWriter writer = resp.getWriter();

  • 通过字符输出流写数据: writer.write("aaa");

示例代码

@WebServlet("/responseDemo2")
public class ResponseDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("ResponseDemo2");
        //content-type,告诉浏览器返回的数据类型是HTML类型数据,这样浏览器才会解析HTML标签,否则原样显示
        response.setContentType("text/html;charset = utf-8");
        response.getWriter().write("abc");
        response.getWriter().write("<h1>abc</h1>");
    }

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

**注意:**一次请求响应结束后,response对象就会被销毁掉,所以不要手动关闭流。

2.3 Response响应字节数据

要想将字节数据写回到浏览器,我们需要两个步骤:

  • 通过Response对象获取字节输出流:ServletOutputStream outputStream = resp.getOutputStream();

  • 通过字节输出流写数据: outputStream.write(字节数据);

示例代码

@WebServlet("/responseDemo3")
public class ResponseDemo3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //Response响应字节数据
        FileInputStream fis = new FileInputStream("/Users/hades/Desktop/单词/day01/1best.png");
        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);
    }
}

2.3.1 使用commons-io

  1. om.xml添加依赖

    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.6</version>
    </dependency>
    
  2. 调用工具类方法

    //fis:输入流
    //os:输出流
    IOUtils.copy(fis,os);
    

3. 用户注册登录案例

学完Request和Response,来练习一个用户登陆注册案例巩固一下学习成果。

3.1 用户登陆

需求分析

  1. 用户在登录页面输入用户名和密码,提交请求给LoginServlet
  2. 在LoginServlet中接收请求和数据[用户名和密码]
  3. 在LoginServlt中通过Mybatis实现调用UserMapper来根据用户名和密码查询数据库表
  4. 将查询的结果封装到User对象中进行返回
  5. 在LoginServlet中判断返回的User对象是否为null
  6. 如果为nul,说明根据用户名和密码没有查询到用户,则登录失败,返回"登录失败"数据给前端
  7. 如果不为null,则说明用户存在并且密码正确,则登录成功,返回"登录成功"数据给前端

环境准备

  • 导入依赖库

    <dependencies>
            <dependency>
    <!--            网络访问-->
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>4.0.1</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
    <!--            数据库操作-->
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.5</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.23</version>
            </dependency>
        </dependencies>
    

代码实现

LoginServlet

package com.example.web;

import com.example.mapper.UserMapper2;
import com.example.pojo.User;
import com.example.utils.SqlSessionUtil;
import org.apache.ibatis.ognl.security.UserMethod;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取请求参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //防止中文乱码
        if (username != null&&password!=null) {
            username = new String(username.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
            password = new String(password.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
            System.out.println(username+":"+password);

            //通过mysql数据查询用户名密码是否存在数据库中
            SqlSessionFactory sqlSessionFactory = SqlSessionUtil.getSqlSessionFactory();
            SqlSession sqlSession = sqlSessionFactory.openSession();
            UserMapper2 mapper = sqlSession.getMapper(UserMapper2.class);
            User user = mapper.findUser(username, password);
            sqlSession.close();
            response.setContentType("text/html;charset = utf-8");
            if (user == null) {
                response.getWriter().write("用户名密码错误!!");
            }else {
                response.getWriter().write("登陆成功");
            }
        }


    }

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

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
    <style>

    </style>
</head>
<body>
<center>
    <form action="/RegisterLoginDemo/loginServlet" method="post">
        用户名:<input type="text" name="username"></br>
        密  码:<input type="password" name="password"></br>
        <input type="submit" value="登陆">
    </form>
</center>

</body>
</html>

3.2 用户注册

  1. 用户在注册页面输入用户名和密码,提交请求给RegisterServlet
  2. 在RegisterServlet中接收请求和数据[用户名和密码]
  3. 在RegisterServlet中通过Mybatis实现调用UserMapper来根据用户名查询数据库表
  4. 将查询的结果封装到User对象中进行返回
  5. 在RegisterServlet中判断返回的User对象是否为null
  6. 如果为nul,说明根据用户名可用,则调用UserMapper来实现添加用户
  7. 如果不为null,则说明用户不可以,返回"用户名已存在"数据给前端

代码实现

RegisterServlet

package com.example.web;

import com.example.mapper.UserMapper2;
import com.example.pojo.User;
import com.example.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

@WebServlet("/registerServlet")
public class RegisterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取请求参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //防止中文乱码
        if (username != null&&password!=null) {
            username = new String(username.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
            password = new String(password.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
            System.out.println(username+":"+password);

            //通过mysql数据查询用户名密码是否存在数据库中
            SqlSessionFactory sqlSessionFactory = SqlSessionUtil.getSqlSessionFactory();
            SqlSession sqlSession = sqlSessionFactory.openSession();
            UserMapper2 mapper = sqlSession.getMapper(UserMapper2.class);
            User user = mapper.findUserByName(username);
            response.setContentType("text/html;charset = utf-8");// 响应中文
            if (user == null) {
                User userAdd = new User();
                userAdd.setUsername(username);
                userAdd.setPassword(password);
                mapper.addUser(userAdd);
                sqlSession.commit();
                sqlSession.close();
                response.getWriter().write("注册成功");
            }else {
                response.getWriter().write("用户名已存在!!");
            }
        }
    }

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

register.html

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>注册</title>
</head>
<body>
<center>
    <h1>注册用户</h1>
    <form action="/RegisterLoginDemo/registerServlet" method="post">
        用户名:<input type="text" name="username"></br>
        密  码:<input type="password" name="password"></br>
        <input type="submit" value="注册">
    </form>
</center>

</body>
</html>

更多详细代码,查看07RequestResponse下的子模块RegisterLoginDemo

Loading...