手写简易版Tomcat第三天

目录

前言

实现

创建Servlet框架

创建request与response的封装类

测试

Get请求

Post请求


前言

        前面我们实现了多线程处理请求,接下来我们去实现Sevelet框架。但是我们不去实现那么多的接口,我们只写一种情况。

实现

创建Servlet框架

创建接口MyServlet

public interface MyServlet {
}

创建抽象类MyHttpServlet实现MyServlet接口

public abstract class MyHttpServlet implements MyServlet {
}

创建request与response的封装类

创建好Servlet框架后,还需要创建两个对象分别对响应和请求的封装,在TomCat中原类名叫做HttpServletRequest与HttpServletResponse。我们自己创建这两个类与其接口。

创建request的接口和实现类

public interface MyRequest {
}
public class MyHttpRequest implements MyRequest{
}

创建response的接口和实现类

public interface MyResponse {
}
public class MyHttpResponse implements MyResponse {
}

现在我们只是提供了必要的类,还没有具体的方法。接下来先对request的封装类添加一些必要的方法。

  • getParameter():获取指定参数的值
  • getMethod():获取请求方式
  • getUri():获取Uri
public interface MyRequest {
    String getParameter(String name);
    String getMethod();
    String getUri();
}
public class MyHttpRequest implements MyRequest{
    //用来存储每个请求的信息
    private String uri;
    private String method;
    private Map<String,String> parameters = new HashMap<>();
    //通过输入流来获取信息
    private InputStream inputStream;

    public MyHttpRequest(InputStream inputStream) {
        this.inputStream = inputStream;
        //未定义,待会实现
        init();
    }

    @Override
    public String getParameter(String name) {
        if (parameters.containsKey(name)){
            return parameters.get(name);
        }
        throw new RuntimeException("没有该参数名为:"+name);
    }

    @Override
    public String getMethod() {
        return method;
    }

    @Override
    public String getUri() {
        return uri;
    }
}

提供好方法后,需要通过输入流获取请求头信息并解析对成员属性赋值。这里我使用了init()方法。

private void init() {
        byte[] bytes = new byte[1024];
        int len;
        StringBuilder stringBuilder = new StringBuilder();
        //从输入流读取信息到bytes
        try {
            //循环读取到读取结束
            while ((len = inputStream.read(bytes)) != -1) {
                stringBuilder.append(new String(bytes, 0, len));
                //我么使用的时Socket通信,如果不关闭socket通道的话,是无法返回-1的。这时read方法阻塞,只有关闭后才会返回-1
                if (len != bytes.length) break;
            }
            String requestHead = URLDecoder.decode(stringBuilder.substring(0, stringBuilder.indexOf("\r\n")), "utf-8");
            // GET /people?username=123456 HTTP/1.1  获取第一个空格的下标位置
            int index = requestHead.indexOf(" ");
            this.method = requestHead.substring(0, index);
            //接下来区分是Get请求还是Post请求
            if ("GET".equals(method)) {
                setGetUriAndParameters(requestHead);
            } else if (("POST").equals(method)) {
                setPostUriAndParameters(requestHead, stringBuilder);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

上面代码中使用了两个方法分别来区分Get请求与Post请求的Uri、Parameter的设置。这两个方法分别为
 

    /**
     * 通过请求地址获取uri与参数
     *
     * @param requestHead
     */
    private void setGetUriAndParameters(String requestHead) {
        //最后一个空格的下标
        int index = requestHead.lastIndexOf(" ");
        //      /favicon.ico?k=v
        String uriAndParameter = requestHead.substring(requestHead.indexOf(" ") + 1, index);
        if (uriAndParameter.contains("?")) {
            //说明包含参数
            //问号的下标
            int flagIndex = uriAndParameter.indexOf("?");
            String params = uriAndParameter.substring(flagIndex + 1);
            String uri = uriAndParameter.substring(0, flagIndex);
            this.uri = uri;
            setParameter(params);
        } else {
            this.uri = uriAndParameter;
        }
    }
    private void setPostUriAndParameters(String requestHead, StringBuilder sb) {
        try {
            //得到uri
            uri = requestHead.substring(requestHead.indexOf(" ") + 1, requestHead.lastIndexOf(" "));
            //URLDecoder是对游览器的请求进行解码
            //传入的sb就是请求头+请求体
            //我们通过寻找最后一个换行符就能得到请求体
            String params = URLDecoder.decode(sb.substring(sb.lastIndexOf("\n") + 1), "utf-8");
            //和get一样传入参数字符串,然后对参数进行初始化
            setParameter(params);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

 这两个方法都使用了setParameter方法来设置参数。该方法代码如下

    private void setParameter(String params) {
        //是否包含&符号
        if (params.contains("&")) {
            String[] kvs = params.split("&");
            for (String s : kvs) {
                String[] kv = s.split("=");
                if (kv.length == 2) {
                    parameters.put(kv[0], kv[1]);
                }
            }
        } else {
            String[] kv = params.split("=");
            if (kv.length == 2) {
                parameters.put(kv[0], kv[1]);
            }
        }
    }

测试

Get请求

首先测试Get方法,我们添加一些输出语句来观察运行结果,修改线程池的执行代码为下

public void execute(Socket socket) {
        executor.execute(()->{
            System.out.println(Thread.currentThread().getName()+"开始处理请求");
            InputStream inputStream = null;
            try {
                inputStream = socket.getInputStream();
                MyHttpRequest myHttpRequest = new MyHttpRequest(inputStream);
                System.out.println("请求方式为:"+myHttpRequest.getMethod());
                System.out.println("请求参数username为:"+myHttpRequest.getParameter("username"));
                System.out.println("请求Uri为:"+myHttpRequest.getUri());
                //读取完毕后,进行一个简单响应
                OutputStream outputStream = socket.getOutputStream();
                String responseHead = "HTTP/1.1 200\r\n" +
                        "Content-Type: text/html;charset=utf-8\r\n\r\n";
                String responseBody = "这是一个简单的展示页面";
                String response = responseHead + responseBody;
                //将数据展示给浏览器
                outputStream.write(response.getBytes());
                outputStream.close();
                inputStream.close();
                socket.close();
                System.out.println("断开连接...");
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

在浏览器输入地址localhost:8080/people?username=123456观察控制台输出结果

可以看到正常输出。

Post请求

接下来测试Post请求,添加一个webapp文件夹,在里面编写一个简单的form表单

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://localhost:8080/login" method="post">
    用户名:<input type="text" name="username"></br>
    密码:<input type="password" name="password"></br>
    <input type="submit">
</form>
</body>
</html>

然后打开该页面后输入值点击提交。

可以看到,可以正常输出结果。

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zmbwcx2003

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值