在上一次,我们设计了自己的Servlet,现在我们来对具体的Servlet进行实现。
我们首先来对MyHttpRequest进行实现,这个类就相当于Tomcat里面的HttpServletRequest,我们实现最基本的几个功能。分别是:
- getParameter(String name)
- getMethod()
- getUri()
代码编写
我们首先在MyHttpServlet接口里面定义这几个方法。
然后在MyHttpRequestImpl类里面实现这几个方法,我们首先定义几个成员变量
写一个构造器,在创建对象时传入一个InputStream流,并使用这个流就行初始化
这个init方法我们最后实现,我们先实现接口里面的几个方法
getParameter(String name)
getMethod()
getUri()
为了测试方便,我们再写一个toString()方法
现在,我们对前面提到的init()方法进行实现,这个方法要对成员属性进行初始化。我们这里仅考虑2种请求方式,一直get,一种post。又因为get和post的http请求不一样,所以我们要分开进行处理。
我们先来对get请求进行处理,我在前面的http介绍里面说过,get请求的参数在请求行中就有体现。所以我们通过请求行就能得到请求参数,方法,uri。如果对http请求不了解,请参考http请求,get和post。
于是,我们就可以定义一个方法来对get请求进行处理
public void setGetParamsAndUri(String requestLine) {
//得到最后一个空格索引
int lastSpace = requestLine.lastIndexOf(" ");
//通过第一个空格索引和最后一个空格索引就能得到uri和参数
String s = requestLine.substring(requestLine.indexOf(" ")+1, lastSpace);
//判断是否含有?
if (s.contains("?")) {
//得到第一个?索引
int firstFlag = s.indexOf("?");
//?前面的就是uri
uri = s.substring(0, firstFlag);
//后面的就是参数
String params = s.substring(firstFlag + 1);
//将字符串传给setParams进行初始化
setParams(params);
} else {
//没有?直接赋值
uri = s;
}
}
public void setParams(String params) {
//通过&进行分割
String[] kvs = params.split("&");
for (String kv : kvs) {
//通过=进行分割
String[] keyVal = kv.split("=");
//如果keyVal是正确写法就将参数和值存入HashMap中进行保存
if (keyVal.length == 2) {
String key = keyVal[0];
String value = keyVal[1];
parameters.put(key, value);
}
}
}
下面我们再对post进行处理,post的请求和get不同,请求行中没有参数,参数在请求体中。我们写一个方法来对post进行处理
public void setPostParamsAndUri(String requestLine, StringBuilder sb) {
try {
//得到uri
uri = requestLine.substring(requestLine.indexOf(" ") + 1, requestLine.lastIndexOf(" "));
//URLDecoder是对游览器的请求进行解码
//传入的sb就是请求头+请求体
//我们通过寻找最后一个换行符就能得到请求体
String params = URLDecoder.decode(sb.substring(sb.lastIndexOf("\n")+1), "utf-8");
//和get一样传入参数字符串,然后对参数进行初始化
setParams(params);
} catch (IOException e) {
e.printStackTrace();
}
}
下面,我们来编写我们的init方法
public void init() {
try {
//定义一个字节数组
byte[] bytes = new byte[1024];
int len = 0;
//定义一个StringBuilder来对http请求进行存储
StringBuilder sb = new StringBuilder();
//读取内容
while ((len = inputStream.read(bytes)) != -1) {
sb.append(new String(bytes, 0, len));
if (len != bytes.length) break;
}
//获得请求行并进行解码
String requestLine = URLDecoder.decode(sb.substring(0, sb.indexOf("\r")), "utf-8");
requestLine = URLDecoder.decode(requestLine, "utf-8");
//得到请求行第一个空格位置
int firstSpace = requestLine.indexOf(" ");
//得到请求方法
method = requestLine.substring(0, firstSpace);
//通过方法调用不同方法
if ("GET".equals(method)) {
setGetParamsAndUri(requestLine);
} else if ("POST".equals(method)) {
setPostParamsAndUri(requestLine, sb);
}
} catch (IOException e) {
e.printStackTrace();
}
}
上面,我们就已经把基本的3个方法实现了,当然,肯定还有不完善的地方,如字符串处理,对其他请求方法的处理,异常参数处理等。但是我们写这个的主要目的是为了对tomcat的运行机制有更深的认识,不需要太过于关注细节,所以,上面这样写就行了。下面给出这个类的完整代码
import java.io.IOException;
import java.io.InputStream;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
public class MyHttpRequestImpl implements MyHttpRequest {
//定义成员属性
private String method;
private String uri;
private Map<String, String> parameters = new HashMap<>();
private InputStream inputStream;
public MyHttpRequestImpl(InputStream inputStream) {
this.inputStream = inputStream;
//对成员属性进行初始化
init();
}
public void init() {
try {
//定义一个字节数组
byte[] bytes = new byte[1024];
int len = 0;
//定义一个StringBuilder来对http请求进行存储
StringBuilder sb = new StringBuilder();
//读取内容
while ((len = inputStream.read(bytes)) != -1) {
sb.append(new String(bytes, 0, len));
if (len != bytes.length) break;
}
//获得请求行并进行解码
String requestLine = URLDecoder.decode(sb.substring(0, sb.indexOf("\r")), "utf-8");
requestLine = URLDecoder.decode(requestLine, "utf-8");
//得到请求行第一个空格位置
int firstSpace = requestLine.indexOf(" ");
//得到请求方法
method = requestLine.substring(0, firstSpace);
//通过方法调用不同方法
if ("GET".equals(method)) {
setGetParamsAndUri(requestLine);
} else if ("POST".equals(method)) {
setPostParamsAndUri(requestLine, sb);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void setGetParamsAndUri(String requestLine) {
//得到最后一个空格索引
int lastSpace = requestLine.lastIndexOf(" ");
//通过第一个空格索引和最后一个空格索引就能得到uri和参数
String s = requestLine.substring(requestLine.indexOf(" ")+1, lastSpace);
//判断是否含有?
if (s.contains("?")) {
//得到第一个?索引
int firstFlag = s.indexOf("?");
//?前面的就是uri
uri = s.substring(0, firstFlag);
//后面的就是参数
String params = s.substring(firstFlag + 1);
//将字符串传给setParams进行初始化
setParams(params);
} else {
//没有?直接赋值
uri = s;
}
}
public void setPostParamsAndUri(String requestLine, StringBuilder sb) {
try {
//得到uri
uri = requestLine.substring(requestLine.indexOf(" ") + 1, requestLine.lastIndexOf(" "));
//URLDecoder是对游览器的请求进行解码
//传入的sb就是请求头+请求体
//我们通过寻找最后一个换行符就能得到请求体
String params = URLDecoder.decode(sb.substring(sb.lastIndexOf("\n")+1), "utf-8");
//和get一样传入参数字符串,然后对参数进行初始化
setParams(params);
} catch (IOException e) {
e.printStackTrace();
}
}
public void setParams(String params) {
//通过&进行分割
String[] kvs = params.split("&");
for (String kv : kvs) {
//通过=进行分割
String[] keyVal = kv.split("=");
//如果keyVal是正确写法就将参数和值存入HashMap中进行保存
if (keyVal.length == 2) {
String key = keyVal[0];
String value = keyVal[1];
parameters.put(key, value);
}
}
}
@Override
public String getParameter(String name) {
if (parameters.containsKey(name)) {
return parameters.get(name);
}
return "";
}
@Override
public String getMethod() {
return method;
}
@Override
public String getUri() {
return uri;
}
@Override
public String toString() {
return "MyHttpRequestImpl{" +
"method='" + method + '\'' +
", uri='" + uri + '\'' +
", parameters=" + parameters +
'}';
}
}
代码测试
我创建了一个测试类来测试刚刚写的代码,测试类非常简单,代码如下
import com.clucky.myTomcat.myHttp.MyHttpRequest;
import com.clucky.myTomcat.myHttp.MyHttpRequestImpl;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Test {
public static void main(String[] args) {
try {
//得到ServerSocket,在8080端口进行监听
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("---------服务器启动成功----------");
while (!serverSocket.isClosed()) {
//等待连接
Socket socket = serverSocket.accept();
System.out.println("连接成功.....");
//得到输入流
InputStream inputStream = socket.getInputStream();
MyHttpRequest myHttpRequest = new MyHttpRequestImpl(inputStream);
System.out.println(myHttpRequest);
//写回数据
socket.getOutputStream().write("HTTP/1.1 200\r\n\r\nhello".getBytes());
System.out.println("断开连接.....");
//关闭流
inputStream.close();
socket.close();
}
} catch (IOException e) {
System.out.println("服务器启动失败");
}
}
}
我们再来创建一个表单来对post进行测试,代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<form action="http://localhost:8080/login" method="post">
username:<input type="text" name="username"/><br/>
password:<input type="password" name="password"/><br/>
<input type="submit">
</form>
</body>
</html>
测试GET请求
首先我们运行测试类,在游览器输入http://localhost:8080/people?name=zs&age=18,然后查看控制台输出。输入如下
成功获取到所有参数,说明GET请求没有问题。
测试POST请求
首先运行测试类,然后打开html文件,账号密码随便输入,点击提交后查看控制台输出
获取到的用户名和密码与我们输入一样,说明POST请求也没有问题。