Web基础知识(5)- Java Servlet (五) | 静态资源、HttpServletRequest、HttpServletResponse、请求转发和重定向


1. 处理静态资源的Servlet

    通过链接来访问应用内的资源文件,例如 *.jpg、*.html、*.js 这类的静态文件。这就需要用到 DefaultServlet,它在tomat 的安装目录下的 conf/web.xml 中的定义,如下:

 1         <servlet>
 2             <servlet-name>default</servlet-name>
 3             <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
 4             <init-param>
 5                 <param-name>debug</param-name>
 6                 <param-value>0</param-value>
 7             </init-param>
 8             <init-param>
 9                 <param-name>listings</param-name>
10                 <param-value>false</param-value>
11             </init-param>
12             <load-on-startup>1</load-on-startup>
13         </servlet>

 

    修改 webapp/WEB-INF/web.xml,添加如下配置:

 1         <web-app>
 2 
 3             <!--  处理 html/js/css/jpg/png 文件 -->
 4             <servlet-mapping>
 5                 <servlet-name>default</servlet-name>
 6                 <url-pattern>*.html</url-pattern>
 7                 <url-pattern>*.js</url-pattern>
 8                 <url-pattern>*.css</url-pattern>
 9                 <url-pattern>*.jpg</url-pattern>
10                 <url-pattern>*.png</url-pattern>
11             </servlet-mapping>
12 
13         </web-app>


    把 *.html、*.js、*.css、*.jpg 和 *.png 等文件放到 webapp 目录或其子目录下,即可通过浏览器访问这些静态资源。

2. HttpServletRequest接口

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

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


    1) 获取请求行信息

        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 地址。

 

    2) 获取请求头信息

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

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


    3) 获取 form 表单的数据

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

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


        在 webapp 目录或子目录下,新建 test.html 文件:

 1             <!DOCTYPE html>
 2             <html>
 3             <head>
 4                 <title>Test Form</title>
 5             </head>
 6                 <body>
 7                     <h3>Test Form</h3>
 8                     <p>&nbsp;</p>
 9 
10                     <form action="/test" method="post">
11                         <p>Username: <input type="text" name="username" /></p>
12                         <p>Email: <input type="text" name="email" /></p>
13                         <p><input type="submit" value="Submit" /></p>
14                     </form>
15 
16                 </body>
17             </html>

 

    4) 中文乱码问题

        (1) POST 请求

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

            解决方案:在获取请求参数之前设置 request 缓冲区字符集为 utf-8 ,代码如下。

                // 修改request缓冲区的字符集为UTF-8
                request.setCharacterEncoding("utf-8");
                String username = request.getParameter("username");

        (2) GET 请求

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

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

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

                a) 修改 tomcat/conf/server.xml 中的配置,代码如下。

                    <Connector port="80" protocol="HTTP/1.1"
                            connectionTimeout="20000"
                            redirectPort="8443" URIEncoding="UTF-8"/>

                b) 使用 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");

                c) 使用 String 的构造方法:String(byte[] bytes, String charset) ,对字节数组(bytes)按照指定的字符集(charset)进行解码,返回解码后的字符串,解决乱码问题(推荐使用)。

                    String username = request.getParameter("username");
                    username = new String(username.getBytes("ISO-8859-1"),"UTF-8");

3. HttpServletResponse接口

    在 Servlet API 中,定义了一个 HttpServletResponse 接口,它继承自 ServletResponse 接口。HttpServletResponse 对象专门用来封装 HTTP 响应消息,简称 response 对象。

    Servlet 容器会针对每次请求创建一个 response 对象,并把它作为参数传递给 Servlet 的 service 方法。Servlet 处理请求后,会将响应信息封装到 response 对象中,并由容器解析后返回给客户端。

    由于 HTTP 响应消息由响应行、响应头、消息体三部分组成,所以 HttpServletResponse 接口中定义了向客户端发送响应状态码、响应头、响应体的方法,下面我们将针对这些方法进行介绍。

    1) 响应行相关的方法

        当 Servlet 返回响应消息时,需要在响应消息中设置状态码。因此,HttpServletResponse 接口定义了发送状态码的方法,如下表。

方法 描述
void setStatus(int status) 用于设置 HTTP 响应消息的状态码,并生成响应状态行。
void sendError(int sc) 用于发送表示错误信息的状态码。

 

    2) 响应头相关的方法

        HttpServletResponse 接口中定义了一系列设置 HTTP 响应头字段的方法,如下表所示。

方法 描述
void addHeader(String name,String value) 用于增加响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值。
void setHeader (String name,String value) 用于设置响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值。
void addIntHeader(String name,int value) 用于增加值为 int 类型的响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值,类型为 int。
void setIntHeader(String name, int value) 用于设置值为 int 类型的响应头字段,其中,参数 name 用于指定响应头字段的名称,参数 value 用于指定响应头字段的值,类型为 int。
void setContentType(String type) 用于设置 Servlet 输出内容的 MIME 类型以及编码格式。
void setCharacterEncoding(String charset)  用于设置输出内容使用的字符编码。

 

    3) 响应体相关的方法

        由于在 HTTP 响应消息中,大量的数据都是通过响应消息体传递的。因此 ServletResponse 遵循以 I/O 流传递大量数据的设计理念,在发送响应消息体时,定义了两个与输出流相关的方法。

方法 描述
ServletOutputStream getOutputStream() 用于获取字节输出流对象。
PrintWriter getWriter() 用于获取字符输出流对象。


        注意:getOutputStream() 和 getWriter() 方法互相排斥,不可同时使用,否则会发生 IllegalStateException 异常。

 

    4) 中文乱码问题

        (1) 使用字节流输出中文

            字节流输出格式:

                ServletOutputStream outptuStream = response.getOutputStream();
                outputStream.write(“中文乱码问题 www.test.com”.getBytes());

            乱码原因:字节流输出中文是否出现乱码,取决于中文转成字节数组时与浏览器打开时采用的字符集是否一致。若两者保持一致,则不会出现乱码问题,若不一致就会出现乱码问题。

            解决方案:将中文转成字节数组时和浏览器默认采用的字符集保持一致即可,代码如下。

                response.setHeader("Content-Type", "text/html;charset=UTF-8");
                // 获取字节输出流
                OutputStream os = response.getOutputStream();
                byte[] str = "中文乱码问题 www.test.com".getBytes("UTF-8");
                // 输出中文
                os.write(str);

        (2)使用字符流输出中文

            乱码原因:通过字符流输出的内容是存放在 response 缓冲区的,response 缓冲区的默认字符集是 ISO-8859-1,该字符集不支持中文。

            解决方案:将 response 缓冲区和浏览器采用的字符集保持一致即可,有如下 2 种的方式。

            第一种方式:

                // 设置response缓冲区的编码
                response.setCharacterEncoding("UTF-8");
                // 设置浏览器打开文件所采用的编码
                response.setHeader("Content-Type", "text/html;charset=UTF-8");
                // 输出中文
                response.getWriter().write("中文乱码问题 www.test.com");

            第二种方式:

                response.setContentType("text/html;charset=UTF-8");
                response.getWriter().write("中文乱码问题 www.test.com");

4. Servlet请求转发

    Web 应用在处理客户端的请求时,经常需要多个 Web 资源共同协作才能生成响应结果。但由于 Serlvet 对象无法直接调用其他 Servlet 的 service() 方法,所以 Servlet 规范提供了 2 种解决方案:

        请求转发
        请求包含(了解即可)

    1)请求转发

        请求转发属于服务器行为。容器接收请求后,Servlet 会先对请求做一些预处理,然后将请求传递给其他 Web 资源,来完成包括生成响应在内的后续工作。

        (1)RequestDispatcher 接口

            javax.servlet 包中定义了一个 RequestDispatcher 接口,RequestDispatcher 对象由 Servlet 容器创建,用于封装由路径所标识的 Web 资源。利用 RequestDispatcher 对象可以把请求转发给其他的 Web 资源。

            Servlet 可以通过 2 种方式获得 RequestDispatcher 对象:

                调用 ServletContext 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,必须为绝对路径;
                调用 ServletRequest 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,可以为绝对路径,也可以为相对路径。

                绝对路径是指以符号“/”开头的路径,“/”表示当前 Web 应用的根目录。相对路径是指相对当前 Web 资源的路径,不以符号“/”开头。

            RequestDispatcher 接口中提供了以下方法。

方法 描述
void forward(ServletRequest request,ServletResponse response) 用于将请求转发给另一个 Web 资源。该方法必须在响应提交给客户端之前被调用,否则将抛出 IllegalStateException 异常
void include(ServletRequest request,ServletResponse response) 用于将其他的资源作为当前响应内容包含进来

                                                                     
            使用方式:

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

                    request.getRequestDispatcher("/next").forward(request, response);
                }

        (2)请求转发的工作原理

            在 Servlet 中,通常使用 forward() 方法将当前请求转发给其他的 Web 资源进行处理。请求转发的工作原理如下图所示。

            请求转发具有以下特点:

                a) 请求转发不支持跨域访问,只能跳转到当前应用中的资源。
                b) 请求转发之后,浏览器地址栏中的 URL 不会发生变化,因此浏览器不知道在服务器内部发生了转发行为,更无法得知转发的次数。
                c) 参与请求转发的 Web 资源之间共享同一 request 对象和 response 对象。
                d) 由于 forward() 方法会先清空 response 缓冲区,因此只有转发到最后一个 Web 资源时,生成的响应才会被发送到客户端。

    2) request 域对象

        request 是 Servlet 的三大域对象之一,它需要与请求转发配合使用,才可以实现动态资源间的数据传递。

        在 ServletRequest 接口 (HttpServletRequest 继承了 ServletRequest)中定义了一系列操作属性的方法,如下表。

方法 描述
void setAttribute(String name, Object o) 将 Java 对象与属性名绑定,并将它作为一个属性存放到 request 对象中。参数 name 为属性名,参数 object 为属性值。
Object getAttribute(String name)  根据属性名 name,返回 request 中对应的属性值。
void removeAttribute(String name) 用于移除 request 对象中指定的属性。
Enumeration getAttributeNames() 用于返回 request 对象中的所有属性名的枚举集合。


        Context 域对象和 request 域对象对比,具有以下 4 点差异:

        (1) 生命周期不同

            Context 域对象的生命周期从容器启动开始,到容器关闭或者 Web 应用被移除时结束;
            request 域对象的生命周期从客户端向容器发送请求开始,到对这次请求做出响应后结束。

        (2) 作用域不同

            Context 域对象对整个 Web 应用内的所有Servlet都有效;
            request 域对象只对本次请求涉及的 Servlet 有效。

        (3) Web 应用中数量不同

            整个 Web 应用中只有一个 Context 域对象;
            由于 Servlet 能处理多个请求,因此 Web 应用中的每个 Servlet 实例都可以有多个 request 域对象。

        (4) 实现数据共享的方式不同

            Context 域对象可以独立完成动态资源之间的数据共享;
            Request 域对象需要与请求转发配合使用才能实现动态资源之间的数据共享。

5. Servlet重定向

    重定向属于客户端行为。服务器在收到客户端请求后,会通知客户端浏览器重新向另外一个 URL 发送请求,这称为请求重定向。它本质上是两次 HTTP 请求,对应两个 request 对象和两个 response 对象。

    1) 重定向的工作流程

        (1) 用户在浏览器中输入 URL,请求访问服务器端的 Web 资源。
        (2) 服务器端的 Web 资源返回一个状态码为 302 的响应信息,该响应的含义为:通知浏览器再次发送请求,访问另一个 Web 资源(在响应信息中提供了另一个资源的 URL)。
        (3) 当浏览器接收到响应后,立即自动访问另一个指定的 Web 资源。
        (4) 另一 Web 资源将请求处理完成后,由容器把响应信息返回给浏览器进行展示。

    2) 转发和重定向的区别

        转发和重定向都能实现页面的跳转,但是两者也存在以下区别。

区别 转发 重定向
浏览器地址栏 URL 是否发生改变
是否支持跨域跳转
请求与响应的次数 一次请求和一次响应 两次请求和两次响应
是否共享 request 和 response 对象
是否能通过 request 域对象传递数据
速度 相对快 相对慢
行为类型 服务器行为 客户端行为

 

    3) response.sendRedirect()

        HttpServletResponse 接口中的 sendRedirect() 方法用于实现重定向。

          void sendRedirect(String location);

    向浏览器返回状态码为 302 的响应结果,让浏览器访问新的 URL。若指定的 URL 是相对路径,Servlet 容器会将相对路径转换为绝对路径。参数 location 表示重定向的URL。

        使用方式:

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

                response.sendRedirect("/next");
            }

示例:

 1     package com.example;
 2 
 3     import java.io.IOException;
 4     import java.io.PrintWriter;
 5     import java.io.OutputStream;
 6     import java.util.Enumeration;
 7 
 8     import javax.servlet.ServletConfig;
 9     import javax.servlet.ServletException;
10     import javax.servlet.http.HttpServlet;
11     import javax.servlet.http.HttpServletRequest;
12     import javax.servlet.http.HttpServletResponse;
13 
14     public class TestServlet extends HttpServlet {
15 
16         protected void doGet(HttpServletRequest request, HttpServletResponse response)
17                 throws ServletException, IOException {
18 
19             response.setContentType("text/html;charset=UTF-8");
20             PrintWriter writer = response.getWriter();
21 
22             ServletConfig config = getServletConfig();
23             writer.write(config.getServletName() + " -> doGet()<br/>");
24             writer.println("<br/>");
25 
26             writer.println("Method: " + request.getMethod() + "<br/>" +
27                     "Remote IP: " + request.getRemoteAddr() + "<br/>" +
28                     "Context Path: " + request.getContextPath() + "<br/>" +
29                     "URI: " + request.getRequestURI() + "<br/>" +
30                     "Query String: " + request.getQueryString() + "<br/>" +
31                     "Servlet Path: " + request.getServletPath() + "<br/>" +
32                     "Remote Host: " + request.getRemoteHost() + "<br/>"
33             );
34             writer.println("<br/>");
35 
36             Enumeration<String> headers = request.getHeaderNames();
37             while (headers.hasMoreElements()) {
38                 String value = request.getHeader(headers.nextElement());
39                 writer.write(headers.nextElement() + ":" + value + "<br/>");
40             }
41             writer.println("<br/>");
42             writer.close();
43 
44         }
45 
46         protected void doPost(HttpServletRequest request, HttpServletResponse response)
47                 throws ServletException, IOException {
48             request.setCharacterEncoding("utf-8");
49             response.setContentType("text/html;charset=UTF-8");
50 
51             String username = request.getParameter("username");
52             String email = request.getParameter("email");
53 
54             String str = "Username(用户名):" + username + "<br/>" + "Email(电子邮件):" + email + "<br/>";
55             OutputStream os = response.getOutputStream();
56             os.write(str.getBytes("UTF-8"));
57         }
58     }

 

posted @ 2022-03-16 12:55  垄山小站  阅读(995)  评论(0)    收藏  举报