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);
    }
}
相關文章
相關標籤/搜索