1. 程式人生 > >JavaWeb學習筆記四 request&response

JavaWeb學習筆記四 request&response

cer 代碼 gbk msi 抓包工具 rom service net war

HttpServletResponse

我們在創建Servlet時會覆蓋service()方法,或doGet()/doPost(),這些方法都有兩個參數,一個為代表請求的request和代表響應response。service方法中的response的類型是ServletResponse,而doGet/doPost方法的response的類型是HttpServletResponse,HttpServletResponse是ServletResponse的子接口,功能和方法更加強大。

response的運行流程

技術分享

通過抓包工具抓取Http響應

技術分享

因為response代表響應,所以我們可以通過該對象分別設置Http響應的響應行,響應頭和響應體

通過response設置響應行

設置響應行的狀態碼 setStatus(int sc)

//手動設置http響應行中的狀態碼
response.setStatus(404);

通過response設置響應頭,其中,add表示添加,而set表示設置

addHeader(String name, String value) 
addIntHeader(String name, int value) 
addDateHeader(String name, long date) 
setHeader(String name, String value) 
setDateHeader(String name, 
long date) setIntHeader(String name, int value)

比如:

Date date = new Date();

//設置響應頭
response.addHeader("name", "zhangsan");
response.addIntHeader("age", 28);
response.addDateHeader("birthday", date.getTime());

response.addHeader("name", "lisi");

response.setHeader("age", "28");
response.setHeader(
"age", "50");
//設置定時刷新的頭 response.setHeader("refresh", "5;url=http://www.baidu.com"); //----------重定向-------------------------- //沒有響應 告知客戶端去重定向到servlet2 //1、設置狀態碼302 //response.setStatus(302); //2、設置響應頭Location //response.setHeader("Location", "/WEB14/servlet2"); //封裝成一個重定向的方法sendRedirect(url) response.sendRedirect("/WEB14/servlet2");

通過response設置響應體

響應體設置文本 PrintWriter getWriter()

response.getWriter().write("hello");

獲得字符流,通過字符流的write(String s)方法可以將字符串設置到response 緩沖區中,隨後Tomcat會將response緩沖區中的內容組裝成Http響應返回給瀏覽器端。

關於設置中文的亂碼問題

原因:response緩沖區的默認編碼是iso8859-1,此碼表中沒有中文,可以通過 response的setCharacterEncoding(String charset) 設置response的編碼。

但發現客戶端還是不能正常顯示文字,原因是我們將response緩沖區的編碼設置成UTF-8,但瀏覽器的默認編碼是本地系統的編碼,因為我們都是中文系統,所以客戶端瀏覽器的默認編碼是GBK,我們可以手動修改瀏覽器的編碼是UTF-8。

還可以在代碼中指定瀏覽器解析頁面的編碼方式,通過response的setContentType(String type)方法指定頁面解析時的編碼是UTF-8, response.setContentType("text/html;charset=UTF-8");
上面的代碼不僅可以指定瀏覽器解析頁面時的編碼,同時也內含 setCharacterEncoding的功能,所以在實際開發中只要編寫 response.setContentType("text/html;charset=UTF-8");就可以解決頁面輸出中文亂碼問題。

        //設置response查詢的碼表
        //resp.setCharacterEncoding("UTF-8");
        //通過一個頭 Content-Type 告知客戶端使用何種碼表
        //resp.setHeader("Content-Type", "text/html;charset=UTF-8");
resp.setContentType("text/html;charset=UTF-8"); PrintWriter writer = resp.getWriter(); writer.write("你好");

響應頭設置字節

ServletOutputStream getOutputStream():獲得字節流,通過該字節流的write(byte[] bytes)可以向response緩沖區中寫入字節,在由Tomcat服務器將字節內容組成Http響應返回給瀏覽器。

案例1:下載服務器上的圖片

        //使用response獲得字節輸出流
        ServletOutputStream out = resp.getOutputStream();

        //獲得服務器上的圖片
        String realPath = this.getServletContext().getRealPath("a.jpg");
        InputStream in = new FileInputStream(realPath);

        int len = 0;
        byte[] buffer = new byte[1024];
        while((len=in.read(buffer))>0){
            out.write(buffer, 0, len);
        }

        in.close();
        out.close();

案例2:使用字節流輸出中文

       //1 獲得輸出字節流
        OutputStream os = resp.getOutputStream();
        //2 輸出中文
        os.write("你好 世界!".getBytes("UTF-8"));
        //3告訴瀏覽器使用GBK解碼 ==> 亂碼
        resp.setHeader("Content-Type", "text/html;charset=utf-8");

案例3:完成文件下載

文件下載的實質就是文件拷貝,將文件從服務器端拷貝到瀏覽器端。所以文件下載需 要IO技術將服務器端的文件使用InputStream讀取到,在使用 ServletOutputStream寫到response緩沖區中

代碼如下:

package response;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;

/**
 * Created by yang on 2017/7/23.
 */
public class Demo01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //要下載的文件的名稱
        String filename = "a.flv";

        //要下載的這個文件的類型-----客戶端通過文件的MIME類型去區分類型
        resp.setContentType(this.getServletContext().getMimeType(filename));
        //告訴客戶端該文件不是直接解析 而是以附件形式打開(下載)
        resp.setHeader("Content-Disposition", "attachment;filename="+filename);

        //獲取文件的絕對路徑
        String path = this.getServletContext().getRealPath("download/"+filename);
        //獲得該文件的輸入流
        InputStream in = new FileInputStream(path);
        //獲得輸出流---通過response獲得的輸出流 用於向客戶端寫內容
        ServletOutputStream out = resp.getOutputStream();
        //文件拷貝的模板代碼
        int len = 0;
        byte[] buffer = new byte[1024];
        while((len=in.read(buffer))>0){
            out.write(buffer, 0, len);
        }
    }
}

如果下載中文文件,頁面在下載時會出現中文亂碼或不能顯示文件名的情況, 原因是不同的瀏覽器默認對下載文件的編碼方式不同,ie是UTF-8編碼方式,而火狐 瀏覽器是Base64編碼方式。所裏這裏需要解決瀏覽器兼容性問題,解決瀏覽器兼容性問題的首要任務是要辨別訪問者是ie還是火狐(其他),通過Http請求體中的一個屬性可以辨別。

技術分享

技術分享

//解決亂碼方法如下(了解):

if (agent.contains("MSIE")) {
        // IE瀏覽器
        filename = URLEncoder.encode(filename, "utf-8");
        filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
        // 火狐瀏覽器
BASE64Encoder base64Encoder = new BASE64Encoder();
        filename = "=?utf-8?B?"
                + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
        // 其它瀏覽器
        filename = URLEncoder.encode(filename, "utf-8");                
}

//其中agent就是請求頭User-Agent的值

完整代碼如下:

技術分享
package response;

import sun.misc.BASE64Encoder;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;

/**
 * Created by yang on 2017/7/23.
 */
public class Demo01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //要下載的文件的名稱
        String filename1 = "美女.jpg";
//獲得要下載的文件的名稱
        //解決獲得中文參數的亂碼----下節課講
        String filename = new String(filename1.getBytes("ISO8859-1"),"UTF-8");


        //獲得請求頭中的User-Agent
        String agent = req.getHeader("User-Agent");
        //根據不同瀏覽器進行不同的編碼
        String filenameEncoder = "";
        if (agent.contains("MSIE")) {
            // IE瀏覽器
            filenameEncoder = URLEncoder.encode(filename, "utf-8");
            filenameEncoder = filenameEncoder.replace("+", " ");
        } else if (agent.contains("Firefox")) {
            // 火狐瀏覽器
            BASE64Encoder base64Encoder = new BASE64Encoder();
            filenameEncoder = "=?utf-8?B?"
                    + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
        } else {
            // 其它瀏覽器
            filenameEncoder = URLEncoder.encode(filename, "utf-8");
        }
        //要下載的這個文件的類型-----客戶端通過文件的MIME類型去區分類型
        resp.setContentType(this.getServletContext().getMimeType(filename));
        //告訴客戶端該文件不是直接解析 而是以附件形式打開(下載)
        resp.setHeader("Content-Disposition", "attachment;filename="+filename);

        //獲取文件的絕對路徑
        String path = this.getServletContext().getRealPath("download/"+filename1);
        //獲得該文件的輸入流
        InputStream in = new FileInputStream(path);
        //獲得輸出流---通過response獲得的輸出流 用於向客戶端寫內容
        ServletOutputStream out = resp.getOutputStream();
        //文件拷貝的模板代碼
        int len = 0;
        byte[] buffer = new byte[1024];
        while((len=in.read(buffer))>0){
            out.write(buffer, 0, len);
        }
    }
}
Demo01

HttpServletRequest

我們在創建Servlet時會覆蓋service()方法,或doGet()/doPost(),這些方法都有兩個參數,一個為代表請求的request和代表響應response。

service方法中的request的類型是ServletRequest,而doGet/doPost方法的request的類型是HttpServletRequest,HttpServletRequest是ServletRequest的子接口,功能和方法更加強大,今天我們學習HttpServletRequest。

request的運行流程

技術分享

通過抓包工具抓取Http請求

技術分享

因為request代表請求,所以我們可以通過該對象分別獲得Http請求的請求行,請 求頭和請求體

通過request獲得請求行

1、獲得客戶端的請求方式: String getMethod()

 //1、獲得請求方式
String method = req.getMethod();
System.out.println("method:"+method);//method:GET

2、獲得請求的資源:

String getRequestURI() 
StringBuffer getRequestURL() 
String getContextPath() ---web應用的名稱
String getQueryString() ---- get提交url地址後的參數字符串username=zhangsan&password=123

比如:

        //2、獲得請求的資源相關的內容
        String requestURI = req.getRequestURI();
        StringBuffer requestURL = req.getRequestURL();
        System.out.println("uri:"+requestURI);//uri:/Demo01
        System.out.println("url:"+requestURL);//url:http://localhost:8080/Demo01
        //獲得web應用的名稱
        String contextPath = req.getContextPath();
        System.out.println("web應用:"+contextPath);//web應用:
        //地址後的參數的字符串
        String queryString = req.getQueryString();
        System.out.println(queryString);//name=yyb&pwd=123
        //3、獲得客戶機的信息---獲得訪問者IP地址
        String remoteAddr = req.getRemoteAddr();
        System.out.println("IP:"+remoteAddr);//IP:0:0:0:0:0:0:0:1

request.getContextPath()方法

在開發Web項目時,經常用到的方法,其作用是獲取當前的系統路徑。當使用Tomcat作為Web服務器,項目一般部署在Tomcat下的webapps的目錄下。

具體來說主要用兩種部署的路徑:

  • 一是將web項目中的webRoot下的文件直接拷貝到webapps/ROOT下(刪除ROOT下的原有文件);
  • 另一中方法在Tomcat下的webapps中創建以項目名稱命名(當然也可以用其他的名稱)的文件夾,並將webRoot下的文件直接拷貝到該文件夾下。

對於第一部署方法,request.getContextPath()的返回值為空(即:"",中間無空格,註意區分null)。 對於第二部署方法,其返回值為:/創建的文件夾的名稱。具體參考這裏。

3、request獲得客戶機(客戶端)的一些信息: request.getRemoteAddr() --- 獲得訪問的客戶端IP地址

4、通過request獲得請求頭。

long getDateHeader(String name)
String getHeader(String name)
Enumeration getHeaderNames()
Enumeration getHeaders(String name)
int getIntHeader(String name)

比如:

//1、獲得指定的頭
        String header = request.getHeader("User-Agent");
//Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240
        System.out.println(header);
//2、獲得所有的頭的名稱
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            System.out.println(headerName + ":" + headerValue);
        }
//accept:text/html, application/xhtml+xml, image/jxr, */*
//accept-language:zh-CN
//user-agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240
//accept-encoding:gzip, deflate
//host:localhost:8080
//connection:Keep-Alive
//cookie:JSESSIONID=ABC9B9C2FFC81561CA377311BD3BD409

5、referer頭的作用:執行該此訪問的的來源,可以做防盜鏈。

    //對該新聞的來源的進行判斷
        String header = request.getHeader("referer");
        if(header!=null&&header.startsWith("http://localhost")){
            //是從我自己的網站跳轉過來的 可以看新聞
            response.setContentType("text/html;charset=UTF-8");
            response.getWriter().write("中國確實已經拿到100塊金牌....");
        }else{
            response.getWriter().write("你是盜鏈者,可恥!!");
        }

6.通過request獲得請求體。

請求體中的內容是通過post提交的請求參數,格式是:username=zhangsan&password=123&hobby=football&hobby=basketball

key --------value
username    [zhangsan]
password    [123]
hobby       [football,basketball]

以上面參數為例,通過以下方法獲得請求參數:

String getParameter(String name) 
String[] getParameterValues(String name)
Enumeration getParameterNames()
Map<String,String[]> getParameterMap()

註意:get請求方式的請求參數, 上述的方法一樣可以獲得。

    //1、獲得單個表單值
        String username = request.getParameter("username");
        System.out.println(username);
        String password = request.getParameter("password");
        System.out.println(password);
//2、獲得多個表單的值 String[] hobbys = request.getParameterValues("hobby"); for(String hobby:hobbys){ System.out.println(hobby); }
//3、獲得所有的請求參數的名稱 Enumeration<String> parameterNames = request.getParameterNames(); while(parameterNames.hasMoreElements()){ System.out.println(parameterNames.nextElement()); } System.out.println("------------------");
//4、獲得所有的參數 參數封裝到一個Map<String,String[]> Map<String, String[]> parameterMap = request.getParameterMap(); for(Map.Entry<String, String[]> entry:parameterMap.entrySet()){ System.out.println(entry.getKey()); for(String str:entry.getValue()){ System.out.println(str); } System.out.println("---------------------------"); }

參數亂碼解決

解決post提交方式的亂碼:request.setCharacterEncoding("UTF-8");
解決get提交的方式的亂碼:parameter = new String(parameter.getbytes("iso8859-1"),"utf-8");

//設置request的編碼---只適合post方式
request.setCharacterEncoding("UTF-8");
        
//get方式亂碼解決
String username = request.getParameter("username");//亂碼
//先用iso8859-1編碼, 在使用utf-8解碼
//username = new String(username.getBytes("iso8859-1"),"UTF-8");

request的其他功能

request是一個域對象,request對象也是一個存儲數據的區域對象,在將來開發中。使用請求轉發時,servlet處理完數據, 處理結果要交給jsp顯示。 可以使用request域將處理結果由servlet帶給jsp顯示。所以也具有如下方法:

setAttribute(String name, Object o)
getAttribute(String name)
removeAttribute(String name)

註意:request域的作用範圍為一次請求中。系統當前有多少個request就有多少request域。

request完成請求轉發

一個Servlet處理完畢交給下面的servlet(JSP)繼續處理,在現實開發中,沒有servlet轉發給servlet的情況。都是由servlet轉發給JSP.這樣可以達到分工的作用:

  • servlet: 比較適合處理業務。
  • JSP: 比較適合顯示功能。
//獲得請求轉發器----path是轉發的地址
RequestDispatcher getRequestDispatcher(String path)
//通過轉發器對象轉發
requestDispathcer.forward(ServletRequest request, ServletResponse response)

比如:

//向request域中存儲數據
request.setAttribute("name", "tom");
//servlet1 將請求轉發給servlet2
RequestDispatcher dispatcher = request.getRequestDispatcher("/servlet2");
//執行轉發的方法
dispatcher.forward(request, response);

包含

兩個servlet(jsp)共同向瀏覽器輸出內容。作用是在現實開發中,多個頁面含有相同的內容,把相同的內容抽取到一個jsp中,在需要顯示這個段內容的jsp中,包含抽取的jsp.可以達到統一管理相同的內容。

response.setContentType("text/html;charset=utf-8");
response.getWriter().print("這是正文部分<hr>");
request.getRequestDispatcher("/GServlet").include(request, response);

ServletContext域與Request域的生命周期比較

ServletContext:

  • 創建:服務器啟動
  • 銷毀:服務器關閉
  • 域的作用範圍:整個web應用

request:

  • 創建:訪問時創建request
  • 銷毀:響應結束request銷毀
  • 域的作用範圍:一次請求中

轉發與重定向的區別

1、重定向兩次請求,轉發一次請求
2、重定向地址欄的地址變化,轉發地址不變
3、重新定向可以訪問外部網站,轉發只能訪問內部資源
4、轉發的性能要優於重定向

路徑總結

路徑分為兩種情況:
1.客戶端路徑,即給瀏覽器用的路徑

<form action="/Day08-request/AServlet" >
<a href="/Day08-request/AServlet" >
<img src="/Day08-request/AServlet" >
response.sendRedirect("/Day08-request/AServlet")
Refresh:3;url=/Day08-request/AServlet

路徑寫法:

帶"/" : "/" 表示相對於主機。例如:表單所在頁面路徑為 http://localhost:8080/Day08-request/login.jsp,則 "/" 代表http://localhost:8080/
不帶"/":代表從當前目錄找。(開發中一定不要出現不帶"/"的情況)。例如:表單所在頁面路徑為 http://localhost:8080/Day08-request/info/login.jsp,則代表 http://localhost:8080/Day08-request/info/
2.服務器端路徑
<url-pattern> 中配置為 /AServlet ,則代表 http://localhost:8080/Day08-request/AServlet
request.getRequestDispatcher("/AServlet") 代表 http://localhost:8080/Day08-request/AServlet
路徑寫法:"/"相對於項目。比如 "/" 可表示 http://localhost:8080/Day08-request/

JavaWeb學習筆記四 request&response