一步一步實現一個Web Server-02

在上一篇中,我們完成了web server的第一個項目搭建,完成了瀏覽器到我們服務器的通路,算是給兩端搭上線了。但這實在太過於基礎了。本期我們打算添加哪些內容呢?一起來看我們的本期目標:

1、按照http協議,解析瀏覽器發送過來的數據,順帶拿到請求參數;

2、根據瀏覽器請求路徑的不同,返回不同的內容給瀏覽器。

走起,老鐵們!

第一步,我們要從http報文中解析出相關的字段,拿到請求參數。先把上一節的服務器跑起來,然後回顧一下請求的報文格式和內容。我們平時工作中常用的http請求方法也就get和post這倆, 用的比較多。其他像put,delete,option啥的,用的就比較少了。我們就發一個最簡單的get請求吧。爲了方便調試,我們安裝一下postman這個工具,用來模擬瀏覽器發送情趣,哦,不!發送請求給服務端。

服務端收到的內容:

我們要拿到GET——請求方法,/getUserById.do——請求url,HTTP/1.1——http版本(這個版本開始支持ka,也就是下面的connection:keep-alive,不詳細展開了,大體就是一次請求結束後不釋放連接,畢竟重建連接成本很高。)

我們先寫一個解析http的通用方法:

    /**
     *
     * @param httpContent
     * @return
     */
    public String parseHttpContent(String httpContent) {

        /**簡單處理了,不做格式校驗了*/
        if ("".equals(httpContent)) {
            return null;
        }
        /**獲取報文第一行*/
        String firstRow = httpContent.split(Constant.CRLF)[0];
        /**解析請求方法*/
        String methodName = firstRow.split(Constant.BLANK)[0].trim().toLowerCase();
        System.out.println("請求方法:"+methodName);
        /**解析請求url*/
        String requestUrl = firstRow.split(Constant.BLANK)[1].trim();
        System.out.println("請求url:"+requestUrl);
        /**解析請求協議版本*/
        String protocolVersion = firstRow.split(Constant.BLANK)[2].trim();
        System.out.println("請求協議版本:"+protocolVersion);
        parseRequestParam(httpContent);
        return null;
    }

這樣我們就拿到了http的請求頭,請求url,請求協議的版本 這三個比較關鍵的字段

接下來就是拿到請求參數了,我們先看看常見的請求格式有哪幾類吧。

GET http://localhost:8008/getUsers.do 

GET http://localhost:8008/getUserByid.do?userId=123

GET http://localhost:8008/getUserByid.do?userId=123&areaId=345

POST http://localhost:8008/getUserByid.do

POST http://localhost:8008/addUser.do ,body參數 {"username":"lyn"}

POST http://localhost:8008/addUser.do ,body參數 {"username":"lyn","password":"lyn123"}

POST http://localhost:8008/addUser.do?username=lyn

POST http://localhost:8008/addUser.do?username=lyn&password=lyn123

POST http://localhost:8008/addUser.do?areaId=345 ,body參數 {"username":"lyn"}

POST http://localhost:8008/addUser.do?areaId=345 ,body參數 {"username":"lyn","password":"lyn123"}

POST http://localhost:8008/addUser.do?areaId=345&officeId=789 ,body參數 {"username":"lyn"}

POST http://localhost:8008/addUser.do?areaId=345&officeId=789 ,body參數 {"username":"lyn","password":"lyn123"}

乍一看可能比較多,其實我們真正關注的就這幾個點:get還是post,請求中帶不帶“?”,參數有幾個,請求體中有沒有參數。上面就是這幾種情況的組合。

具體到代碼上:

    /**
     * 解析請求參數,暫只考慮get和post方式,參數封裝爲map,k-v格式,考慮一對多
     * @param httpContent
     */
    public void parseRequestParam(String httpContent) {

        /**url後帶問號的形式,/addUser.do?username=xx&pwd=xxx */
        String firstRow = httpContent.split(Constant.CRLF)[0];
        String methodName = firstRow.split(Constant.BLANK)[0].trim().toLowerCase();
        String urlStr = firstRow.split(Constant.BLANK)[1];

        /**根據請求體中是否包含參數來分類*/
        String[] httpBodyArray = httpContent.split("\n\r\n");

        if ("".equals(firstRow) || (!"post".equals(methodName) && !urlStr.contains("?")) ) return;

        if (urlStr.contains("?")) {
            /**格式爲 key1=value1&key2=value2... */
            String paramStr = urlStr.split("\\?")[1];
            //只有一個參數
            if (!urlStr.contains("&")) {
                String paramKey = paramStr.split("=")[0];
                String paramValue = paramStr.split("=")[1];
                System.out.println("參數:"+paramKey+" , 值:"+paramValue);
            }else {  //多個參數
                String[] paramArray = paramStr.split("&");
                for (String param:paramArray) {
                    System.out.println("參數:"+param.split("=")[0]+" , 值:"+param.split("=")[1]);
                }
            }
        }

        /**如果請求體有參數,繼續解析*/
        if (httpBodyArray.length > 1) {
            String bodyParamStr = httpBodyArray[1];
            if (bodyParamStr.contains("&")) {
                String[] bodyParamArray = bodyParamStr.split("&");
                for (String bodyParam:bodyParamArray) {
                    System.out.println("Body參數: "+bodyParam.split("=")[0]+", Body值:"+bodyParam.split("=")[1]);
                }
            }else {
                System.out.println("Body參數: "+bodyParamStr.split("=")[0]+", Body值:"+bodyParamStr.split("=")[1]);
            }

        }

    }

我們來測試一下:

看下服務端:

這樣,我們就順利地從請求報文中拿到了常用的幾個關鍵屬性,比如請求方法,請求url,而且還拿到了請求參數。後面我們能做得事情就很多了。根據不同url執行不同業務方法返回不同內容。別慌,一步步來。

本期代碼路徑:https://github.com/justein/lyn-server/tree/master/server-parse,後面我們將在這個基礎上實現參數的存儲和靈活讀取。我們下期再見!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章