Servlet Request 处理服务器的请求


Servlet Request

  • 用户通过浏览器访问服务器时,Tomcat 将 HTTP 请求中所有的信息都封装在 Request 对象中。
  • 开发人员可以通过 Request 对象方法,来获取浏览器发送的所有信息。
    在这里插入图片描述

1. Request 的体系结构

ServletRequest
		|
HttpServletRequest
		|
org.apache.catalina.connector.RequestFacade(由 Tomcat 厂商提供实现类)
  • JavaEE 的编程即是面向接口编程。
  • 面向接口编程就是先把客户的业务提取出来,作为接口。业务具体实现通过该接口的实现类来完成。当客户需求变化时,只需编来写该业务逻辑的新的实现类,通过更改配置文件(例如 Spring 框架)中该接口的实现类就可以完成需求,不需要改写现有代码,减少对系统的源影响。
  • 采用基于接口编程的项目,业务逻辑清晰,代码易懂,方便扩展,可维护性强。理论上度当需求变化的时候只需要修改接口实现。

2. Request 获取 HTTP 请求信息

在这里插入图片描述

a. 获取请求行信息

  • 请求行是 HTTP 请求内容的第一行。例如:GET /webappPractice2/requestDemo HTTP/1.1
  • 相关 API:
    1. 获取请求方式,如 GET
      String getMethod()
    2. 获取项目虚拟路径项目名,如 /webappPractice2
      String getContextPath()
    3. 获取 URI 统一资源标识符,如 /webappPractice2/requestDemo
      String getRequestURI()
    4. 获取 URL 统一资源定位符,如 http://localhost:8080/webappPractice2/requestDemo
      StringBuffer getRequestURL()
    5. 获取协议和版本号,如 HTTP/1.1
      String getProtocol()
    6. 获取客户端 IP
      String getRemoteAddr()
  • 代码示例:
@WebServlet("/requestDemo")
public class RequestDemo extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(req);
        System.out.println("请求方式:" + req.getMethod());
        System.out.println("虚拟路径:" + req.getContextPath());
        System.out.println("URI:" + req.getRequestURI());
        System.out.println("URL:" + req.getRequestURL());
        System.out.println("协议和版本:" + req.getProtocol());
        System.out.println("客户端ip:" + req.getRemoteAddr());
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

b. 获取请求头信息

  • 请求头信息很多,例如 Host: localhost:8080
  • 相关 API:
    1. 获取知道请求头名称对应的值,注:名称不区分大小写
      String getHeader(String name)
    2. 获取所有请求头的名称,注:Enumeration 是 Iterator 的前身
      Enumeration<String> getHeaderNames()

i. 获取所有请求头

  • 代码示例:
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;

@WebServlet("/requestDemo")
public class RequestDemo extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取所有的请求头名称
        Enumeration<String> enumeration = req.getHeaderNames();
        // 遍历
        while(enumeration.hasMoreElements()){
            // 取出元素名(请求头名称)
            String name = enumeration.nextElement();
            // 根据名称获取值
            String value = req.getHeader(name);
            System.out.println(name +" : "+ value);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

ii. 获取指定请求头

案例 1:视频防盗链
  • referer : 请求来源。
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("/requestDemo")
public class RequestDemo extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        // 1.获取请求来源(如果是浏览器地址直接访问的话,referer就是null)
        String referer = req.getHeader("referer");
        // 2.判断是不是自家网站发起的请求
        if (referer != null && referer.startsWith("http://localhost:8080")) {
            resp.getWriter().write("正常播放视频...");
        }else{
            resp.getWriter().write("需要到优酷官网才能播放视频....");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}
案例 2:浏览器兼容性
  • user-agen:浏览器版本信息。
  • 现在后端处理兼容性问题的需求越来越少,基本都是前端工程师完成的。
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("/requestDemo")
public class RequestDemo extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");

        // 1.获取浏览器版本信息
        String userAgent = req.getHeader("user-agent");
        // 2.判断浏览器版本
        if (userAgent.contains("Chrome")) {
            resp.getWriter().write("浏览器:谷歌");
        } else if (userAgent.contains("Firefox")) {
            resp.getWriter().write("浏览器:火狐");
        } else {
            resp.getWriter().write("浏览器:其他");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

在这里插入图片描述

c. 获取请求参数(请求体信息)

  • 不论是 GET 还是 POST 的请求方式,它们的业务逻辑都是一样的,都可以使用下列方法来获取请求参数。
  • 请求参数,例如:username=Regino&password=123&hobby=drink&hobby=perm
  • 相关 API:
    1. 获取指定参数名的值,如 username=jack
      String getParameter(String name)
    2. 获取指定参数名的值数组,如 hobby=drink&hobby=perm
      String[] getParameterValues(String name)
    3. 获取所有参数名和对应值的数组,封装为 map 集合,key 是参数名 name,value 是值数组:
      Map<String,String[]> getParameterMap()
  • 解决中文乱码问题:
    1. get:在 Tomcat8 及以上版本,内部 URL 编码(UTF-8),不用处理;
    2. post:编码解码不一致,造成乱码现象:
      客户端(浏览器)编码:UTF-8
      服务器默认解码:ISO-8859-1(拉丁文)
      指定解码方法:void setCharacterEncoding(String env),注:该方法必须在方法内的行首
  • 测试网页 demo.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>form</title>
</head>
<body>
<h3>get方式:</h3>
<form action="/webappPractice2/requestDemo" method="get">
  用户:<input type="text" name="username"><br>
  密码:<input type="password" name="password"><br>
  爱好:
  <input type="checkbox" name="hobby" value="somke"/>抽烟
  <input type="checkbox" name="hobby" value="drink"/>喝酒
  <input type="checkbox" name="hobby" value="perm"/>烫头
  <input type="submit" value="get提交...">
</form>
<h3>post方式:</h3>
<form action="/webappPractice2/requestDemo" method="post">
  用户:<input type="text" name="username"><br>
  密码:<input type="password" name="password"><br>
  爱好:
  <input type="checkbox" name="hobby" value="somke"/>抽烟
  <input type="checkbox" name="hobby" value="drink"/>喝酒
  <input type="checkbox" name="hobby" value="perm"/>烫头
  <input type="submit" value="post提交...">
</form>
</body>
</html>
  • RequestDemo.java:
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.Arrays;
import java.util.Map;

@WebServlet("/requestDemo")
public class RequestDemo extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("1.getParameter指定参数名的值或值数组的获取");
        String username = req.getParameter("username");
        System.out.println("用户:" + username);
        String password = req.getParameter("password");
        System.out.println("密码:" + password);
        System.out.println("2.getParameterValues指定参数名的值数组的获取");
        String[] hobby = req.getParameterValues("hobby");
        System.out.println("爱好:" + Arrays.toString(hobby));
        System.out.println("3.getParameterMap一并获取");
        Map<String, String[]> parameterMap = req.getParameterMap();
        parameterMap.forEach((k, v) -> {
            System.out.println(k + " = " + Arrays.toString(v));
        });
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");// 指定post的解码方式
        this.doGet(req, resp); // 转到doGet更省事
    }
}

3. Request 的其他功能

a. 请求转发

  • 请求转发是一种在服务器内部的资源跳转方式。
    在这里插入图片描述
  • 相关 API:
    1. 通过 Reqeust 对象,获得转发器对象:
      RequestDispatcher getRequestDispatcher(String path)
    2. 通过转发器对象,实现转发功能:
      void forward(ServletRequest request, ServletResponse response)
  • 推荐写链式编程:request.getRequestDispatcher("/requestDemo2").forward(request,response);
  • 请求转发特点:浏览器发了一次请求,虽然地址栏没有发生改变,但是转发到了服务器的内部资源(引用地址栏只用写相对地址)。
  • RequestDemo.java:
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("/requestDemo")
public class RequestDemo extends HttpServlet {

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

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("RequestDemo is working.");
        request.getRequestDispatcher("/requestDemo2").forward(request,response);
    }
}
  • RequestDemo2.java:
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("/requestDemo2")
public class RequestDemo2 extends HttpServlet {

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

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("RequestDemo2 is working.");
    }
}

b. 共享数据(Request 域对象 )

  • 域对象:一个有作用范围的对象,可以在范围内实现 Servlet 与 Servlet 之间的共享数据。
  • Request 域:代表一次请求的范围,一般用于一次请求中 Servlet 与 Servlet 之间转发的多个资源的数据共享。
    在这里插入图片描述
  • 相关 API:
    1. 存储数据
      void setAttribute(String name, Object o)
    2. 获取数据
      Object getAttribute(String name)
    3. 删除数据
      void removeAttribute(String name)
  • 生命周期:
    1. 何时创建?
      用户发送请求时,创建 Request 域对象。
    2. 何时销毁
      服务器返回响应时,销毁 Request 域对象。
    3. 作用范围?
      一次请求,包含多次转发。
  • RequestDemo:
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("/requestDemo")
public class RequestDemo extends HttpServlet {

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

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("RequestDemo is working.");
        request.setAttribute("receipt", "hamburger");
        request.getRequestDispatcher("/requestDemo2").forward(request,response);
    }
}
  • RequestDemo2:
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("/requestDemo2")
public class RequestDemo2 extends HttpServlet {

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

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("RequestDemo2 is working.");
        String received = (String) request.getAttribute("receipt");
        System.out.println("receipt:" + received);
    }
}
  • 请求转发中之所以可以用 Request 域对象共享数据是因为使用不同的 Servlet 使用了同一个 Request 对象。
  • Tomcat 服务器收到请求转发的时候,不会创建新的 Request 与 Response 对象,而是直接利用 RequestDemo 的 Request 与 Response 对象调用 RequestDemo2 的 doPost 方法。

c. 获取 ServletContext 对象

  • ServletContext 的中文直译:应用上下文对象。
  • ServletContext 可以应用在一个 web 项目中:
    在这里插入图片描述
  • 通过reuqest,可以获得 ServletContext 对象:
public ServletContext getServletContext();

4. 综合案例:用户登录

a. 主要需求

  • 实现用户的登录功能;
  • 登录成功跳转到 SuccessServlet 展示:登录成功!xxx,欢迎您
  • 登录失败跳转到 FailServlet 展示:登录失败,用户名或密码错误

b. 需求分析

  • Request 获取请求参数
  • Request 请求转发
  • Request 域共享数据
    在这里插入图片描述

c. 代码实现

i. 创建 web 项目,并导入 BeanUtils 工具类

在这里插入图片描述

ii. index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<h4>用户登录</h4>
<form action="/webappPractice2/loginServlet" method="post">
    用户:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <input type="submit" value="登录">
</form>
</body>
</html>

iii. User 实体类

public class User {

    private String username;// 用户名

    private String password;// 密码

    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{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

iv. LoginServlet

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("/loginServlet")
public class LoginServlet extends HttpServlet {

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

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1.设置request解码方式,即解决post请求方式获取请求参数的中文乱码问题
        request.setCharacterEncoding("UTF-8");
        // 2.获取浏览器请求参数,map集合
        Map<String, String[]> parameterMap = request.getParameterMap();
        // 3.使用BeanUtils工具类,封装user对象中
        User user = new User();
        try {
            BeanUtils.populate(user, parameterMap);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        // 4.判断
        if ("Regino".equals(user.getUsername()) && "123".equals(user.getPassword())) {
            // 正确
            request.setAttribute("user", user);
            request.getRequestDispatcher("/successServlet").forward(request, response);
        } else {
            // 错误
            request.getRequestDispatcher("/failServlet").forward(request, response);
        }
    }
}

v. SuccessServlet

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 doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1.从request域获取user对象
        User user = (User) request.getAttribute("user");
        // 2.提示信息
        response.setContentType("text/html;charset=utf-8");// 将展示的文本转码,防止输出中文时乱码,即解决向浏览器输出中文数据的乱码问题
        response.getWriter().write(user.getUsername() + ",登录成功!");
    }
}

vi. FailServlet

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 doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1.提示信息
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("用户名或密码错误。");
    }
}

d. 测试


原文链接:https://qwert.blog.csdn.net/article/details/105509803

发布了371 篇原创文章 · 获赞 384 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Regino/article/details/105509803