day06 Request Response

Author:相忠良
Email: ugoood@163.com
起始于:April 18, 2018
最后更新日期:April 22, 2018javascript

声明:本笔记依据传智播客方立勋老师 Java Web 的授课视频内容记录而成,中间加入了本身的理解。本笔记目的是强化本身学习所用。如有疏漏或不当之处,请在评论区指出。谢谢。
涉及的图片,文档写完后,一次性更新。html

day05 Request Response

Web服务器收到客户端的http请求,会针对每一次请求,分别建立一个用于表明请求的 request 对象,和表明响应的 response 对象:java

  • 要想获取客户机提交过来的数据,只需找 request 对象;
  • 要向客户机输出数据,只需找 response 对象就好了。

1. HttpServletResponse 简介

HttpServletResponse响应,它封装了向客户机 发送数据、发送响应头和发送响应状态码 的方法。例如:web

setStatus(int)
setHeader(String, String)
getWriter()
getOutputStream()

1.1 Response 的 OutputStream 输出中文的问题

程序已什么码表输出了,程序就必定要控制浏览器以什么码表打开。chrome

1.用 响应头 的方式控制浏览器的码表,以下:apache

// servlet 中用 OutputStream 输出中文的问题
public class ResponseDemo1 extends HttpServlet {

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

        // 经过response对象为浏览器指定码表
        // 程序已什么码表输出了,程序就必定要控制浏览器以什么码表打开
        response.setHeader("Content-type", "text/html;charset=UTF-8");

        String data = "中国";
        OutputStream out = response.getOutputStream();
        out.write(data.getBytes("UTF-8")); // 以该码表输出
    }
}

2.用 html 的<meta>方式控制浏览器的码表,此方法 没有 向浏览器发送响应头。以下(我试验失败):windows

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

        String data = "中国";
        OutputStream out = response.getOutputStream();

        out.write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'>"
                .getBytes());
        out.write(data.getBytes("UTF-8"));
}

1.2 Response 的 Writer 输出中文(只能写字符串)

public class ResponseDemo2 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 设置 response 码表
        response.setCharacterEncoding("UTF-8");
        // 设置浏览器应查的码表
        response.setHeader("content-type", "text/html;charset=UTF-8");

        String data = "中国";
        PrintWriter out = response.getWriter();
        out.write(data);
    }
}

上述代码设置浏览器码表,也可这样写:response.setContentType("text/html;charset=UTF-8");,且这句话也修改了 response 的码表。设计模式

2. response 实现文件下载

准备:项目WebRoot目录下创建download目录,并在该目录中预先放入1.jpg日本妞.jpg文件。
分别下载这两个文件的代码:
注意:若是文件名为中文,则文件名需通过 url 编码,下面代码中有体现:浏览器

// 如下载方式打开
public class ResponseDemo3 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

//      String path = this.getServletContext().getRealPath("/download/1.jpg");
        String path = this.getServletContext().getRealPath("/download/日本妞.jpg");
        String filename = path.substring(path.lastIndexOf("\\") + 1);

        // 发送响应头,通知浏览器如下载方式打开,且文件名是 filename
//      response.setHeader("content-disposition", "attachment;filename="
//              + filename);

        // 若是文件名为中文,则文件名需通过 url 编码
        response.setHeader("content-disposition", "attachment;filename="
                + URLEncoder.encode(filename, "UTF-8"));

        InputStream in = null;
        OutputStream out = null;
        try {
            in = new FileInputStream(path);
            int len = 0;
            byte[] buffer = new byte[1024];
            out = response.getOutputStream();
            while ((len = in.read(buffer)) > 0) {
                out.write(buffer, 0, len);
            }
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

3. response 输出随机认证码图片 - 涉及了控制浏览器不留缓存 - 经常使用汉字码表

刷新按钮做用:无论服务器有没有缓存,都向服务器发请求。就是把上一次的申请再干一次。
下面代码涉及了 控制浏览器不留缓存缓存

// 验证码, 谷歌浏览器不支持,IE支持本程序
public class ResponseDemo4 extends HttpServlet {

    // ctrl + shift + x 小写变大写
    public static final int WIDTH = 120;
    public static final int HEIGHT = 35;

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

        BufferedImage image = new BufferedImage(WIDTH, HEIGHT,
                BufferedImage.TYPE_INT_RGB);

        Graphics g = image.getGraphics();

        // 1. 设置背景色
        setBackGround(g);

        // 2. 设置边框
        setBorder(g);

        // 3. 画干扰线
        drawRandomLine(g);

        // 4. 写随机数
        drawRandomNum((Graphics2D) g);

        // 5. 图形写给浏览器
        response.setContentType("image/jpeg");
        // 发头控制浏览器不要缓存
        response.setDateHeader("expries", -1);
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");

        ImageIO.write(image, "jpg", response.getOutputStream());
    }

    private void drawRandomNum(Graphics2D g) {
        g.setColor(Color.RED);
        g.setFont(new Font("宋体", Font.BOLD, 20));

        // unicode 汉字码表范围 [\u4e00-\u9fa5]
        // 经常使用汉字
        String base = "\u4e00\u4e86\u662f\u6211\u4e0d\u5728\u4eba\u4eec\u6709\u6765\u4ed6\u8fd9\u4e0a\u7740\u4e2a\u5730\u5230\u5927\u91cc\u8bf4\u5c31\u53bb\u5b50\u5f97\u4e5f\u548c\u90a3\u8981\u4e0b\u770b\u5929\u65f6\u8fc7\u51fa\u5c0f\u4e48\u8d77\u4f60\u90fd\u628a\u597d\u8fd8\u591a\u6ca1\u4e3a\u53c8\u53ef\u5bb6\u5b66\u53ea\u4ee5\u4e3b\u4f1a\u6837\u5e74\u60f3\u751f\u540c\u8001\u4e2d\u5341\u4ece\u81ea\u9762\u524d\u5934\u9053\u5b83\u540e\u7136\u8d70\u5f88\u50cf\u89c1\u4e24\u7528\u5979\u56fd\u52a8\u8fdb\u6210\u56de\u4ec0\u8fb9\u4f5c\u5bf9\u5f00\u800c\u5df1\u4e9b\u73b0\u5c71\u6c11\u5019\u7ecf\u53d1\u5de5\u5411\u4e8b\u547d\u7ed9\u957f\u6c34\u51e0\u4e49\u4e09\u58f0\u4e8e\u9ad8\u624b\u77e5\u7406\u773c\u5fd7\u70b9\u5fc3\u6218\u4e8c\u95ee\u4f46\u8eab\u65b9\u5b9e\u5403\u505a\u53eb\u5f53\u4f4f\u542c\u9769\u6253\u5462\u771f\u5168\u624d\u56db\u5df2\u6240\u654c\u4e4b\u6700\u5149\u4ea7\u60c5\u8def\u5206\u603b\u6761\u767d\u8bdd\u4e1c\u5e2d\u6b21\u4eb2\u5982\u88ab\u82b1\u53e3\u653e\u513f\u5e38\u6c14\u4e94\u7b2c\u4f7f\u5199\u519b\u5427\u6587\u8fd0\u518d\u679c\u600e\u5b9a\u8bb8\u5feb\u660e\u884c\u56e0\u522b\u98de\u5916\u6811\u7269\u6d3b\u90e8\u95e8\u65e0\u5f80\u8239\u671b\u65b0\u5e26\u961f\u5148\u529b\u5b8c\u5374\u7ad9\u4ee3\u5458\u673a\u66f4\u4e5d\u60a8\u6bcf\u98ce\u7ea7\u8ddf\u7b11\u554a\u5b69\u4e07\u5c11\u76f4\u610f\u591c\u6bd4\u9636           \u8fde\u8f66\u91cd\u4fbf\u6597\u9a6c\u54ea\u5316\u592a\u6307\u53d8\u793e\u4f3c\u58eb\u8005\u5e72\u77f3\u6ee1\u65e5\u51b3\u767e\u539f\u62ff\u7fa4\u7a76\u5404\u516d\u672c\u601d\u89e3\u7acb\u6cb3\u6751\u516b\u96be\u65e9\u8bba\u5417";

        int x = 5; // 从这开始写
        for (int i = 0; i < 4; i++) {

            // -30 -- 30度旋转设定
            int degree = new Random().nextInt() % 30;

            String ch = base.charAt(new Random().nextInt(base.length())) + "";
            g.rotate(degree * Math.PI / 180, x, 20); // 设置旋转弧度
            g.drawString(ch, x, 20);
            g.rotate(-degree * Math.PI / 180, x, 20);
            x += 30;
        }
    }

    private void drawRandomLine(Graphics g) {
        g.setColor(Color.GREEN);
        // 4-5条干扰线
        for (int i = 0; i < 5; i++) {

            int x1 = new Random().nextInt(WIDTH);
            int y1 = new Random().nextInt(HEIGHT);

            int x2 = new Random().nextInt(WIDTH);
            int y2 = new Random().nextInt(HEIGHT);

            g.drawLine(x1, y1, x2, y2);
        }

    }

    private void setBorder(Graphics g) {
        g.setColor(Color.BLUE);
        g.drawRect(1, 1, WIDTH - 2, HEIGHT - 2);
    }

    private void setBackGround(Graphics g) {
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, WIDTH, HEIGHT);
    }
}

4. JavaScript 编码 点验证码换一张图片

涉及2文件:src/ResponseDemo4(上节有了)和WebRoot/register.html

register.html代码以下:
我实践的结果是,小手 cursor:hand 在 IE 和 Chrome 下都不显示。

<!DOCTYPE html>
<html>
<head>
<title>register.html</title>

<script type="text/javascript">
    function changeImage(img) {
        /* 点图片,能换一张,而不受缓存影响 */
        img.src = img.src + "?" + new Date().getTime();
    }
</script>
</head>

<body>
    <form action="">
        用户名:<input type="text" name="username"><br /> 密码:<input
            type="password" name="password"><br /> 认证码:<input type="text"
            name="checkcode">

        <!-- 小手点验证码 -->
        <img src="/day06/servlet/ResponseDemo4" onclick="changeImage(this)"
            alt="换一张" style="cursor:hand"><br /> <input type="submit"
            value="注册">
    </form>
</body>
</html>

浏览器输入http://localhost:8080/day06/register.html查看结果。

5. 实用技术 - 用 response 的 refresh 控制浏览器定时刷新

1.控制浏览器3秒刷新代码:

public class ResponseDemo5 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setHeader("refresh","3"); // 每3秒刷新

        String data = new Random().nextInt(10000) + "";
        response.getWriter().write(data);
    }
}

2.登陆成功后,跳转代码:

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

  // 假设这是处理登陆的sevlet

  // 假设程序运行到此,用户登陆成功了

  response.setCharacterEncoding("UTF-8");
  response.setContentType("text/html;charset=UTF-8");

  response.setHeader("refresh", "3;url='/day06/index.jsp'");
  response.getWriter().write("恭喜你登陆成功,浏览器将在3秒后,跳到首页,若没跳,请点<a href=''>超连接</a>");
}

3.登陆成功后,实用的自动跳转技术: 用到 ,想法仍是 servlet 不适合输出数据,数据在 servlet 中产生后,应传给一个jsp文件,由jsp修饰格式后,再输出,代码以下:
涉及2文件,ResponseDemo5WebRoot/message.jsp,分别以下:

// 控制浏览器刷新,跳转实用代码

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

  // 假设这是处理登陆的sevlet

  // 假设程序运行到此,用户登陆成功了,要跳转,实用代码
  // 用 <meta> 模拟 响应头 让jsp传给浏览器
  // 由于 sevlet 不适合输出数据,数据需jsp修饰后输出才是王道!故而才用<meta>模拟响应头
  String message = "<meta http-equiv='refresh' content='3;url=/day06/index.jsp'>恭喜你登陆成功,浏览器将在3秒后,跳到首页,若没跳,请点<a href=''>超连接</a>";

  this.getServletContext().setAttribute("message", message);
  // 让jsp负责输出数据,需再创建一个message.jsp文件
  this.getServletContext().getRequestDispatcher("/message.jsp")
      .forward(request, response);
}

WebRoot/message.jsp,下面代码注意把encoding改成UTF-8:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://"
            + request.getServerName() + ":" + request.getServerPort()
            + path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'message.jsp' starting page</title>
</head>

<body>
    <!-- 代码从这开始 -->
    <%
        String message = (String) application.getAttribute("message");
        out.write(message);
    %>
</body>
</html>

6. 实用技术 - 用 Expires 头控制浏览器缓存

本节故事是:控制浏览器缓存某个东西1个小时。背景是有些内容几乎无变化,这就得控制客户浏览器缓存这个内容,不然服务器会累死。
办法就是 经过服务器向浏览器发送控制缓存的响应头。

实例:客户访问index.jsp,点击“查看图书”超连接,而后跳转到ResponseDemo6,显示图书内容aaaaaaaaaa。由于图书内容不怎么变化,故需用expires头控制缓存。

WebRoot/index.jsp代码以下:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">

    <title>My JSP 'index.jsp' starting page</title>

  </head>

  <body>
    <a href="/day06/servlet/ResponseDemo6">查看图书</a>
  </body>
</html>

ResponseDemo6代码以下,注意用System.currentTimeMillis()获取当前时间,若直接用1000*3600是不行的

// 控制缓存
public class ResponseDemo6 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        // 注意 System.currentTimeMillis() 获取当前时间
        response.setDateHeader("expires",
                System.currentTimeMillis() + 1000 * 3600); // 缓存1小时

        String data = "aaaaaaaaaaaaaa";
        response.getWriter().write(data);
    }
}

7. response实现请求重定向和response的一些细节

请求重定向:你管我借钱,我没有,我让你找别人。
特色:重定向后,浏览器地址栏内容会发生变化,且浏览器至关于发送服务器2次请求。
请求重定向能不用尽可能不要用,它加剧了服务器负担(2次请求)。

何时必须用请求重定向?

  1. 登陆,登陆后跳到首页。地址栏发生变化,让用户知道他/她到首页上去了;
  2. 购物,网上购物成功后,应该重定向到购物车显示页面(不要用转发技术)。

实例代码:

// 实现请求重定向
public class ResponseDemo7 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        /*
        response.setStatus(302);
        response.setHeader("location", "/day06/index.jsp");
        */

        response.sendRedirect("/day06/index.jsp"); // 这句=上面2句
    }
}

浏览器输入http://localhost:8080/day06/servlet/ResponseDemo7查看地址栏变化及结果。

8. response 的 getOutputStream 和 getWriter 不能同时调用,他们居然互相排斥

如题,代码以下:

public class ResponseDemo8 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        response.getOutputStream();
        response.getWriter();
    }
}

上面代码运行时要抛异常:

java.lang.IllegalStateException: getOutputStream() has already been called for this response
    org.apache.catalina.connector.Response.getWriter(Response.java:609)
    org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:211)
    cn.wk.response.ResponseDemo8.doGet(ResponseDemo8.java:16)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

方立勋老师挺有意思,他课堂上说,学生们之后开发时确定会犯上面所说的错,并报异常,这种异常见不到,不算是合格的开发人员。看到这,我就呵呵了,相忠良确定不会犯这错误^^,玩笑话啦。

方老师说,有以下状况:

public class ResponseDemo8 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        response.getOutputStream();
        this.getServletContext.getRequestDispatcher("/servlet/ResponseDemo9").forward(request, response);
    // 上面是转发,不是重定向哦,转发时客户浏览器地址栏是不变的!
    }
}
public class ResponseDemo9 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        response.getWriter();       
    }
}

转着转着就懵逼了,报了那个java.lang.IllegalStateException异常。

重定向就不会抛这种异常,由于那是2次请求,有2个 response! 转发是1次请求,只有1个response对象!

经过 response 对象弄出的流,咱们不需关,web容器帮咱们关。但若不是经过 response 弄出的流,咱本身得关,服务器无论!

9. request 简介

HttpServletRequest对象表明客户端的请求,当客户端经过 HTTP 协议访问服务器时,HTTP 请求头中的全部信息都封装在这个对象中,开发人员经过这个对象的方法,可或得客户的这些信息。 request 里确定有表明客户请求的请求行,请求头和请求数据的相关方法。经过 servlet API 查看之。

URI: /news/1.html 它能标识任意资源,URI是爸爸,URL是崽。开发中 URI 用的比 URL 多。
URL: http://www.sina.com/news/1.html 它只能标识互联网上的资源

例子:

// request 简单示例
public class RequestDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        System.out.println(request.getRequestURI());
        System.out.println(request.getRequestURL());

        System.out.println(request.getQueryString());

        System.out.println("---------------------");

        System.out.println(request.getRemoteAddr()); // client ip
        System.out.println(request.getRemoteHost()); // client host name

        System.out.println(request.getLocalAddr()); // WEB 服务器的 ip
        System.out.println(request.getLocalName()); // WEB 服务器的主机名

        System.out.println(request.getRemotePort());

        System.out.println(request.getMethod()); // 返回 GET POST 等
    }
}

浏览器中输入http://localhost:8080/day06/servlet/RequestDemo1?name=aaa&pass=666查看结果为:

/day06/servlet/RequestDemo1
http://localhost:8080/day06/servlet/RequestDemo1
name=aaa&pass=666
---------------------
0:0:0:0:0:0:0:1
0:0:0:0:0:0:0:1
0:0:0:0:0:0:0:1
0:0:0:0:0:0:0:1
51607
GET

0:0:0:0:0:0:0:1这个事,访问时localhost换成127.0.0.1就好了,具体状况网上查。听说一般发生在客户机和服务器在同一台机器上。感受这个结论不靠谱。
而且,客户机浏览器的 port 也不像方老师所说的随机,我机器上的表现倒是固定的,但也不彻底固定,不管 chrome 仍是 ie。

10. request 获取请求头和请求数据

// request 获取头相关的方法
public class RequestDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        String headValue = request.getHeader("Accept-Encoding");
        System.out.println(headValue);

        System.out.println("---------------------");

        Enumeration e = request.getHeaders("Accept-Encoding");
        while (e.hasMoreElements()) {
            String value = (String) e.nextElement();
            System.out.println(value);
        }

        System.out.println("---------------------");

        e = request.getHeaderNames();
        while (e.hasMoreElements()) {
            String name = (String) e.nextElement();
            String value = request.getHeader(name);
            System.out.println(name + " = " + value);
        }
    }
}

浏览器输入http://localhost:8080/day06/servlet/RequestDemo2,我机器控制台的输出是:

gzip, deflate, br
---------------------
gzip, deflate, br
---------------------
host = localhost:8080
connection = keep-alive
cache-control = max-age=0
upgrade-insecure-requests = 1
user-agent = Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
accept = text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
accept-encoding = gzip, deflate, br
accept-language = zh-CN,zh;q=0.9
cookie = _ga=GA1.1.1376610490.1484797437; _gid=GA1.1.1268285374.1524302785

注意:不一样浏览器(IE 和 Chrome)发出的请求头根本不同,差异还挺大的。

10.1 用户带数据给服务器的方式,两种:超连接中或表单,重要例子

本节案例很重要!

该案例涉及3个文件和2个工具包:RequestDemo2WebRoot/test.htmlcn.wk.User普通java类,commons-beanutils-1.7.0.jarcommons-logging-1.1.1.jar工具包。

test.html代码:

<!DOCTYPE html>
<html>
  <head>
    <title>带数据给RequestDemo2.html</title>

    <meta name="keywords" content="keyword1,keyword2,keyword3">
    <meta name="description" content="this is my page">
    <meta name="content-type" content="text/html; charset=UTF-8">   

  </head>

  <body>
    <!-- 带数据方式1 -->
    <a href="/day06/servlet/RequestDemo2?username=xxx">点点</a>

    <!-- 带数据方式2 -->

    <form action="/day06/servlet/RequestDemo2" method="post">
        用户名1:<input type="text" name="username"><br/>
        用户名2:<input type="text" name="username"><br/>
        密码:<input type="text" name="password"><br/>
        <input type="submit" value="提交"><br/>
    </form>

  </body>
</html>

cn.wk.User普通java类代码:

package cn.wk;

public class User {
  //由于变态的弄了2个username,因此String[],正常不会这样干
    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;
    }
}

RequestDemo2 代码,里面有很重要的东西,涉及了 BeanUtils 工具包的使用:

// request 获取请求头 和 请求数据
public class RequestDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        System.out.println("-------获取数据方式1---------");
        String value = request.getParameter("username");
        if (value != null && !value.trim().equals("")) {
            System.out.println(value);
        }

        System.out.println("-------获取数据方式2---------");

        String[] values = request.getParameterValues("username");
        for (int i = 0; value != null && i < values.length; i++)
            System.out.println(values[i]);

        System.out.println("-----最重要的获取数据方式3  用BeanUtils.populate()----");
        // 使用对象 封装浏览器送来的数据
        Map<String, String[]> map = request.getParameterMap();
        User user = new User();
        try {
            BeanUtils.populate(user, map); // map集合数据填充bean
            // BeanUtils.copyProperties(dest, orig); bean的拷贝
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 服务器必须以debug方式启动, 才能观察断点
        System.out.println(user); // 这打断点,观察是否传入进user对象
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(req, resp);
    }
}

浏览器输入http://localhost:8080/day06/test.html,页面中输入数据后提交,再查看控制台结果。个人test.html页面出乱码了。

11. 经过表单收集客户机数据 - 多种 form 数据项类型展现

下面的2行没解决个人乱码问题
eclipse导入项目后HTML文件都是乱码的(原UTF-8,现GBK)
windows->perferences->General->Content Types->Text->HTML,而后将Default encoding设置为utf-8便可。

乱码问题困扰着我。

WebRoot/form.html代码以下 (多种 form 数据项类型展现):

<!DOCTYPE html>
<html>
  <head>
    <title>form.html</title>

    <meta name="keywords" content="keyword1,keyword2,keyword3">
    <meta name="description" content="this is my page">
    <meta name="content-type" content="text/html; charset=UTF-8">

  </head>

  <!-- 经常使用的 表单输入项 类型 -->
  <body>
    <form action="/day06/servlet/RequestDemo3" method="post">
        用户名:<input type="text" name="username"><br/>
        密码:<input type="password" name="password"><br/>
        性别:
            <input type="radio" name="gender" value="male">男
            <input type="radio" name="gender" value="female">女<br/>

        所在地:
            <select name="city">
                <option value="beijing">北京</option>
                <option value="shanghai">上海</option>
                <option value="chaihe">柴河</option>
            </select>
            <br/>

        爱好:
            <input type="checkbox" name="likes" value="sing">唱歌
            <input type="checkbox" name="likes" value="dance">跳舞
            <input type="checkbox" name="likes" value="basketball">篮球
            <input type="checkbox" name="likes" value="football">足球
            <br/>

        备注:<textarea rows="6" cols="60" name="description"></textarea><br/>

        大头照:<input type="file" name="image"><br/>

        <!-- 隐藏输入项 用户不可见 但表单提交时 一同提交 -->
        <input type="hidden" name="id" value="12356">

        <input type="submit" value="提交"><br/>
    </form>
  </body>
</html>

RequestDemo3代码:
用户提交的数据必定先检查后使用!

public class RequestDemo3 extends HttpServlet {

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

        System.out.println(request.getParameter("username"));
        System.out.println(request.getParameter("password"));
        System.out.println(request.getParameter("gender"));
        System.out.println(request.getParameter("city"));

        // 数据必定 先检查 后使用
        String[] likes = request.getParameterValues("likes"); // 遍历时当心likes为空
        for (int i = 0; likes != null && i < likes.length; i++) {
            System.out.println(likes[i]);
        }

        System.out.println(request.getParameter("description"));

        // 大头照涉及文件上传和下载,在这先不处理

        System.out.println(request.getParameter("id"));
    }

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

浏览器输入http://localhost:8080/day06/form.html可能有乱码,填入数据提交,控制台查看结果。

12. request乱码(试验失败,待解决)

涉及2文件,WebRoot/form3.htmlRequestDemo4,我机器上本实验作的不成功。

WebRoot/form3.html以下,2个表单,上面的用post方法,下面的用get方法:

另外一个纳闷的事是<meta name="content-type" content="text/html; charset=UTF-8">对浏览器码表的设置对我机器不起做用,不知为啥!

<!DOCTYPE html>
<html>
  <head>
    <title>form3.html</title>

    <meta name="keywords" content="keyword1,keyword2,keyword3">
    <meta name="description" content="this is my page">
    <meta name="content-type" content="text/html; charset=UTF-8">

  </head>

  <body>
    <form action="/day06/servlet/RequestDemo4" method="post">
        用户名:<input type="text" name="username"><br/>        
        <input type="submit" value="提交"><br/>
    </form>

    <form action="/day06/servlet/RequestDemo4" method="get">
        用户名:<input type="text" name="username"><br/>        
        <input type="submit" value="提交"><br/>
    </form>
  </body>
</html>
// post 提交方式 乱码解决
request.setCharacterEncoding("UTF-8"); //只对post有效
String username = request.getParameter("username");
System.out.println(username);

我机器下面代码没法去除乱码:

// get 提交方式 乱码解决
String username = request.getParameter("username");
username = new String(username.getBytes("iso-8859-1"), "UTF-8");
System.out.println(username);

超连接提交的中文也只能手工处理(由于也是get提交,按上面get提交乱码解决方式处理):

<a href="/day06/servlet/RequestDemo4?username=中国">点点</a>

13. request 实现请求转发和 mvc 设计模式 - 涉及到 forward 转发时的幺蛾子 - 不能转发2次

1

request 域
MVC思想 (model->javabean view -> jsp controller -> servlet)

RequestDemo6 生成数据,用 request 转发给 /WebRoot/message.jsp 文件显示:
下面例子简单,但很是重要。另外,不能转发两次!详细情节看下面代码:

forward请求转发的特色:

  1. 客户端只发1次请求,而服务器端有多个资源调用;
  2. 客户端浏览器地址栏无变化。
// 请求转发,以及使用request域对象把数据带给转发资源
public class RequestDemo6 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        // MVC思想 (model->javabean view -> jsp controller -> servlet)

        String data = "aaaaaaa";

        req.setAttribute("data", data);

        // request也可实现转发
        if (true) {
            req.getRequestDispatcher("/message.jsp").forward(req, resp);
            return; // return保证了之后不会再次转发了
        }

        // 但不能再次转发
        // 将抛异常 java.lang.IllegalStateException: Cannot forward after response
        // has been committed
        req.getRequestDispatcher("/index.jsp").forward(req, resp);
    }

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

        doGet(req, resp);
    }
}

/WebRoot/message.jsp 的代码,里面涉及了 EL 表达式,jsp中用 EL 表达式取出数据输出:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">

    <title>My JSP 'message.jsp' starting page</title>    

  </head>

  <body>

  ${data}   <!-- EL 表达式 -->

  <!-- 下面写法也可,推荐用 EL -->
<%
  String data = (String)request.getAttribute("data");
  out.write(data);
%>

  </body>
</html>

forward细节:forward 时,会清空response中的数据,以下:

// forward细节: forward时,会清空response中的数据
public class RequestDemo7 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        String data = "aaaaaaa";
        resp.getWriter().write(data);

        req.getRequestDispatcher("/index.jsp").forward(req, resp); //会覆盖上面的resp向浏览器送出的信息
    }

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

14. request 实现页面包含 include 代替 forward (此技术不用,一般由 jsp 处理包含,而不是由 servlet 处理)

如题,代码以下,本例涉及1个servlet,2个jsp:

public class RequestDemo8 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        req.getRequestDispatcher("/public/head.jsp").include(req, resp);

        resp.getWriter().write("hahahahaha<br/>");

        req.getRequestDispatcher("/public/foot.jsp").include(req, resp);
    }
}

/public/head.jsp:

<body>
  head <br>
</body>

/public/foot.jsp:

<body>
  foot <br>
</body>

浏览器输入http://localhost:8080/day06/servlet/RequestDemo8,输出结果为:

head
hahahahaha
foot

15. web 工程中各种地址的写法

写地址的原则:

  1. /开头;
  2. 若地址是写给服务器用的,/就表明当前 web 应用;
  3. 若地址是写给浏览器用的,/就表明网站;

我认为,第1条有用,其余2条无所谓。

// 用地址的地方
public class ServletDemo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        // 1.
        req.getRequestDispatcher("/").forward(req, resp);

        // 2.
        resp.sendRedirect("");

        // 3.
        this.getServletContext().getRealPath("");

        // 4.
        this.getServletContext().getResourceAsStream("");

        // 5.
        /*
         * <a href="">点点</a>
         *
         * <form action="/day06/form1.html">
         *
         * </form>
         * */       
    }
}

16. 利用 referer 防盗链

// 防盗链
public class RequestDemo9 extends HttpServlet {

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

        // 判断 来访者从哪一个页面来的
        String referer = request.getHeader("referer");

        // 符合盗链者条件
        if (referer == null || !referer.startsWith("http://localhost")) {
            response.sendRedirect("/day06/index.jsp");
            return;
        }

        String data = "凤姐日记";
        response.getWriter().write(data);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        doGet(request, response);
    }
}
相关文章
相关标签/搜索