• JavaWeb Request和Response


    1. RequestResponse

    1.1. Web应用运行机制

    到目前为止,我们已经掌握了Web应用程序的运行机制,现在学习的就是Web应用程序运行机制中很重要的内容 —— RequestResponse

    首先,我们先来看一看RequestResponseWeb应用程序运行时,是怎么样的。

     

    通过上图的Web应用程序运行机制,我们可以知道关于RequestResponse的信息:

    • Web应用程序接收一次请求,就创建一个Request对象和Response对象。
    • 通过Request对象可以在服务器端获取客户端发送的请求数据内容。
    • 通过Response对象可以生成服务器端向客户端响应的数据内容。
    • Request对象和Response对象并不是Web应用程序所创建的,而是由Tomcat服务器创建(JavaEE并没有RequestResponse的实现类)。
    • JavaEE提供了javax.servlet.http包中提供了HttpServletRequestHttpServletResponse接口,这两个接口是继承于javax.servlet包中的ServletRequestServletResponse接口。
    • javax.servlet包中的ServletRequestServletResponse接口是与协议无关的,而javax.servlet.http包中的HttpServletRequestHttpServletResponse接口是与Http协议有关的。

    1.2. 百度百科

    • Request

    Request这个对象不用事先声明,就可以在JSP网页中使用,在编译为Servlet之后,它会转换为javax.servlet.http.HttpServletRequest形态的对象,HttpServletRequest对象是有关于客户端所发出的请求的对象,只要是有关于客户端请求的信息,都可以藉由它来取得,例如请求标头、请求方法、请求参数、客户端IP,客户端浏览器等等信息。

    • Response

    Response对象用于动态响应客户端请示,控制发送给用户的信息,并将动态生成响应。Response对象只提供了一个数据集合cookie,它用于在客户端写入cookie值。若指定的cookie不存在,则创建它。若存在,则将自动进行更新。结果返回给客户端浏览器。

    2. 掌握Response

    2.1. Response概述

    ResponseWeb应用程序用来封装向客户端响应信息的,是Servlet接口的service()方法的一个参数,类型为javax.servlet.http.HttpServletResponse。客户端每次发送请求时,服务器都会创建一个Response对象,并传递给Servlet接口的service()方法,来完成向客户端的响应工作。

    下列是javax.servlet.http.HttpServletResponse常用的API列表:

    Method Summary

     void

    addDateHeader(String name, long date) 
              Adds a response header with the given name and date-value.

     void

    addHeader(String name, String value) 
              Adds a response header with the given name and value.

     void

    sendRedirect(String location) 
              Sends a temporary redirect response to the client using the specified redirect location URL.

     void

    setDateHeader(String name, long date) 
              Sets a response header with the given name and date-value.

     void

    setHeader(String name, String value) 
              Sets a response header with the given name and value.

     void

    setStatus(int sc) 
              Sets the status code for this response.

    下列是javax.servlet.ServletResponse常用的API列表:

    Method Summary

     ServletOutputStream

    getOutputStream() 
              Returns a ServletOutputStream suitable for writing binary data in the response.

     PrintWriter

    getWriter() 
              Returns a PrintWriter object that can send character text to the client.

     void

    setCharacterEncoding(String charset) 
              Sets the character encoding (MIME charset) of the response being sent to the client, for example, to UTF-8.

     void

    setContentType(String type) 
              Sets the content type of the response being sent to the client, if the response has not been committed yet.

    针对HttpServletResponseServletResponse常用的API,我们进行逐一讨论。

    2.2. Response重定向

    在学习Http响应协议时,我们曾做过重定向案例,但那时我们并不清楚其原理,下面我们就讨论一下利用HttpServletResponse来完成重定向的功能。

    • 创建一个Servlet来完成重定向功能。
    public class ResponseServlet1 extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            response.setStatus(302);
            response.setHeader("Location", "/response/index.html");
        }
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    }
    • Web工程的web.xml文件中注册Servlet
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" 
        xmlns="http://java.sun.com/xml/ns/javaee" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
      <servlet>
        <servlet-name>ResponseServlet1</servlet-name>
        <servlet-class>app.java.response.ResponseServlet1</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>ResponseServlet1</servlet-name>
        <url-pattern>/response1</url-pattern>
      </servlet-mapping>    
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
    </web-app>
    • 创建一个HTML页面。
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title>index.html</title>
        <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
        <meta http-equiv="description" content="this is my page">
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      </head>
      <body>
        <h1>Request与Response内容</h1>
      </body>
    </html>
    • Web工程发布到Tomcat服务器,并启动Tomcat服务器。
    • 打开浏览器,在地址栏中输入http://localhost:8080/response/response1。

    通过上述案例,我们可以发现在重定向中,实际上客户端向服务器端发送了两次请求,具体步骤如下:

    • 在浏览器地址输入URL,访问服务器端的Servlet,这是第一次发送的请求。
    • 服务器端接收到客户端的请求后,由Servlet做出处理,重定向到index.html页面,并响应给客户端浏览器。
    • 客户端浏览器接收到服务器端的响应后,再次发送第二次请求。
    • 服务器端接收到客户端的第二次请求后,并响应index.html给客户端浏览器显示。

    其实Response对象提供了sendRedirect(String location)方法,同样可以完成重定向的工作。

    public class ResponseServlet1 extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.sendRedirect("/09_request&response/response/index.html");
        }
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    } 

    2.3. 登录错误案例

    利用Response对象提供了sendRedirect(String location)方法可以完成重定向的功能,实现登录功能中如果错误的案例。具体实现步骤如下:

    • 创建一个用于登录的HTML页面。
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title>login.html</title>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      </head>
      <body>
        <h1>登录案例:登录错误重定向回当前页面</h1>
        <form id="form1" action="/response/response2" method="post">
            用户名:<input type="text" name="username"><br>
            密码:<input type="password" name="password"><br>
            <input type="submit" value="登录">
        </form>
      </body>
    </html>
    • 创建一个Servlet用于处理登录是否成功逻辑。
    public class ResponseServlet2 extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 获取客户端浏览器提交的用户名与密码内容
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            //模拟查询数据库:admin/admin
            if(username.equals("admin")&&password.equals("admin")){
                // 登录成功
                response.setContentType("text/html;charset=utf-8");
                PrintWriter out = response.getWriter();
                out.println("<h1>登录成功</h1>");
            }else{
                // 登录错误
        response.sendRedirect("/response/response/login.html");
            }
        }
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    } 
    • Web工程的web.xml文件中注册Servlet
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" 
        xmlns="http://java.sun.com/xml/ns/javaee" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
      <servlet>
        <servlet-name>ResponseServlet2</servlet-name>
        <servlet-class>app.java.response.ResponseServlet2</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>ResponseServlet2</servlet-name>
        <url-pattern>/response2</url-pattern>
      </servlet-mapping>    
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
    </web-app>
    • Web工程发布到Tomcat服务器,并启动Tomcat服务器。
    • 打开浏览器,在地址栏中输入http://localhost:8080/response/response/login.html

    2.4. 自动刷新案例

    在学习Http响应协议时,我们曾做过自动刷新案例,但那时我们并不清楚其原理,下面我们就讨论一下利用HttpServletResponse来完成自动刷新的功能。

    • 创建一个Servlet用于设置5秒钟后自动刷新页面的功能(自动跳转到登录页面)。
    public class ResponseServlet3 extends HttpServlet {
    
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            response.setHeader("refresh", "5;url=/response/response/login.html");
            response.setContentType("text/html;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.println("当前页面会在5秒钟后自动跳转到登录页面.");
        }
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    } 
    • Web工程的web.xml文件中注册Servlet
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" 
        xmlns="http://java.sun.com/xml/ns/javaee" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
      <servlet>
        <servlet-name>ResponseServlet3</servlet-name>
        <servlet-class>app.java.response.ResponseServlet3</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>ResponseServlet3</servlet-name>
        <url-pattern>/response3</url-pattern>
      </servlet-mapping>    
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
    </web-app>
    • Web工程发布到Tomcat服务器,并启动Tomcat服务器。
    • 打开浏览器,在地址栏中输入http://localhost:8080/response/response3

    在学习HTML技术的时候,我们知道在head标签中有meta标签,该标签同样可以完成自动刷新页面的功能。

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title>refresh.html</title>
        <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
        <meta http-equiv="description" content="this is my page">
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
        <meta http-equiv="refresh" content="5;url=/response/response/login.html">
      </head>
      <body>
        <h1>当前页面会在5秒钟后自动跳转到登录页面.</h1>
      </body>
    </html>

    使用Response对象的setHeader()方法与HTML页面的<meta>标签,同样可以完成页面自动刷新功能,但是两者是有区别的:

    • 使用Response对象的setHeader()方法:refresh信息是显示在响应头信息中。
    • 使用HTML页面的<meta>标签:refresh信息是显示在响应体信息中。

    在上述基础上,完成动态效果的倒计时功能,需要使用javascript技术来完成。

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title>refresh.html</title>
        <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
        <meta http-equiv="description" content="this is my page">
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
        <meta http-equiv="refresh" content="5;url=/response/response/login.html">
      </head>
      <script type="text/javascript">
          var times = 5;
          function init(){
            document.getElementById("times").innerHTML = times;
            times--;
            setTimeout("init()",1000);
        }
      </script>
      <body onload="init();">
        <h1>当前页面会在<span id="times"></span>秒钟后自动跳转到登录页面</h1>
      </body>
    </html>

    2.5. 禁止浏览器缓存案例

    在学习Http响应协议时,我们知道响应协议中有三个头信息可以禁止浏览器本地缓存,分别是Cache-Control、Pragma和Expires。下面我们就通过一个案例来讨论一下。

    • 创建一个Servlet用来向客户端浏览器响应一些数据内容。
    public class ResponseServlet4 extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            PrintWriter out = response.getWriter();
            out.println("当前时间是:"+new Date().toString());
            System.out.println("已经成功地访问了当前Servlet...");
        }
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    } 
    • Web工程的web.xml文件中注册Servlet
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" 
        xmlns="http://java.sun.com/xml/ns/javaee" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
      <servlet>
        <servlet-name>ResponseServlet4</servlet-name>
        <servlet-class>app.java.response.ResponseServlet4</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>ResponseServlet4</servlet-name>
        <url-pattern>/response4</url-pattern>
      </servlet-mapping>    
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
    </web-app>
    • 打开浏览器,选择“工具”->Internet选项”->“浏览历史记录”中的“设置”->“查看文件”,清空所有缓存内容。
    • 在浏览器在地址栏中输入http://localhost:8080/response/response4进行访问。
    • 这时再查看浏览器缓存内容的文件夹,会发现产生了这次访问的缓存内容。
    • 这时再查看控制台信息,会发现打印了相关内容,说明客户端成功访问了服务器端的Servlet
    • 这时再次访问http://localhost:8080/response/response4地址,会发现内容不再变化,也不会产生请求和响应内容。

    由于IE浏览器的本地缓存问题,第二次再次访问相同Servlet拦截路径时,不会再访问服务器端的Servlet,而是访问本地缓存内容。要想每次客户端访问都访问到服务器端的Servlet的话,我们需要禁止浏览器缓存机制。

    • 我们可以通过响应头信息中的三个相关内容来设置禁止浏览器缓存。
    public class ResponseServlet4 extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            response.setContentType("text/html;charset=utf-8");
            //设置响应头信息,禁止浏览器缓存.
            response.setHeader("Cache-Control", "no-cache");
            response.setHeader("Pragma", "no-cache");
            response.setDateHeader("Expires", -1);
            PrintWriter out = response.getWriter();
            out.println("<h1>已经成功地访问了当前Servlet...</h1>");
         }
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    } 
    • 重新发布Web工程到Tomcar服务器,并重启Tomcat服务器。
    • 打开浏览器,清空本地缓存内容,在地址栏中输入相同URL地址。
    • 但是,现在查看浏览器本地缓存文件夹,会发现没有任何缓存内容。
    • 这时再次访问http://localhost:8080/response/response4地址。
    • 而且控制台也会打印相关信息,说明第二次也成功地访问服务器端的Servlet

    2.6. 生成响应体内容

    到目前为止,操作的都是Response对象的响应头信息。而Http协议的响应协议中,除了响应头信息之外,还有响应体,如何利用Response对象向客户端发送响应体呢?可以利用Response对象的getWriter()方法或getOutputStream()方法获取响应输出流,通过响应输出流向客户端进行响应。具体操作步骤如下:

    • 创建一个Servlet用于向客户端响应内容。
    public class ResponseServlet5 extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            PrintWriter out = response.getWriter();
            out.println("<HTML>");
            out.println("  <BODY>");
            out.println("    <H1>Servlet生成的HTML页面.</H1>");
            out.println("  </BODY>");
            out.println("</HTML>");
        }
     public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    }
    • Web工程的web.xml文件中注册Servlet
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" 
        xmlns="http://java.sun.com/xml/ns/javaee" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
      <servlet>
        <servlet-name>ResponseServlet5</servlet-name>
        <servlet-class>app.java.response.ResponseServlet5</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>ResponseServlet5</servlet-name>
        <url-pattern>/response5</url-pattern>
      </servlet-mapping>    
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
    </web-app>
    • 打开浏览器,在地址栏中输入http://localhost:8080/response/response5。
    • 发现在响应内容的页面中,中文显示为乱码。利用Response对象响应前,需要设置响应编码的字符集。
    response.setCharacterEncoding("utf-8");

    需要注意的是,调用Response对象的setCharacterEncoding()方法设置编码字符集时,必须要在调用Response对象的getWriter()方法或getOutputStream()方法之前。

    • 重新打开IE浏览器,在地址栏中输入http://localhost:8080/response/response5。
    • 但是,打开火狐浏览器,在地址栏中输入http://localhost:8080/response/response5。

    虽然在响应之前设置了响应编码字符集,但是使用火狐浏览器显示时,依旧是乱码。

    • 查看火狐浏览器的字符集编码,会发现并不是utf-8编码。

    这说明利用Response对象的setCharacterEncoding()方法虽然可以设置响应体内容的编码字符集,但并不能通过浏览器,浏览器显示默认使用的编码集还是浏览器默认设置的。可以调用Response对象的setContentType()方法设置响应体的文件类型和编码字符集。

    response.setContentType("text/html;charset=utf-8");
    • 重新打开火狐浏览器,在地址栏中输入http://localhost:8080/response/response5乱码问题解决

    需要注意的是,其实在调用Response对象的setContentType()方法时,设置的编码字符集是覆盖了Response对象的setCharacterEncoding()方法设置的编码字符集的。所以,实际上只调用setContentType()方法即可。

    利用Response对象的getWriter()方法或getOutputStream()方法向客户端进行响应的时候,需要注意的问题是:

    • 如果需要手动响应内容的时候,使用getWriter()方法,否则使用getOutputStream()方法。
    • getWriter()方法和getOutputStream()方法之间是互斥的,也就是说只能使用其中一个方法。
    • 使用getWriter()方法或getOutputStream()方法只能向客户端响应体,而不能修改响应行和响应头信息。
    • getWriter()方法或getOutputStream()方法进行响应时,Tomcat服务器会自动关闭响应输出流,不必手动关闭响应输出流。

    2.7. 生成验证码图片

    利用Response对象向客户端进行响应的功能,来完成页面中验证码生成的案例,具体实现步骤如下:

    • 创建一个HTML页面用于显示生成的验证码。
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title>identi.html</title>
        <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
        <meta http-equiv="description" content="this is my page">
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      </head>
      <body>
        <img src="/response/response6">
      </body>
    </html>
    • 创建一个Servlet用于生成页面实现的验证码。
    public class ResponseServlet6 extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            /*
             * 当前Servlet的作用:生成HTML页面显示的验证码(图片).
             *  * Java生成图片内容,使用图形界面技术的awt、swing包.
             */
            /*
             *  1 在内存中创建图片
             *   * 创建图片,需要定义图片的宽度和高度.
             *   * 利用BufferedImage类来创建图片.
             *   * new BufferedImage(宽度, 高度, 图片类型)
             */
            int width = 120;
            int height = 30;
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            /*
             *  2 绘制图片背景颜色
             *   * 通过创建的图片对象的getGraphics()方法,获取画笔.
             *   * 通过画笔对象的setColor()方法设置图片的背景颜色.
             *   * 通过画笔对象的fillRect()方法设置背景颜色填充的面积.
             */
            Graphics2D graphics2d = (Graphics2D)image.getGraphics();
            graphics2d.setColor(Color.GRAY);
            graphics2d.fillRect(0, 0, width, height);
            /*
             *  3 绘制边框
             *   * 通过画笔对象的drawRect()方法绘制边框的面积.
             */
            graphics2d.setColor(Color.BLACK);
            graphics2d.drawRect(1, 1, width - 1, height - 1);
            /*
             *  4 向图片中生成显示的验证码内容
             *   * 通过画笔对象的setFont()方法设置验证码内容的字体、大小等.
             *   * word表示生成验证码的备选文本内容.
             */
            graphics2d.setColor(Color.RED);
            graphics2d.setFont(new Font("新宋体", Font.BOLD, 24));
            String word = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
            // 这段代码用于将生成的验证码内容写入到图片中.
            Random random = new Random();
            int x = 5;
            for (int i = 0; i < 4; i++) {
                // 加入字体旋转 角度为"-30-30"之间.
                int jiaodu = random.nextInt(60) - 30;
                // 转换角度为弧度.
                double theta = jiaodu * Math.PI / 180;
                // 生成下标
                int randomIndex = random.nextInt(word.length());
                // 获取用于验证码显示的字符.
                char c = word.charAt(randomIndex);
                // 将字符写入图片.
                graphics2d.rotate(theta, x, 20);
                graphics2d.drawString(c + "", x, 20);
                graphics2d.rotate(-theta, x, 20);
                // 设置下一个字符出现的水平坐标.
                x += 30;
            }
            // 5 绘制干扰线
            graphics2d.setColor(Color.LIGHT_GRAY);
            for (int i = 0; i < 10; i++) {
                int x1 = random.nextInt(width);
                int x2 = random.nextInt(width);
                int y1 = random.nextInt(height);
                int y2 = random.nextInt(height);
                graphics2d.drawLine(x1, y1, x2, y2);
            }
            // 6 释放内存中的资源
            graphics2d.dispose();
            // 7 将生成的图片,响应到客户端浏览器
            ImageIO.write(image, "jpg", response.getOutputStream());
        }
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    }
    • Web工程的web.xml文件中注册Servlet
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" 
        xmlns="http://java.sun.com/xml/ns/javaee" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
      <servlet>
        <servlet-name>ResponseServlet6</servlet-name>
        <servlet-class>app.java.response.ResponseServlet6</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>ResponseServlet6</servlet-name>
        <url-pattern>/response6</url-pattern>
      </servlet-mapping>    
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
    </web-app>
    • 打开浏览器,在地址栏中输入http://localhost:8080/response/response6

    如果验证码的内容设置为中文的话,只需要将上述代码中的word变量的值设置为常用汉字即可。

    到目前为止,生成的验证码需要每次刷新页面才能重新生成验证码,如何实现鼠标点击验证码图片改变验证码内容呢?具体实现步骤如下:

    • 需要在显示验证码图片的HTML页面中,使用JavaScript代码来实现。
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title>identi.html</title>
        <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
        <meta http-equiv="description" content="this is my page">
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      </head>
      <script type="text/javascript">
          function change(){
            document.getElementById("myimg").src = "/response/response6";
        }
      </script>
      <body>
        <img src="/response/response6" id="myimg" onclick="change();" style="cursor: pointer;">
      </body>
    </html>

    这样实现之后,实际测试发现验证码内容并没有改变。原因是IE浏览器缓存问题,有两种方式来解决:

    • 利用之前掌握的通过设置响应头信息中的内容,禁止浏览器保存缓存内容。
    response.setHeader("Cache-Control", "no-cache");
    response.setHeader("Pragma", "no-cache");
    response.setDateHeader("Expires", -1);
    • 通过设置img图片每次请求的URL地址不同解决这个问题。
    document.getElementById("myimg").src = "/response/response6?"+new Date().getTime();

    3. 掌握Request

    3.1. Request概述

    RequestWeb应用程序用来封装向客户端请求信息的,是Servlet接口的service()方法的一个参数,类型为javax.servlet.http.HttpServletRequest。客户端每次发送请求时,服务器都会创建一个Request对象,并传递给Servlet接口的service()方法,来完成向客户端的请求工作。

    下列是javax.servlet.http. HttpServletRequest常用的API列表:

    Method Summary

     String

    getContextPath() 
              Returns the portion of the request URI that indicates the context of the request.

     long

    getDateHeader(String name) 
              Returns the value of the specified request header as a long value that represents a Date object.

     String

    getHeader(String name) 
              Returns the value of the specified request header as a String.

     Enumeration

    getHeaderNames() 
              Returns an enumeration of all the header names this request contains.

     Enumeration

    getHeaders(String name) 
              Returns all the values of the specified request header as an Enumeration of String objects.

     int

    getIntHeader(String name) 
              Returns the value of the specified request header as an int.

     String

    getMethod() 
              Returns the name of the HTTP method with which this request was made, for example, GET, POST, or PUT.

     String

    getPathInfo() 
              Returns any extra path information associated with the URL the client sent when it made this request.

     String

    getQueryString() 
              Returns the query string that is contained in the request URL after the path.

     String

    getRequestURI() 
              Returns the part of this request's URL from the protocol name up to the query string in the first line of the HTTP request.

     StringBuffer

    getRequestURL() 
              Reconstructs the URL the client used to make the request.

     String

    getServletPath() 
              Returns the part of this request's URL that calls the servlet.

    下列是javax.servlet.ServletRequest常用的API列表:

    Method Summary

     String

    getCharacterEncoding() 
              Returns the name of the character encoding used in the body of this request.

     String

    getContentType() 
              Returns the MIME type of the body of the request, or null if the type is not known.

     String

    getParameter(String name) 
              Returns the value of a request parameter as a String, or null if the parameter does not exist.

     String

    getProtocol() 
              Returns the name and version of the protocol the request uses in the form protocol/majorVersion.minorVersion, for example, HTTP/1.1.

     String

    getRealPath(String path) 
              Deprecated. As of Version 2.1 of the Java Servlet API, use ServletContext.getRealPath(java.lang.String) instead.

     String

    getRemoteAddr() 
              Returns the Internet Protocol (IP) address of the client or last proxy that sent the request.

     String

    getRemoteHost() 
              Returns the fully qualified name of the client or the last proxy that sent the request.

     int

    getRemotePort() 
              Returns the Internet Protocol (IP) source port of the client or last proxy that sent the request.

     RequestDispatcher

    getRequestDispatcher(String path) 
              Returns a RequestDispatcher object that acts as a wrapper for the resource located at the given path.

     String

    getServerName() 
              Returns the host name of the server to which the request was sent.

     int

    getServerPort() 
              Returns the port number to which the request was sent.

    3.2. 获取请求行信息

    在学习Http请求协议时,知道Http请求协议包含请求行、请求头及请求体三个部分。首先我们来讨论请求协议中的请求行,请求行包含请求方式、请求链接及GET方式的参数和Http请求协议版本。

    GET方式的请求行内容:

    GET /http/01_get.html?username=zhangsan HTTP/1.1

    POST方式的请求行内容

    POST /http/02_post.html HTTP/1.1

    获取Http请求协议中请求行的内容,可以通过以下方法:

    • getMethod()方法:获取Http请求协议的请求方式,例如GETPOST等。
    • getRequestRUI()方法:获取Http请求协议的资源路径。
    • getProtocol()方法:获取Http请求协议的协议版本。
    • getQueryString()方法:获取Http请求协议GET方式的请求参数。

    下面我们通过代码实现,来验证一下上述方法的功能:

    • 创建一个Servlet用于打印上述方法。
    public class RequestServlet1 extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 获取请求方式,GET或POST
            System.out.println("请求方式:"+request.getMethod());
            // 获取请求协议
            System.out.println("请求协议:"+request.getProtocol());
            // 获取请求路径URI和URL
            System.out.println("请求路径URI:"+request.getRequestURI());
            System.out.println("请求路径URL:"+request.getRequestURL());
            // 获取请求参数
            System.out.println("请求参数:"+request.getQueryString());
        }
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    }
    • Web工程的web.xml文件中注册Servlet
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" 
        xmlns="http://java.sun.com/xml/ns/javaee" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
      <servlet>
        <servlet-name>RequestServlet1</servlet-name>
        <servlet-class>app.java.request.RequestServlet1</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>RequestServlet1</servlet-name>
        <url-pattern>/request1</url-pattern>
      </servlet-mapping>    
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
    </web-app>
    • Web工程发布到Tomcat服务器,并启动Tomcat服务器。
    • 打开浏览器,在地址栏中输入http://localhost:8080/response/request1。
    • 在地址后增加参数http://localhost:8080/response/request1?username=zhangsan

    除上述方法可以获取请求协议的请求行中信息外,我们还可以通过getRemoteAddr()方法获取客户端IP地址和getContextPath()方法获取Web工程虚拟目录名称。

    // 获取客户端IP地址
    System.out.println("客户端IP地址:"+request.getRemoteAddr());
    // 获取Web工程虚拟目录名称
    System.out.println("Web工程虚拟目录名称:"+request.getContextPath());

    3.3. 获取请求头信息

    下面我们来讨论Http请求协议中的请求头信息,下面是一个Http请求协议的请求头信息内容:

    Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, */*
    Referer: http://localhost:8080/07_http/01_get.html
    Accept-Language: zh-CN
    User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; qdesk 2.5.1277.202; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3)
    Accept-Encoding: gzip, deflate
    If-Modified-Since: Thu, 11 Sep 2014 02:44:35 GMT
    If-None-Match: W/"679-1410403475587"
    Host: localhost:8080
    Connection: Keep-Alive

    我们之前曾利用请求头信息中的Referer完成了防盗链案例,下面我们来回顾一下。

    • 首先我们创建一个网站的主页面。
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title>index.html</title>
        <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="this is my page">
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      </head>
      <body>
        <a href="refererServlet">特价商品</a>
      </body>
    </html>
    • 然后我们完成服务器端Servlet防止盗链的逻辑内容。
    public class RefererServlet extends HttpServlet {
        // 处理GET方式的请求
        public void doGet(HttpServletRequest request, HttpServletResponse response)  throws ServletException, IOException {
            // 判断请求中referer是否存在,有效  --- 防止盗链
            String referer = request.getHeader("referer");
            if(referer!=null && referer.equals("http://localhost:8080/http/index.html")){
                // 有效
                response.setContentType("text/html;charset=utf-8");
                response.getWriter().println("笔记本1000元");
            }else{
                // 无效
                response.setContentType("text/html;charset=utf-8");
                response.getWriter().println("盗链真无耻!");
            }
        }
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    }
    • 将工程发布到Tomcat服务器,启动Tomcat服务器。
    • 打开浏览器,输入http://localhost:8080/http/index.html地址。
    • 在网站主页面中,点击“特价商品”链接,会返回正确内容。
    • 如果在浏览器地址栏中直接输入http://localhost:8080/http/refererServlet链接。
    • 由于直接输入链接地址的请求协议中,没有referer信息,所以会显示报错信息。

    在请求头信息中,除了Referer可以实现防盗链,还可以利用User-Agent获取客户端浏览器相关信息。

    • 创建一个Servlet用于打印User-Agent请求头信息。
    public class RequestServlet2 extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("浏览器是:"+request.getHeader("user-agent"));
        }
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    }
    • Web工程的web.xml文件中注册Servlet
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" 
        xmlns="http://java.sun.com/xml/ns/javaee" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
      <servlet>
        <servlet-name>RequestServlet2</servlet-name>
        <servlet-class>app.java.request.RequestServlet2</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>RequestServlet2</servlet-name>
        <url-pattern>/request2</url-pattern>
      </servlet-mapping>    
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
    </web-app>
    • Web工程发布到Tomcat服务器,并启动Tomcat服务器。
    • 打开浏览器,在地址栏中输入http://localhost:8080/response/request2。

    我们还可以利用getHeaderNames()方法打印所有请求头信息。

    Enumeration<String> names = request.getHeaderNames();
    while (names.hasMoreElements()) {
        String name = (String) names.nextElement();
        System.out.println(name+":"+request.getHeader(name));
    }

    3.4. 获取请求参数

    由于Http请求协议中的请求方式常用的有两种GETPOSTGET方式的请求参数在请求行中的资源路径后面,POST方式的请求参数在请求体中。

    在服务器端Servlet获取请求参数共有以下四种方法:

    • getParameter()方法:获取指定参数名的参数值(单个值)。
    • getParameterValues()方法:获取指定参数名的参数值(多个值)。
    • getParameterNames()方法:获取所有参数的参数名。
    • getParameterMap()方法:获取参数以name=value形式存储在Map集合中,将Map集合返回。

    首先,我们来讨论POST方式的请求参数,在服务器端如何获取:

    • 创建一个HTML页面编写form表单用于提交客户端数据。
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title>POST方式请求.html</title>
        <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
        <meta http-equiv="description" content="this is my page">
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      </head>
      <body>
        <h1>最复杂form表单</h1>
        <form method="post" action="/response/request3">
            用户名 <input type="text" name="username" /><br/>
            密码 <input type="password" name="password" /><br/>
            性别 <input type="radio" name="gender" value="男" /><input type="radio" name="gender" value="女" /><br/>
            爱好 <input type="checkbox" name="hobby" value="体育" />体育
            <input type="checkbox" name="hobby" value="音乐" />音乐
            <input type="checkbox" name="hobby" value="读书" />读书<br/>
            城市 <select name="city">
                <option value="北京">北京</option>
                <option value="上海">上海</option>
                <option value="广州">广州</option>
            </select> <br/>
            个人简介 <textarea rows="5" cols="60" name="introduce"></textarea><br/>
            <input type="submit" value="提交" />
        </form>
      </body>
    </html>
    • 创建一个Servlet用于接收客户端浏览器请求参数。
    public class RequestServlet3 extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            
        }
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            String gender = request.getParameter("gender");
            String hobby = request.getParameter("hobby");
            String city = request.getParameter("city");
            String introduce = request.getParameter("introduce");
            
            System.out.println("姓名:" + username);
            System.out.println("密码:" + password);
            System.out.println("性别:" + gender);
            System.out.println("爱好:" + hobby);
            System.out.println("城市:" + city);
            System.out.println("个人介绍:" + introduce);
        }
    }
    • Web工程的web.xml文件中注册Servlet
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" 
        xmlns="http://java.sun.com/xml/ns/javaee" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
      <servlet>
        <servlet-name>RequestServlet3</servlet-name>
        <servlet-class>app.java.request.RequestServlet3</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>RequestServlet3</servlet-name>
        <url-pattern>/request3</url-pattern>
      </servlet-mapping>    
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
    </web-app>
    • Web工程发布到Tomcat服务器,并启动Tomcat服务器。
    • 打开浏览器,在地址栏中输入http://localhost:8080/response/request/ POST.html。
    • HTML页面中,输入相关信息后,点击“提交”按钮。
    • 在控制台打印信息时,发现都是乱码。POST方式解决乱码,需要使用setCharacterEncoding()方法。
    request.setCharacterEncoding("utf-8");
    • 在查看控制台打印信息,这时的信息已经显示正常中文了。

    获取页面请求的数据信息中,“爱好”内容为多选框,但实际上只打印其中一项。需要调用getParameterValues()方法来解决。

    String[] hobby = request.getParameterValues("hobby");
    System.out.println("爱好:" + Arrays.toString(hobby));

    下面,我们来讨论一下GET方式的请求参数,在服务器端如何获取:

    • HTML页面中表单的Method改为“GET”。
    • 编写Servlet用于接收客户端浏览器请求参数。
    public class RequestServlet3 extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            String gender = request.getParameter("gender");
            String hobby = request.getParameter("hobby");
            String city = request.getParameter("city");
            String introduce = request.getParameter("introduce");
            
            System.out.println("姓名:" + username);
            System.out.println("密码:" + password);
            System.out.println("性别:" + gender);
            System.out.println("爱好:" + hobby);
            System.out.println("城市:" + city);
            System.out.println("个人介绍:" + introduce);
        }
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            
        }
    }
    • 打开浏览器,在地址栏中输入http://localhost:8080/response/request/ POST.html。
    • 在控制台打印信息时,发现都是乱码。

    POST方式解决乱码,有两种方式:

    • 修改Tomcat服务器安装目录的conf目录中的server.xml文件。
    <Connector port="8080" protocol="HTTP/1.1"
             connectionTimeout="20000"
             redirectPort="8443"
    RUIEncoding="utf-8" />
    • 通过代码进行逆向解码(以用户名为例)。
    username = URLEncoder.encode(username, "ISO-8859-1");
    username = URLDecoder.decode(username, "utf-8");

    或者也可以利用下面这种方式来解决逆向解码。

    gender = new String(gender.getBytes("ISO-8859-1"), "utf-8");

    当然,获取到客户端浏览器发送的请求参数内容后,还可以完成非空的验证功能。

    if (username != null && username.length() > 0) {
        System.out.println("username 有效.");
    }

    3.5. 请求转发数据

    之前我们完成了重定向的功能,现在要完成请求转发的功能。这两个功能经常会放在一起比较:

    • 重定向:
      • 发生两次请求,两次响应。
      • 重定向在客户端浏览器可以查看到。
      • 重定向无法携带数据。
      • 重定向中的第二次请求资源路径来源于客户端。
    • 请求转发:
      • 发生一次请求,一次响应。
      • 请求转发在客户端浏览器无法查看。
      • 请求转发可以携带数据。
      • 请求转发的第二次请求资源路径来源于服务器端内部。

    下面我们来实现请求转发的功能,再比较请求转发与重定向的区别。

    • 创建一个Servlet用于接收客户端请求。
    public class RequestServlet4 extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("这是Servlet4...");
            // 获取请求转发对象
            RequestDispatcher dispatcher = request.getRequestDispatcher("/request5");
            // 利用forward()方法进行请求转发.
            dispatcher.forward(request, response);
        }
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    }
    • 再创建一个Servlet用于接收请求转发的请求。
    public class RequestServlet5 extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("这是Servlet5...");
        }
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    }
    • Web工程的web.xml文件中注册Servlet
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" 
        xmlns="http://java.sun.com/xml/ns/javaee" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
      <servlet>
        <servlet-name>RequestServlet4</servlet-name>
        <servlet-class>app.java.request.RequestServlet4</servlet-class>
      </servlet>
      <servlet>
        <servlet-name>RequestServlet5</servlet-name>
        <servlet-class>app.java.request.RequestServlet5</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>RequestServlet4</servlet-name>
    <url-pattern>/request4</url-pattern>
      </servlet-mapping>    
      <servlet-mapping>
        <servlet-name>RequestServlet5</servlet-name>
    <url-pattern>/request5</url-pattern>
      </servlet-mapping>    
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
    </web-app>
    • 打开浏览器,在地址栏输入http://localhost:8080/response/request4
    • 通过Request对象的setAttribute()方法和getAttribute()方法来携带数据。

    RequestServlet4

    // 向Request对象中,存储数据内容.
    request.setAttribute("name", "longestory");

    RequestServlet5

    String name = (String)request.getAttribute("name");
    System.out.println("获取的name为:"+name);
    • 打开浏览器,在地址栏输入http://localhost:8080/response/request4
    • 而从客户端浏览器通过HttpWatch工具查看请求时,只发生了一次请求。
  • 相关阅读:
    【JavaScript从入门到精通】第二课 初探JavaScript魅力-02
    【JavaScript从入门到精通】第一课 初探JavaScript魅力-01
    程序员技术周刊
    【Geek软技能】程序员,为什么写不好一份简历?
    众里寻他千百度?No!这项技术只需走两步就能“看穿”你!
    PornHub 正式发布 AI自动标注色情演员引擎
    9 月份 GitHub 上最火的 JavaScript 开源项目!
    累了吗?来挑战一下算法趣题,看看自己是哪个段位的程序猿吧!
    Chrome 开发者控制台中,你可能意想不到的功能
    现代软件工程 作业 最后一周总结
  • 原文地址:https://www.cnblogs.com/aaron911/p/7832055.html
Copyright © 2020-2023  润新知