HttpServletRequest接口详解

 
一般情况下,浏览器(客户端)通过 HTTP 协议来访问服务器的资源,Servlet 主要用来处理 HTTP 请求。

Servlet 处理 HTTP 请求的流程如下:

Servlet 处理HTTP请求

  1. Servlet 容器接收到来自客户端的 HTTP 请求后,容器会针对该请求分别创建一个 HttpServletRequest 对象和 HttpServletReponse 对象。
  2. 容器将 HttpServletRequest 对象和 HttpServletReponse 对象以参数的形式传入 service() 方法内,并调用该方法。
  3. 在 service() 方法中 Servlet 通过 HttpServletRequest 对象获取客户端信息以及请求的相关信息。
  4. 对 HTTP 请求进行处理。
  5. 请求处理完成后,将响应信息封装到 HttpServletReponse 对象中。
  6. Servlet 容器将响应信息返回给客户端。
  7. 当 Servlet 容器将响应信息返回给客户端后,HttpServletRequest 对象和 HttpServletReponse 对象被销毁。

通过以上流程可以看出, HttpServletRequest 和 HttpServletReponse 是 Servlet 处理 HTTP 请求流程中最重要的两个对象。HttpServletRequest 对象用于封装 HTTP 请求信息,HttpServletReponse 对象用于封装 HTTP 响应信息。

下面我们将对 HttpServletRequest 进行详细介绍。

HttpServletRequest 接口

在 Servlet API 中,定义了一个 HttpServletRequest 接口,它继承自 ServletRequest 接口。HttpServletRequest 对象专门用于封装 HTTP 请求消息,简称 request 对象。

HTTP 请求消息分为请求行、请求消息头和请求消息体三部分,所以 HttpServletRequest 接口中定义了获取请求行、请求头和请求消息体的相关方法。

获取请求行信息

HTTP 请求的请求行中包含请求方法、请求资源名、请求路径等信息,HttpServletRequest 接口定义了一系列获取请求行信息的方法,如下表。

返回值类型 方法声明 描述
String getMethod() 该方法用于获取 HTTP 请求方式(如 GET、POST 等)。
String getRequestURI() 该方法用于获取请求行中的资源名称部分,即位于 URL 的主机和端口之后,参数部分之前的部分。
String getQueryString() 该方法用于获取请求行中的参数部分,也就是 URL 中“?”以后的所有内容。
String getContextPath() 返回当前 Servlet 所在的应用的名字(上下文)。对于默认(ROOT)上下文中的 Servlet,此方法返回空字符串""。
String getServletPath() 该方法用于获取 Servlet 所映射的路径。
String getRemoteAddr() 该方法用于获取客户端的 IP 地址。
String getRemoteHost() 该方法用于获取客户端的完整主机名,如果无法解析出客户机的完整主机名,则该方法将会返回客户端的 IP 地址。

示例 1

为了更好地理解这些方法,下面通过案例演示这些方法的使用。

创建一个名称为 httpServletRequestDemo 的项目,在 net.biancheng.www 包中创建一个名为 RequestLine 的 Servlet 类,代码如下。
package net.biancheng.www;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/RequestLine")
public class RequestLine extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        writer.println("请求方式:" + request.getMethod() + "<br/>" +
                "客户端的 IP 地址:" + request.getRemoteAddr() + "<br/>" +
                "应用名字(上下文):" + request.getContextPath() + "<br/>" +
                "URI:" + request.getRequestURI() + "<br/>" +
                "请求字符串:" + request.getQueryString() + "<br/>" +
                "Servlet所映射的路径:" + request.getServletPath() + "<br/>" +
                "客户端的完整主机名:" + request.getRemoteHost() + "<br/>"
        );
    }

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

}

启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/httpServletRequestDemo/RequestLine?a=1&b=2”,访问 RequestLine,结果如下图。

requestLine

获取请求头信息

当浏览器发送请求时,需要通过请求头向服务器传递一些附加信息,例如客户端可以接收的数据类型、压缩方式、语言等。为了获取请求头中的信息, HttpServletRequest 接口定义了一系列用于获取 HTTP 请求头字段的方法,如下表所示。

返回值类型 方法声明 描述
String getHeader(String name) 该方法用于获取一个指定头字段的值。
如果请求消息中包含多个指定名称的头字段,则该方法返回其中第一个头字段的值。
Enumeration getHeaders(String name) 该方法返回指定头字段的所有值的枚举集合,
在多数情况下,一个头字段名在请求消息中只出现一次,但有时可能会出现多次。
Enumeration getHeaderNames() 该方法返回请求头中所有头字段的枚举集合。
String getContentType() 该方法用于获取 Content-Type 头字段的值。
int getContentLength() 该方法用于获取 Content-Length 头字段的值 。
String getCharacterEncoding() 该方法用于返回请求消息的字符集编码 。

示例 2

为了更好地理解这些方法,下面通过案例演示这些方法的使用。

在 net.biancheng.www 包中创建一个名为 RequestHeader 的 Servlet 类,代码如下。
package net.biancheng.www;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/RequestHeader")
public class RequestHeader extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        //获得所有请求头字段的枚举集合
        Enumeration<String> headers = request.getHeaderNames();
        while (headers.hasMoreElements()) {
            //获得请求头字段的值
            String value = request.getHeader(headers.nextElement());
            writer.write(headers.nextElement() + ":" + value + "<br/>");
        }
    }

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

}

启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/httpServletRequestDemo/RequestHeader”,访问 RequestHeader ,结果如下图。

requestHeader

获取 form 表单的数据

在实际开发中,我们经常需要获取用户提交的表单数据,例如用户名和密码等。为了方便获取表单中的请求参数,ServletRequest 定义了一系列获取请求参数的方法,如下表所示。

返回值类型 方法声明 功能描述
String  getParameter(String name) 返回指定参数名的参数值。
String [ ]  getParameterValues (String name) 以字符串数组的形式返回指定参数名的所有参数值(HTTP 请求中可以有多个相同参数名的参数)。
Enumeration  getParameterNames() 以枚举集合的形式返回请求中所有参数名。
Map  getParameterMap() 用于将请求中的所有参数名和参数值装入一个 Map 对象中返回。

示例 3

为了更好地理解这些方法,下面通过案例演示这些方法的使用。

在 httpServletRequestDemo 的 WebContent 目录下,创建 form.html,代码如下。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <form action="/httpServletRequestDemo/RequestParam" method="post">
        <table border="1" width="50%">
            <tr>
                <td colspan="2" align="center">编程帮wwww.biancheng.net</td>
            </tr>
            <tr>
                <td>输入姓名</td>
                <td><input type="text" name="username" /></td>
            </tr>
            <tr>
                <td>输入密码</td>
                <td><input type="password" name="password" /></td>
            </tr>
            <tr>
                <td>选择性别</td>
                <td><input type="radio" name="sex" value="male" />男 <input
                    type="radio" name="sex" value="female" />女</td>
            </tr>
            <tr>
                <td>选择使用的语言</td>
                <td><input type="checkbox" name="language" value="JAVA" />JAVA
                    <input type="checkbox" name="language" value="C" />C语言 <input
                    type="checkbox" name="language" value="PHP" />PHP <input
                    type="checkbox" name="language" value="Python" />Python</td>
            </tr>
            <tr>
                <td>选择城市</td>
                <td><select name="city">
                        <option value="none">--请选择--</option>
                        <option value="beijing">北京</option>
                        <option value="shanghai">上海</option>
                        <option value="guangzhou">广州</option>
                </select></td>
            </tr>
            <tr>
                <td colspan="2"><input type="submit" value="提交" /></td>
            </tr>
        </table>
    </form>
</body>
</html>

在 net.biancheng.www 包中,创建一个名为 RequestParam 的 Servlet 类,代码如下。
package net.biancheng.www;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/RequestParam")
public class RequestParam extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        // 获取内容,做其他操作
        // 获取姓名
        String username = request.getParameter("username");
        // 获取密码
        String password = request.getParameter("password");
        // 获取性别
        String sex = request.getParameter("sex");
        // 获取城市
        String city = request.getParameter("city");
        // 获取语言
        String[] languages = request.getParameterValues("language");
        writer.write("用户名:" + username + "<br/>" + "密码:" + password + "<br/>" + "性别:" + sex + "<br/>" + "城市:" + city
                + "<br/>" + "使用过的语言:" + Arrays.toString(languages) + "<br/>");
    }

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

启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/httpServletRequestDemo/form.html”,即可访问 form.html,结果如下图。

form 图

在表单中填写信息后,点击提交,结果如下图。

表单参数

中文乱码问题

如果在 form 表单输入框中输入中文,例如在输入姓名时,填写“编程帮默认用户”,点击提交后就会出现乱码问题,如下图所示。


 
根据请求方式的不同,请求一般可以被分为两种:GET 请求和 POST 请求。这两种请求方式都可能会产生中文乱码问题,下面我们分别对它们产生乱码的原因及其解决方案进行介绍。

POST 请求

乱码的原因:POST 提交的数据在请求体中,其所使用的编码格式时页面一致(即 utf-8)。request 对象接收到数据之后,会将数据放到 request 缓冲区,缓冲区的默认字符集是 ISO-8859-1(该字符集不支持中文),两者使用的字符集不一致导致乱码。

解决方案:在获取请求参数之前设置 request 缓冲区字符集为 utf-8 ,代码如下。
//修改request缓冲区的字符集为UTF-8
request.setCharacterEncoding("utf-8");
// 获取用户名
String username = request.getParameter("username");

GET 请求

乱码的原因:Get 请求将请求数据附加到 URL 后面作为参数,浏览器发送文字时采用的编码格式与页面编码保持一致(utf-8)。如果 Tomcat 没有设置字符集,接收 URL 时默认使用 ISO-8859-1 进行解码,ISO-8859-1 不兼容中文,无法正确解码,导致出现乱码。

需要注意的是,在 Tomcat 8 中已解决了 get 方式提交请求中文乱码的问题,使用 Tomcat 8 及以上版本的同学不必再考虑此问题了,如果您使用的是 Tomcat 7 或更早的版本,出现乱码问题可以使用如下的方案解决。


解决方案:解决 GET 请求中文乱码问题,有以下 3 种解决方案。

1. 修改 tomcat/conf/server.xml 中的配置,代码如下。
<Connector port="80" protocol="HTTP/1.1"
         connectionTimeout="20000"
           redirectPort="8443" URIEncoding="UTF-8"/>

2. 使用 URLEncoder 和 URLDecoder 进行编码和解码的操作(逆向编解码)。 
//得到TOMCAT通过ISO8859-1解码的字符串
String username = request.getParameter("username");
//对字符串使用ISO8859-1进行编码,得到最初浏览器使用UTF-8编码的字符串
username = URLEncoder.encode(username, "ISO8859-1");
//将使用UTF-8编码的字符串使用UTF-8进行解码,得到正确的字符串
username = URLDecoder.decode(username, "UTF-8");

3. 使用 String 的构造方法:String(byte[] bytes, String charset) ,对字节数组(bytes)按照指定的字符集(charset)进行解码,返回解码后的字符串,解决乱码问题(推荐使用)。
 //获取姓名
String username = request.getParameter("username");
//使用String的构造方法解决乱码的问题
username = new String(username.getBytes("ISO-8859-1"),"UTF-8");