我们这次是引入获取参数,比如你的GET 请求 或者post 请求 如何吧请求参数进行封装 成map 集合 。
先看下erquest。请求类里边改造
private void parseRequestLine() 这个方法 改造成 依据 ?进行分割处理因为
http://localhost:8080/servlet/com.yixin.HelloWorldServlet?name=jxd&age=18请求 要把参数拿出来 name=jxd&age=18
新增了protected void parseParameters() 解析参数方方法
新增了 private static void putMapEntry 存入参数的方法
这里post 的解析只给出application/x-www-form-urlencoded 格式的请求,至于我们常用的json 请求 后期找机会给出,因为相对简单,就是解析 json 格式 的数据 。
具体的实现逻辑如下(给出关键性代码):
public class JxdRequest implements HttpServletRequest {
private InputStream input;
private SocketInputStream sis;
private String uri;
InetAddress address;
int port;
protected HashMap<String, String> headers = new HashMap<>();
protected Map<String, String[]> parameters = new ConcurrentHashMap<>();
HttpRequestLine requestLine = new HttpRequestLine();
private boolean parsed = false;
private String queryString;
public void parse(Socket socket) {
try {
input = socket.getInputStream();
this.sis = new SocketInputStream(this.input, 2048);
parseConnection(socket);
this.sis.readRequestLine(requestLine);
parseRequestLine();//解析数据
parseHeaders();
} catch (IOException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
}
}
private void parseRequestLine() {
int question = requestLine.indexOf("?");
if (question >= 0) {
queryString=new String(requestLine.uri, question + 1, requestLine.uriEnd - question - 1);
uri = new String(requestLine.uri, 0, question);
} else {
queryString = null;
uri = new String(requestLine.uri, 0, requestLine.uriEnd);
}
}
private void parseConnection(Socket socket) {
address = socket.getInetAddress();
port = socket.getPort();
}
private void parseHeaders() throws IOException, ServletException {
while (true) {
HttpHeader header = new HttpHeader();
sis.readHeader(header);
//表示读取完毕
if (header.nameEnd == 0) {
if (header.valueEnd == 0) {
return;
} else {
throw new ServletException("httpProcessor.parseHeaders.colon");
}
}
String name = new String(header.name, 0, header.nameEnd);
String value = new String(header.value, 0, header.valueEnd);
// 设置相应的请求头
if (name.equals(DefaultHeaders.ACCEPT_LANGUAGE_NAME)) {
headers.put(name, value);
} else if (name.equals(DefaultHeaders.CONTENT_LENGTH_NAME)) {
headers.put(name, value);
} else if (name.equals(DefaultHeaders.CONTENT_TYPE_NAME)) {
headers.put(name, value);
} else if (name.equals(DefaultHeaders.HOST_NAME)) {
headers.put(name, value);
} else if (name.equals(DefaultHeaders.CONNECTION_NAME)) {
headers.put(name, value);
} else if (name.equals(DefaultHeaders.TRANSFER_ENCODING_NAME)) {
headers.put(name, value);
} else {
headers.put(name, value);
}
}
}
protected void parseParameters() {
String encoding = getCharacterEncoding();
System.out.println(encoding);
if (encoding == null) {
encoding = "ISO-8859-1";
}
String qString = getQueryString();
System.out.println("getQueryString:"+qString);
if (qString != null) {
byte[] bytes ;
try {
bytes = qString.getBytes(encoding);
parseParameters(this.parameters, bytes, encoding);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();;
}
}
String contentType = getContentType();
if (contentType == null)
contentType = "";
int semicolon = contentType.indexOf(';');
if (semicolon >= 0) {
contentType = contentType.substring(0, semicolon).trim();
}
else {
contentType = contentType.trim();
}
if ("POST".equals(getMethod()) && (getContentLength() > 0) && "application/x-www-form-urlencoded".equals(contentType)) {
try {
int max = getContentLength();
int len = 0;
byte buf[] = new byte[getContentLength()];
ServletInputStream is = getInputStream();
while (len < max) {
int next = is.read(buf, len, max - len);
if (next < 0) {
break;
}
len += next;
}
is.close();
if (len < max) {
throw new RuntimeException("Content length mismatch");
}
parseParameters(this.parameters, buf, encoding);
}
catch (UnsupportedEncodingException ue) {
}
catch (IOException e) {
throw new RuntimeException("Content read fail");
}
}
}
/**
*
* parseParameters 这个方法 举例 例如data name=jxd&age=18
* 他会从0位置遍历到最后一位 同时设置两个指针一个ix 一个ox
* ix 就是从char 数组0位开始遍历到最后
* ox 是一个查找指针 ,当遇到 = 或者&时候,进行取舍,=前边表示key ,& 前边表示value 如果没有& 表示结尾
* 要注意一个细节 当遇到 =或者& 时候会把ox 赋值0 ,但是为啥要 default: data[ox++] = c;
* ,当我们遇到第一个=的时候 下一个是value=jxd 那么之前那个key(name) 就没有用了,因为已经赋值到map里边了 ,所以读取
* jxd 时候覆盖掉前边的nam,然后 ox指针因为是从0开始 等遇到jxd后变边的&时候
* 照样能把value=jxd 取出来 这样 一个数组 就能完成了,虽然data 原数组被改变 这样看似不太好但是,但是节省空间 不然你就要2个数组才能完成,一个取值一个
* 放值 不得不说设计的很巧妙 。
*/
public void parseParameters(Map<String,String[]> map, byte[] data, String encoding)
throws UnsupportedEncodingException {
if (parsed)
return;
System.out.println(data);
if (data != null && data.length > 0) {
int pos = 0;
int ix = 0;
int ox = 0;
String key = null;
String value = null;
while (ix < data.length) {
byte c = data[ix++];
switch ((char) c) {
case '&':
value = new String(data, 0, ox, encoding);
if (key != null) {
putMapEntry(map,key, value);
key = null;
}
ox = 0;
break;
case '=':
key = new String(data, 0, ox, encoding);
ox = 0;
break;
case '+':
data[ox++] = (byte)' ';
break;
case '%':
data[ox++] = (byte)((convertHexDigit(data[ix++]) << 4)
+ convertHexDigit(data[ix++]));
break;
default:
data[ox++] = c;
}
}
//The last value does not end in '&'. So save it now.
//最后一个参数没有&结尾
if (key != null) {
value = new String(data, 0, ox, encoding);
putMapEntry(map,key, value);
}
}
parsed = true;
}
private byte convertHexDigit(byte b) {
if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
return 0;
}
/**
*
* 这个方式是 存入map集合 因为有的value值对应多个key 所以是数组形式存储value
*/
private static void putMapEntry( Map<String,String[]> map, String name, String value) {
String[] newValues = null;
String[] oldValues = (String[]) map.get(name);
if (oldValues == null) {
newValues = new String[1];
newValues[0] = value;
} else {
newValues = new String[oldValues.length + 1];
System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
newValues[oldValues.length] = value;
}
map.put(name, newValues);
}
public String getUri() {
return uri;
}
@Override
public String getMethod() {
return new String(this.requestLine.method, 0, this.requestLine.methodEnd);
}
@Override
public Collection<Part> getParts() throws IOException, ServletException {
return null;
}
@Override
public Part getPart(String s) throws IOException, ServletException {
return null;
}
@Override
public <T extends HttpUpgradeHandler> T upgrade(Class<T> aClass) throws IOException, ServletException {
return null;
}
@Override
public Object getAttribute(String s) {
return null;
}
@Override
public Enumeration<String> getAttributeNames() {
return null;
}
@Override
public String getCharacterEncoding() {
return null;
}
@Override
public void setCharacterEncoding(String s) throws UnsupportedEncodingException {
}
@Override
public int getContentLength() {
return Integer.parseInt(headers.get(DefaultHeaders.CONTENT_LENGTH_NAME));
}
@Override
public long getContentLengthLong() {
return 0;
}
@Override
public String getContentType() {
return headers.get(DefaultHeaders.CONTENT_TYPE_NAME);
}
@Override
public ServletInputStream getInputStream() throws IOException {
return this.sis;
}
@Override
public String getParameter(String name) {
parseParameters();
String values[] = parameters.get(name);
if (values != null)
return (values[0]);
else
return (null);
}
@Override
public Enumeration<String> getParameterNames() {
parseParameters();
return (Collections.enumeration(parameters.keySet()));
}
@Override
public String[] getParameterValues(String name) {
parseParameters();
String values[] = (String[]) parameters.get(name);
if (values != null)
return (values);
else
return null;
}
}
这个类里边我们增加了
//这段代码是测试用,可以获取的 请求参数 支持get 和post
Map<String, String[]> map = requestFacade.getParameterMap();
这段代码 可以运行 测试的main 方法进行测试 看一下 map 里边的数据。
public class JxdServletProcessor {
public void process(JxdRequest request, JxdResponse response) {
//首先根据uri最后一个/号来定位,后面的字符串认为是servlet名字
String uri = request.getUri();
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
URLClassLoader loader = null;
try {
// create a URLClassLoader
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(JxdHttpServer.WEB_ROOT);
String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString();
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader(urls);
} catch (IOException e) {
System.out.println(e.toString());
}
//由上面的URLClassLoader加载这个servlet
Class<?> servletClass = null;
Servlet servlet = null;
try {
HttpRequestFacade requestFacade = new HttpRequestFacade(request);
HttpResponseFacade responseFacade = new HttpResponseFacade(response);
servletClass = loader.loadClass(servletName);
response.setCharacterEncoding("UTF-8");
response.addHeader(DefaultHeaders.CONTENT_TYPE_NAME,"text/html;charset=UTF-8");
response.sendHeaders();//发送响应头
//这段代码是测试用,可以获取的 请求参数 支持get 和post
Map<String, String[]> map = requestFacade.getParameterMap();
servlet = (Servlet) servletClass.newInstance();
servlet.service(requestFacade, responseFacade);
} catch (ClassNotFoundException | IOException e) {
System.out.println(e.toString());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
}
}
}