天天看點

自己動手寫Web容器之TomJetty之五:包裝請求參數1擴充RequestHeader類2自定義請求參數類3封裝請求對象4響應請求參數效果展示

傳送門 ☞ 輪子的專欄 ☞ 轉載請注明 ☞ http://blog.csdn.net/leverage_1229

        前面我們實作了TomJetty響應無參請求靜态頁面的功能,但真實情況下,幾乎所有請求都得攜帶參數。不能處理使用者參數請求的Web伺服器就好比溫室裡的花朵,始終上不了台面。是以本節我們将為TomJetty加入響應使用者參數的功能。此外,前面我們使用的送出請求的方式都是GET方式,但在實際情況下,POST方式使用的更廣泛,是以我們也将給TomJetty增加響應POST請求的能力。

1擴充RequestHeader類

1.1在RequestHeader類中新增parameter屬性,用于标示請求頭中客戶請求的參數,并提供對應的getter/setter方法

private String parameter;

public String getParameter() {
    return parameter;
}

public void setParameter(String parameter) {
    this.parameter = parameter;
}
           

1.2在RequestHeaderParserImpl類的parse()方法中加入如下片段,用于解析在GET和POST兩種送出方式下的請求頭的參數部分,并将其儲存到requestHeader對象中

String parameter = null;
try {
    if(method.equalsIgnoreCase("post")) {
        parameter = txt.substring(txt.lastIndexOf("\n") + 1, txt.length());
    } else if(method.equalsIgnoreCase("get")) {
        parameter = url.substring(url.indexOf("?") + 1, url.length());
        url = url.substring(0, url.indexOf("?"));
    }
} catch (Exception e) {
}

header.setParameter(parameter);
           

2自定義請求參數類

        建立一個Parameter類,采用鍵值對的方式存儲請求攜帶的參數。如果直接使用HashMap資料結構進行存儲,由于其底層的設計原則,将無法避免用戶端浏覽器重複送出的問題。

package cn.lynn.tomjetty;

public class Parameter {

    private String key;
    
    private String value;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((key == null) ? 0 : key.hashCode());
        result = prime * result + ((value == null) ? 0 : value.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final Parameter other = (Parameter) obj;
        if (key == null) {
            if (other.key != null)
                return false;
        } else if (!key.equals(other.key))
            return false;
        if (value == null) {
            if (other.value != null)
                return false;
        } else if (!value.equals(other.value))
            return false;
        return true;
    }
    
}
           

3封裝請求對象

        前面我們講過,為Java語言服務的Web容器,實際上裝載的是Servlet(Java服務端應用程式)。打開Java EE幫助文檔,檢索Servlet關鍵字。可以看到Servlet實際上被設計成一個接口。其設計層次如下:

自己動手寫Web容器之TomJetty之五:包裝請求參數1擴充RequestHeader類2自定義請求參數類3封裝請求對象4響應請求參數效果展示

        它有三個實作子類,分别對應于不同的網絡協定,這裡我們隻關注HttpServlet。此外,它還聲明了五個方法,分别是:destroy()、getServletConfig()、getServletInfo()、init()、service(ServletRequest req, ServletResponse res),學過JSP的同學知道,service()方法才是Servlet容器響應請求的核心方法。看到這裡,我們将Servlet相關HTTP協定的架構設計思想引入到TomJetty中。

3.1.建立一個Servlet接口,并聲明一個service()方法

package cn.lynn.tomjetty;

public interface IServlet {

    public void service(Request req, Response res);

}
           

3.2新增一個IServlet接口的HTTP協定實作類HttpServletImpl,并提供doGet()方法和doPost()方法(方法體暫時為空)分别處理來自用戶端浏覽器的兩種不同的送出方式

package cn.lynn.tomjetty;

public class HttpServletImpl implements IServlet {

    public void service(Request req, Response res) {
    }

    public void doGet(Request req, Response res) {
    }
    
    public void doPost(Request req, Response res) {
    }
}
           

        那麼為什麼要添加這兩個方法?通過檢視Java EE的HttpServlet的設計文檔,我們知道HttpServlet也提供了這兩個方法。當然提供的不止這兩個,還有doDelete()、doPut()等,呵呵,想多了,它們不屬于本系列關注的範疇^_^。

自己動手寫Web容器之TomJetty之五:包裝請求參數1擴充RequestHeader類2自定義請求參數類3封裝請求對象4響應請求參數效果展示
自己動手寫Web容器之TomJetty之五:包裝請求參數1擴充RequestHeader類2自定義請求參數類3封裝請求對象4響應請求參數效果展示

        我們發現service()方法中傳入參數類型已經不是普通的Request和Response,Servlet對它們進行了封裝。是以我們也照貓畫虎學着做。

3.3建立一個Request類,用于封裝HTTP請求頭和請求參數。并提供對應的存取它們的方法以供外部調用

package cn.lynn.tomjetty;

import java.util.ArrayList;
import java.util.List;

public class Request {
    // 引入請求頭
    private RequestHeader header;
    // 設定參數集合
    private List<Parameter> params = new ArrayList<Parameter>();
    // 設定請求參數
    public void setParameter(String param) {
        if(param == null || param.trim().equals("")) {
            return;
        }
        
        String[] result = param.split("&");
        
        for (int i = 0; i < result.length; i++) {
            Parameter parameter = new Parameter();
            parameter.setKey(result[i].split("=")[0]);
            parameter.setValue((result[i].split("=").length <= 1) ? "" : result[i].split("=")[1]);
            params.add(parameter);
        }
    }
    // 擷取請求參數的值
    public String getParameterValue(String key) {
        String result = null;
        for(Parameter parameter : params) {
            if(parameter.getKey().equals(key)) {
                result =  parameter.getValue();
            }
        }
        return result;
    }

    public RequestHeader getHeader() {
        return header;
    }

    public void setHeader(RequestHeader header) {
        this.header = header;
    }
}
           

3.4在TomJetty類的run()方法中加入如下片段,将請求頭和請求參數存放到Request對象中進行統一管理

// 封裝請求頭
Request request = new Request();
request.setParameter(header.getParameter());
request.setHeader(header);
           

4響應請求參數效果展示

4.1GET方式送出

自己動手寫Web容器之TomJetty之五:包裝請求參數1擴充RequestHeader類2自定義請求參數類3封裝請求對象4響應請求參數效果展示

        在IE浏覽器輸入上述位址後回車,控制台列印如下:

RequestHeader [
GET /index.htm HTTP/1.1
Accept: */*
Accept-Language: zh-cn
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; KB974488)
Accept-Encoding: gzip, deflate
Host: 127.0.0.1:9527
Connection: Keep-Alive
Cookie: null
parameter: username=lynnli1229&password=123456
]
           

4.2POST方式送出

        建立一個input.htm檔案,在其<body>标簽内編寫一個表單用于POST送出使用者資訊。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <form action="http://127.0.0.1:9527/index.htm" name="form" id="form" method="post">
    使用者名:<input name="username" type="text"></br>
    密    碼:<input name="password" type="password"></br>
        <input name="submit" value="送出" type="submit">
        <input name="reset" value="重置" type="reset">
    </form>
</body>
</html>
           
自己動手寫Web容器之TomJetty之五:包裝請求參數1擴充RequestHeader類2自定義請求參數類3封裝請求對象4響應請求參數效果展示

        在IE浏覽器輸入上述位址後回車,接着填寫表單項,點選送出按鈕,頁面會導航到index.htm,控制台列印如下:

RequestHeader [
GET /input.htm HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/QVOD, application/QVOD, */*
Accept-Language: zh-cn
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; KB974488)
Accept-Encoding: gzip, deflate
Host: 127.0.0.1:9527
Connection: Keep-Alive
Cookie: null
parameter: /input.htm
]
RequestHeader [
POST /index.htm HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/QVOD, application/QVOD, */*
Accept-Language: zh-cn
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; KB974488)
Accept-Encoding: gzip, deflate
Host: 127.0.0.1:9527
Connection: Keep-Alive
Cookie: null
parameter: username=lynnli1229&password=123456&submit=%E6%8F%90%E4%BA%A4
]
           

        至此,TomJetty伺服器已經能夠在POST和GET兩種請求送出方式下,都可以為攜帶參數的靜态頁面請求提供服務。然而隻能處理靜态頁面請求的伺服器還是遠遠不夠的,畢竟現階段動态網頁才是主流。是以下一節我們将賦予TomJetty伺服器處理動态網頁的能力。