一步一步实现一个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,后面我们将在这个基础上实现参数的存储和灵活读取。我们下期再见!

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