手写一个民用Tomcat (06)
我们这次是引入获取参数,比如你的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(); } } }
原文地址:https://blog.csdn.net/weixin_39831786/article/details/137909744
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!