在http包下新建一个类:HttpContext,使用这个类定义相关Http协议的内容。由于不同的文件对应的Content-Type值不同,那么在HttpContext类中定义一个Map。 使用Tomcat安装目录的下的conf/web.xml文件,将里面配置的所有媒介(介质)类型都解析出来,并初始化HttpContext中mimeMapping的这个Map,使得我们的WebServer也能支持所有的媒介(介质)类型。
步骤1:在项目的pom.xml文件添加dom4j的依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.tedu</groupId> <artifactId>webserver01</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- https://mvnrepository.com/artifact/dom4j/dom4j --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> </dependencies> </project>
步骤2:在项目目录下创建一个目录,名字为conf,将群文件中上传的web.xml复制进来
步骤3:编写HttpContext类,通过解析web.xml文件初始化mimeMapping
package cn.tedu.http; import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; /** * 解析web.xml文件,使用其中的媒介类型 * @author cjn * */ public class HttpContext { //定义用于存储媒介类型的Map属性 private static Map<String, String> mimeMapping = new HashMap<String, String>(); static { initMime(); } /** * 静态方法,用于解析web.xml文件中的媒介类型 * @param args */ public static void initMime() { /* * 解析web.xml文件中的媒介类型, * 然后添加到Map属性中 */ try { SAXReader reader = new SAXReader(); Document document = reader.read(new File("conf/web.xml")); //获取根节点 Element root = document.getRootElement(); //获取根节点中字节点名字是mime-mapping的节点元素 List<Element> elements = root.elements("mime-mapping"); //获取mime-mapping节点中子节点对应的内容 for (Element e : elements) { String key = e.elementText("extension"); String value = e.elementText("mime-type"); //将获取到的元素数据存储到属性Map中 mimeMapping.put(key, value); } } catch (Exception e) { e.printStackTrace(); } } /** * 对外暴露可以根据请求的后缀进行响应的媒介类型value值 * @param args */ public static String getMimeType(String extension) { return mimeMapping.get(extension); } public static void main(String[] args) { //测试 String mimeType = HttpContext.getMimeType("png"); System.out.println(mimeType); System.out.println("媒介类型的个数:" + mimeMapping.size()); } }
输出结果:
image/png 媒介类型的个数:1011
由于一个响应中包含响应正文时,一定会在响应头中包含Content-Type和Content-Length。对此,我们直接在HttpResponse的setEntity方法中添加代码,根据给定的文件自动设置这两个头,这样就免去了在外界设置响应正文以后还要额外添加这两个头的麻烦。
HttpResponse.java
public void setEntity(File entity) { this.entity = entity; //获取请求资源的后缀名 String fileName = entity.getName(); int index = fileName.lastIndexOf("."); String suffix = fileName.substring(index + 1); /* * 定义一个Map对象,这个Map中需要添加多个媒介类型作为Map的value值, * 而key可以根据需要进行请求的资源后缀作为key值,这个Map的主要作用 * 是用于根据用户的请求不同,进行查询出不同的媒介类型 */ String mime = HttpContext.getMimeType(suffix); //向HttpResponse对象中的headers进行赋值,目的是后期进行发送响应头 putHeaders("Content-Type", mime); putHeaders("Content-Length", entity.length()+""); }
测试步骤:
(1)启动服务端,然后打开浏览器,在浏览器的地址栏处输入http://localhost:8888/myweb/index.html请求,查看是否可以显示一个首页资源,包括首页中的图片,需要观察浏览器中是否有报错的信息。
(2)打开一个新的浏览器页面,在地址栏处输入http://localhost:8888/myweb/qwer.html请求,查看是否浏览器页面显示404自定义页面。
用户打开注册页面,输入注册信息后点击注册按钮,服务端在接受这些数据后写入文件中,并响应注册成功。
页面完成注册流程:
步骤1:在webapps/myweb目录下新建一个注册页面reg.html页面,表单提交的地址指定为reg
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>注册用户</title> </head> <body> <center> <h1>欢迎注册</h1> <form action="reg" method="get"> <table border="1"> <tr> <td>用户名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密码:</td> <td><input type="password" name="password"></td> </tr> <tr> <td>昵称:</td> <td><input type="text" name="nickname"></td> </tr> <tr> <td>年龄:</td> <td><input type="text" name="age"></td> </tr> <tr> <td align="center" colspan="2"> <input type="submit" value="注册"> </td> </tr> </table> </form> </center> </body> </html>
测试:启动服务端程序,打开浏览器,在浏览器的地址栏中输入http://localhost:8888/myweb/reg.html请求路径,观察浏览器中显示的页面内容是否如下图:
步骤2:由于请求中的url中可能包含用户提交的数据,对此我们在解析请求时要对url进行进一步解析 将其拆分2部分: 请求路径与参数部分.并且对参数再次进行进一步解析,得到每个具体的参数。
2.1 重构HttpRequest解析请求行的代码
2.2 定义三个属性:String requestUri;存储url请求部分、String queryString;存储url中的参数部分、Map parameters;存储具体的每一个参数(key:参数名、value:参数值)
2.3 提供一个方法:parseURL ,该方法要对url进行进一步解析,并将解析出来的内容分别设置到 (2.2)中定义的属性中
2.4 在原来的解析请求行中的方法内,当获得到url后,就调用parseURL,对其进一步解析
2.5 在(2.2)定义的属性对外提供get方法.
步骤3:创建一个包servlets,定义一个用于处理注册业务的类RegServlet,并定义一个用于处理注册的方法service
步骤4:在ClientHandler处理请求的地方再添加一个分支,先根据请求判断是否为请求注册业务,如果是请求注册业务,则实例化一个 RegServlet并调用其service方法处理;如果不是,则执行原流程,判断是否请求一个资源 。
步骤5:在webapps/myweb下新建一个注册成功页面:reg_success.html页面
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>注册成功</title> </head> <body> <h1>恭喜您注册成功!!!</h1> </body> </html>
步骤6:完成RegServlet的service方法,在该方法中首先获取用户提交的注册信息,并写入user.dat文件之后,设置response响应注册成功页面
package cn.tedu.servlets; import java.io.File; import cn.tedu.http.HttpRequest; import cn.tedu.http.HttpResponse; /** * 处理注册业务 * @author cjn * */ public class RegServlet { public void service(HttpRequest request, HttpResponse response) { /* * 1.从paramters这个Map中取出对应key的value值 * 2.检查用户是否已经注册过 * 3.将没有注册过的用户信息存储到user.dat文件中 * 4.注册成功以后,响应一个注册成功的页面,反之注册失败响应注册失败的页面 * username=包佳奇 * password=123456 * nickname=baozi * age=30 */ String userName = request.getParamters("username"); String password = request.getParamters("password"); String nickName = request.getParamters("nickname"); String age = request.getParamters("age"); System.out.println("用户名:" + userName + ",密码:" + password + ",昵称:" + nickName + ",年龄" + age); //目前测试没有响应报空指针异常,给一个响应 response.setEntity(new File("webapps/myweb/reg_success.html")); } }
测试:启动项目的服务端主类,然后打开浏览器,在浏览器地址栏输入localhost:8888/myweb/reg.html请求路径,可以在浏览器页面中查看到一个注册页面,在注册页面的输入框位置添加相关注册信息,当点击注册按钮时,会跳转到一个登录成功页面,在服务端控制台中没有任何的报错信息,并且可以看到注册的用户信息的打印内容。
有关于将注册信息写入到user.dat文件中周二完成。
注意:在注册时年龄填写不正确,报错NumberFormatException:For input string:"xxx" 包装类把字符串转换成数字时会抛出异常,里面输入的不是数字。
URI包括URL(统一资源定位符)和URN(统一资源名称)