1. 程式人生 > >Tomcat的response

Tomcat的response

1. response組成

  • response有三部分組成:響應行、響應頭、響應體

1.1 response物件

  • HttpServletResponse 物件封裝了向客戶端傳送資料、傳送響應頭,傳送響應狀態碼的方法。

1.1.1 常用方法

- void setStatus(int sc); 設定此響應的狀態程式碼;
- void setHeader(String name, String value); 用給定名稱和值設定響應頭

1.2 響應行(狀態行)

1.2.1概述

  • 響應行是http 響應內容的第一行
  • 響應行分為三個部分:協議版本、響應狀態碼、對響應狀態碼的解釋
  • 對於Tomcat,一般資料為:HTTP/1.1 200(tomcat8.5) 或者HTTP/1.1 200 OK(tomcat7)

1.2.2 常見的響應狀態碼

  1. 200 OK :請求和響應都已成功,伺服器通訊正常;
  2. 302 Move temporarily :設定重定向頁面跳轉的動作執行;
  3. 304 Not Modified :從瀏覽器快取中讀取資料,不從伺服器重新獲取資料;
  4. 404 Not Found :請求失敗,請求所希望得到的資源未被在伺服器上發現。一般是使用者輸錯了url 導致;
  5. 405 Method Not Allowed :請求行中指定的請求方法不存在。例如,傳送post 請求,伺服器沒有doPost方法,就會報這個錯誤;
  6. 500 Internal Server Error : 伺服器發生了錯誤。一般伺服器程式碼錯誤。

1.3 響應頭

響應頭包含但不僅限於以下幾個屬性:

  1. refresh:定時重新整理跳轉頁面;
  2. location:重定向操作:通常告知瀏覽器馬上向該地址傳送請求,通常和響應碼302 一起使用;
  3. content-encoding:設定當前資料的壓縮格式,告知瀏覽器以何種壓縮格式解壓資料(目前瀏覽器只支援”gzip”格式解壓);
  4. content-disposition:通知瀏覽器以何種方式獲取資料(直接解析資料(網頁,圖片文字),或者以附件方式(下載檔案));
  5. content-type:實體頭部用於指示資源的MIME 型別。
注意:我們content-type 常用的設定一般都是“text/html;charset=utf-8”
	其中“text/html;”——設定瀏覽器以檔案格式解析資料;“charset=utf-8”——響應資料的編碼表

1.3.1 響應頭:Location

  • 作用:通知瀏覽器要進行頁面跳轉的目標地址
  • http 狀態碼302 的作用是通知瀏覽器進行頁面跳轉的動作執行,所以響應頭location(跳轉目標地址)和http 狀態碼302 (跳轉動作執行)配合起來才可以完成頁面跳轉。
1.3.1.1 實現程式碼
方式一
//需求:跳轉到資源CountServlet
//設定跳轉目標地址
response.setHeader("location", "index.html");
//設定http狀態碼為302
response.setStatus(302);
優化程式碼(重要)
- response.sendRedirect("目標頁面/目標Servlet");
	傳送重定向【原理就是方法一】

注意:目標頁面/Servlet需要寫工程名稱,但是不能將工程名寫死,需要用servletContext來 獲取工程名字

1.3.1.2 請求轉發與請求重定向的區別及選擇(重點)
請求轉發的原理

1536570357045

請求重定向

1536570389228

總結(重要):
  1. 轉發在一次請求中完成,轉發位址列不變(只有一次請求);重定向則是兩次請求,重定向位址列變化(兩次請求,對應兩個地址);
  2. 轉發發生在伺服器內部,只能訪問伺服器當前工程的內部資源,不能訪問外部資源;重定向是瀏覽器執行跳轉操作,可以訪問任何資源;
  3. 轉發前後均共享同一個request和response(由伺服器收到第一次請求時建立);重定向前後有各自獨立的request和response;
  4. 一次請求建立一個request和response,在伺服器響應結束時就會銷燬。不同請求不會共享request和response。
選擇:
  • 由於request 是請求域物件,如果頁面跳轉需要在一次請求域內傳遞資料的(即前後頁面要傳輸資料)使用請求轉發,否則建議使用重定向
注意事項:
  • 重定向除了設定location實現之外,也能通過設定refresh實現。

1.3.2 響應頭:Content-Type

  • 格式:Content-Type: text/html; charset=碼錶

解釋:Content-Type是設定響應正文型別/報文型別,包含兩部分

第一部分:設定響應的資料型別(Mime-Type),伺服器
可以響應任何型別的資源給客戶端,資源不同,Mime-Type 不同。

Mime-Type 含義
text/html Html 程式碼
text/plain Txt 文字檔案
image/jpeg Jpg 圖片檔案
Application/json Json 資料

第二部分:響應字元碼錶:通知瀏覽器以什麼碼錶解碼資料

  • 作用:用於伺服器通知瀏覽器採用什麼碼錶對伺服器響應的資料進行解碼。解決響應中文亂碼問題
1.3.2.1 獲取Mime-Type方法
- getServletContext().getMimeType(fileName)

根據Mime-Type設定檔案媒體格式

  • 用來告知瀏覽器檔案的媒體格式,要根據哪種媒體格式解析資源
- response.setContentType(getServletContext().getMimeType(fileName));
1.3.2.2 伺服器端輸出中文資料有2 種方式
位元組流輸出
response.getOutputStream().write("輸出字串".getBytes());
字元流輸出
response.getWriter().write("輸出字串");
1.3.2.3 解決伺服器輸出字元流中文亂碼(重要)
  • 產生亂碼原因:伺服器響應資料預設採用iso8859-1 碼錶,瀏覽器預設採用GBK碼錶。

瀏覽器與伺服器傳輸中文資料的url 編碼過程(瞭解)

  1. 對位元組陣列中的負數加256
  2. 將加後的位元組陣列轉換為十六進位制整型資料
  3. 之後在每個16 進位制資料前加%
解決方案:設定響應頭content-type

方案1:

//修改response預設輸出中文資料碼錶
response.setCharacterEncoding("utf-8");
//通知瀏覽器以utf-8 解碼
response.setHeader("content-type","text/html;charset=utf-8");

優化方案:

- response.setContentType("text/html;charset=utf8");
	修改伺服器響應的編碼碼錶和瀏覽器解碼碼錶【原理就是方案1
1.3.2.4 response輸出中文資料的區別
  1. 位元組流輸出中文不亂碼原因是中國編碼預設使用gbk,瀏覽器預設gbk解碼;
  2. 但由於有可能部分瀏覽器不適用gbk,用位元組流輸出中文資料不安全,故建議輸出中文資料使用字元流。

1.3.3 響應頭:refresh

  • 格式:Refresh: 定時時間;url=跳轉網址

    • 定時時間:以秒為單位,不用引號;
    • 跳轉網址:整個網頁的完整地址,不用引號;
  • 作用:設定瀏覽器定時重新整理頁面或定時跳轉到指定的資源;

1.3.3.1 設定方式
/*需求:3 秒以後跳轉到demo.html*/
response.setHeader("refresh","3;url="+getServletContext().getContextPath()+"/demo.html");
  • 注意,工程名不能寫死
1.3.3.2 重新整理和跳轉案例
@WebServlet(urlPatterns = "/demo01")
public class Notes01Servlet extends HttpServlet {
/*
需求:倒計時5秒後跳轉到新的頁面succeed.html。
思路:首先直接進入demo01(Servlet),然後進入到倒計時的wait.html,5秒後跳轉到新的頁面
 */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //5秒後跳轉到新的頁面中,使用refresh重定向跳轉
        //這裡修改第一次進入demo01時建立的response物件refresh屬性實現跳轉功能
        response.setHeader("refresh", "2;url=succeed.html");

        //1.這裡必須使用請求轉發頁面跳轉
        //2.如果這裡也使用重定向,那麼重定向後上面修改refresh的response已經被銷燬而建立了新的response,自然無法跳轉
        //3.上一行程式碼和這行程式碼的順序不能調,因為本行程式碼已經將request和response轉發了,那麼就沒有response能呼叫上行程式碼
        request.getRequestDispatcher("/wait.html").forward(request, response);
        
        /*
        response.sendRedirect("/day29/wait.html");    //跳轉後此時傳入的response已經被銷燬     
        response.setHeader("refresh", "2;url=succeed.html");    //這裡的response仍是傳入的response,已被銷燬,該行程式碼無效,也不會修改到跳轉後的refresh實現再度跳轉
        //上述兩行程式碼也說明,即使是先重定向到wait.html,再refresh也無法跳轉
        */

    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //*如果post方式的執行內容與get不同,則刪掉下面程式碼重寫即可
        doGet(request, response);
    }
}

1.3.4 響應頭:Content-Disposition

  • 格式:Content-Disposition: attachment; filename=下載檔名

    • attachment,通知瀏覽器不要顯示資料要以附件形式下載
    • 下載檔名:下載的檔名字,包含字尾名
  • 作用:預設瀏覽器檢視資料是直接顯示資料,而Content-Disposition可以通知瀏覽器不要直接顯示資料,以附件形式下載資料

1.3.4.1 設定方式
response.setHeader("content-disposition","attachment;filename="+fileName);
1.3.4.2 注意事項
  • 如果響應頭傳遞中文預設不會進行url編碼,故無法傳遞中文,需要手動進行url編碼;
  • 請求體和請求行、響應體在傳遞中文資料會自動進行url編碼;
  • 瀏覽器和伺服器在傳輸中文資料時,中間過程還要進行url編碼,才能執行中文資料傳輸。
手動進行URL編碼的程式碼(不需強記,直接複製即可)
String fileName = "檔名.字尾名";
String ua = request.getHeader("User-Agent");
boolean IE_LT11 = ua.contains("MSIE"); // IE11 以下版本
boolean IE11 = ua.contains("rv:11.0) like Gecko"); // IE11
boolean Edge = ua.contains("Edge"); // win10 自帶的Edge 瀏覽器
// 如果是微軟的瀏覽器,直接進行UTF-8 編碼
if (IE_LT11 || IE11 || Edge) {
	fileName = URLEncoder.encode(fileName, "UTF-8");
	// java 的編碼方式和瀏覽器有略微的不同:對於空格,java 編碼後的結果是加號,
	// 而瀏覽器的編碼結果是%20,因此將+替換成%20, 這樣瀏覽器才能正確解析空格
    fileName = fileName.replace("+", "%20");
}
// 標準瀏覽器使用Base64 編碼
else {
	Base64.Encoder encoder = Base64.getEncoder();
	fileName = encoder.encodeToString(fileName.getBytes("utf-8"));
	// =?utf-8?B?檔名?= 是告訴瀏覽器以Base64 進行解碼
	fileName = "=?utf-8?B?" + fileName + "?=";
}
1.3.4.3 擴充套件案例:以附件形式下載不同型別檔案(類似於工具類)

前期準備:download頁面由前端編寫,這裡只是簡單的頁面。

另外還要注意,本地可訪問資源要放在IDEA的web資料夾內

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>下載頁面</title>
    <style type="text/css"></style>
    <script type="text/javascript">
        /*
        除了IE瀏覽器(IE11及以下)預設不對路徑中的中文字元進行編碼以外,其餘瀏覽器都會支援中文路徑編碼,
        所以為了傳送請求時解決中文亂碼的問題,這裡要對瀏覽器進行判斷。如果是ie11及以下的要手動url編碼
        */
        function isIE() {
            //獲取當前瀏覽器相關資訊
            var explorer = window.navigator.userAgent.toLowerCase();
            //判斷是否是ie瀏覽器
            if (explorer.indexOf("msie") >= 0 || explorer.indexOf("rv:11.0) like gecko") >= 0) {
                return true;
            } else {
                false;
            }
        }

        window.onload = function () {
            if (isIE()) {
                //在是IE瀏覽器的情況下,對中文請求引數編碼
                var str = document.getElementById("sn").href;
                var str = encodeURI(str);
                document.getElementById("sn").href = str;
            }
        };

    </script>
</head>
<body>
<a href="download?fileName=1.txt">1.txt</a>     <!--路徑預設使用get方式提交,用?串聯相當於提交該檔案-->
<a href="download?fileName=Impactor_0.9.34.zip">Impactor_0.9.34.zip</a>
<a href="download?fileName=hq.jpg">hq.jpg</a>
<a id="sn" href="download?fileName=燒酒.png">燒酒.png</a>
</body>
</html>

後端程式碼

注意:如果檔名有中文和特殊字元,必須要先解決request的中文亂碼問題,否則獲取的檔名亂碼,導致對應的資源無法下載

@WebServlet(urlPatterns = "/download")
public class Notes02DownloadServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //解決request中文亂碼問題
        request.setCharacterEncoding("utf-8");

        /*1.獲得下載的檔名字*/
        String fileName = request.getParameter("fileName");
        System.out.println(fileName);

        /*2.獲取資源的真實路徑*/
        ServletContext servletContext = getServletContext();
        String realPath = servletContext.getRealPath("/download");
        //根據真實路徑獲得file物件,用來建立輸入流,讀取檔案
        File file = new File(realPath,fileName);

        /*3.根據Mime-Type設定檔案媒體格式,告知瀏覽器傳輸的檔案是什麼格式*/
        response.setContentType(servletContext.getMimeType(fileName));

        /*4.解決中文檔名亂碼的問題,複製黏貼即可*/
        String ua = request.getHeader("User-Agent");
        boolean IE_LT11 = ua.contains("MSIE"); // IE11以下版本
        boolean IE11 = ua.contains("rv:11.0) like Gecko"); // IE11
        boolean Edge = ua.contains("Edge"); // win10自帶的Edge瀏覽器
        // 如果是微軟的瀏覽器,直接進行UTF-8編碼
        if (IE_LT11 || IE11 || Edge) {
            fileName = URLEncoder.encode(fileName, "UTF-8");
            // java的編碼方式和瀏覽器有略微的不同:對於空格,java編碼後的結果是加號,
            // 而瀏覽器的編碼結果是%20,因此將+替換成%20, 這樣瀏覽器才能正確解析空格
            fileName = fileName.replace("+", "%20");
        }
        // 標準瀏覽器使用Base64編碼
        else {
            Base64.Encoder encoder = Base64.getEncoder();
            fileName = encoder.encodeToString(fileName.getBytes("utf-8"));
            // =?utf-8?B?檔名?= 是告訴瀏覽器以Base64進行解碼
            fileName = "=?utf-8?B?" + fileName + "?=";
        }

        /*5.提示瀏覽器以下載的形式獲取伺服器資源*/
        response.setHeader("content-disposition","attachment;filename=" + fileName);

        /*6.使用流輸出資料*/
        FileInputStream fis = new FileInputStream(file);
        ServletOutputStream sos = response.getOutputStream();
        byte[] bytes = new byte[1024];
        int len = -1;
        while ((len = fis.read(bytes))!= -1) {
            sos.write(bytes,0,len);
        }

        /*7.記得關閉輸入流釋放資源*/
        fis.close();
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //*如果post方式的執行內容與get不同,則刪掉下面程式碼重寫即可
        doGet(request, response);
    }
}

1.4 響應體

  • 瀏覽器將伺服器輸出的資料直接顯示給使用者
  • 作用:
    • 輸出字元資料、位元組資料
    • 輸出資原始檔資料(資源圖片)
    • 輸出快取(記憶體中)圖片(資源沒有對應的物理資源,不是真實資源)–驗證碼

1.4.1 驗證碼案例(類似工具類)

實現驗證碼功能

@WebServlet(urlPatterns = "/CheckCodeServlet")
public class Notes03CheckCodeServlet extends HttpServlet {
    //獲取隨機數
    private Random random = new Random();

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

        //建立一個快取圖片
        //BufferedImage(int width,int height,int imageType)
        BufferedImage image = new BufferedImage(90,30,BufferedImage.TYPE_INT_RGB);
        //獲取畫筆
        Graphics g = image.getGraphics();
        //設定畫筆的顏色
        g.setColor(Color.white);
        //設定畫布範圍
        g.fillRect(0,0,90,30);
        //畫干擾線
        //g.drawLine(x1,y1,x2,y2);
        for (int i=1;i<=4;i++){
            //設定每條線的隨機顏色
            g.setColor(getColor());
            int x1=random.nextInt(91);
            int y1=random.nextInt(31);
            int x2=random.nextInt(91);
            int y2=random.nextInt(31);
            g.drawLine(x1,y1,x2,y2);
        }

        //畫驗證碼
        String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890小天使";
        //隨機選取4個
        StringBuilder checkCode = new StringBuilder();
        for(int i=0;i<4;i++){
            int index = random.nextInt(data.length());
            char item = data.charAt(index);
            checkCode.append(item);
        }
        System.out.println("驗證碼:"+checkCode.toString());
        //將驗證碼畫到圖片上
        //g.drawString(str,x,y);
        int i=0;
        for(char item : checkCode.toString().toCharArray()){
            //設定每個字元隨機顏色
            g.setColor(getColor());
            //將每個字元畫到圖片上
            g.drawString(item+"",10+(i*20),15);
            i++;
        }
        //將記憶體圖片輸出到瀏覽器中
        ImageIO.write(image,"png",response.getOutputStream());
    }

    /*獲取隨機顏色*/     
    public Color getColor(){
        int r = random.nextInt(256);
        int g = random.nextInt(256);
        int b = random.nextInt(256);
        //Color(int r, int g, int b)
        return new Color(r,g,b);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //*如果post方式的執行內容與get不同,則刪掉下面程式碼重寫即可
        doGet(request, response);
    }
}

2. ServletContext

2.1 ServletContext概述

    1、ServletContext 是一個容器(**域物件**)可以儲存**鍵值對資料(String key,Object value)**,儲存在ServletContext 中的資料不僅可以**提供給所有的servlet 使用**,而且可以在**整個專案範圍內使用**(後期的過濾器、監聽器也可以使用ServletContext)

    2、伺服器會為每一個工程建立一個ServletContext 物件,這個物件就是ServletContext 物件。這個物件**全域性唯一(一個工程僅一個)**,而且**工程內部的所有servlet 都共享**這個物件。所以叫**全域性應用程式共享物件**。

    3、伺服器啟動就建立ServletContext,伺服器關閉了才銷燬ServletContext

2.2 ServletContext的作用

  1. 可以讀取資源在當前專案中的檔案位置
  2. 可以作為域物件在專案全域性範圍內提供共享資料

2.3 使用ServletContext讀取資源在當前專案中的檔案位置

  • 方法:

    - String getServletContext().getRealPath(path);
    	根據相對路徑獲取專案部署位置上資源的絕對路徑
    

2.3.1 示例程式碼

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
	//servletContext可以獲取當前專案下的資原始檔
	//可以根據相對路徑獲取在伺服器上的絕對路徑
	String realPath = 
            
           

相關推薦

no