# WebServerTest **Repository Path**: yang-chun-Yxx/web-server-test ## Basic Information - **Project Name**: WebServerTest - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-11-15 - **Last Updated**: 2021-11-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 功能实现: 1:在ClientHandler中定义方法readLine,用于读取一行字符串的操作重用 2:将解析请求行中读取第一行内容的操作改为readLine 3:继续利用readLine读取消息头并保存每个消息头 功能实现(访问服务器返回一个界面): 一:先创建第一个页面index.html 1:在项目目录下新建一个目录webapps 这个目录用于存放当前服务端下所有的网络应用。注:每个网路应用相当于一个"网站"的所有内容 而每个网络应用以一个独立的目录保存在webapps下,该目录的名字就是这个网络应用的名字。 tomcat就是这样规定的,我们也按照该规定来实现。 2:在webapps目录下新建目录:myweb,作为我们的第一个"网站" 3:在myweb目录下新建第一个页面:index.html 功能实现(404): 1:在webapps下新建一个子目录root 该目录用于保存当前服务端所有网络应用共用的资源,比如404页面,因为无论请求哪个 网络应用中的资源都可能发生不存在的情况。 2:在root目录下新建页面:404.html 该页面居中显示一行字即可:404,资源不存在! 3:在ClientHandler处理请求的环节,当实例化File对象后添加一个分支,如果该File 对象存在且表示的是一个文件则将其响应给浏览器 否则发送的响应做如下变化: 1:状态行中的状态代码改为404,状态描述改为NotFound 2:响应头Content-Length发送的是404页面的长度 3:响应正文为404页面内容 完成后,在浏览器地址栏输入一个不存在的资源地址,检查服务端是否正确响应404页面 实现(将ClientHandler中发送响应的工作拆分出去): 1:在com.webserver.http包中新建类:HttpServletResponse 响应对象 2:在响应对象中定义对应的属性来保存响应内容并定义response方法来发送响应 3:修改ClientHandler,使用响应对象完成响应的发送 实现(解决浏览器访问一次,就结束的问题): 将WebServerApplication的start方法中的代码加上死循环,重复接收多次客户端的连接即可 实现(将ClientHandler中处理请求的操作拆分出去): 1.在com.webserver.core包下新建类:DispatcherServlet并定义service方法,用来处理请求 2.将ClientHandler处理请求的操作移动到service方法中去 3.ClientHandler通过调用DispatcherServlet的service完成处理请求环节 导入学子商城webapp资源后访问其首页,发现页面无法正常显示 浏览器F12跟踪请求和响应的交互发现两个问题: 1.我们仅发送了两个响应头(Content-Length和Content-Type) 目前仅需要这两个头,但是服务端实际上可以根据处理情况设置需要发送其他响应头 2.Content-Type的值是固定的"text/html",这导致浏览器请求到该资源无法正确理解噶资源因此每发挥出实际作用 分两个版本解决这两个问题 此版本解决响应头可以根据设置进行发送 实现: 1.在HttpServletResponse中添加一个Map类型的属性用于保存所有要发送的响应头Map headers 2.修改发送响应头的方法SendHeaders中的逻辑,将固定发送两个头的操作改为遍历Headers这个Map, 将所有要发送的两个响应头逐个发送 3.只需要在发送前根据处理情况,向Headers中put要发送的响应头即可,这个工作需要 3.1:在响应头中添加一个方法:addHeader,将要发送的响应头存入headers中 3.2:在DispatcherServlet处理请求环节调用addHeader存放要发送的响应头即可 此版本在DispatcherServlet中实现根据用户请求的资源的后缀来设置响应头 Content-Type的值. 目前先支持6个类型实现功能.6个类型参照http.txt文件最后. 实现: 1:在DispatcherServlet中定义一个Map,保存资源后缀名与对应的类型头信息 2:截取用户请求的资源的后缀名并作为key提取对应的头信息 3:设置响应头Content-Type的值 上个版本中在DispatcherServlet中的service方法中每次处理请求时为了设置资源对应 的响应头Content-Type,我们先实例化一个Map保存类型与投信息的对应关系,但实际上这 个Map没有必要每次都创建一次,全局一份即可,每次仅需要根据资源后缀提取对应的响应头即可 实现: 1.在com.webserver.http包下新建类:HttpContext 这个类用于维护所有的HTTP协议规定的内容以便反复使用 2.在HttpContext中定义一个静态属性:Map mimeMapping 3.在静态块中对该属性进行初始化 4.提供一个静态方法,可以根据资源后缀从这个Map中提取Content-Type对应的值 这个方法名为:getMimeType() 5.设置响应头Content-Type的值时就可以根据资源后缀调用步骤4提供的方法 6.将要去DispatcherServlet中设置响应头Content-Type和Content-Length的 工作移动到HttpServletResponse的设置响应正文方法setEntity中 原因:一个响应中只要包含正文就应当包含说明正文信息的两个头Content-Type和Content-Length 因此我们完全可以设置正文的时候自动设置这两个头,这样做的好处是将来设置完正文(调用setEntity)后 无需再单独设置这两个头了; 解决空请求问题 HTTP协议注明:为了保证服务端的健壮性,应当忽略客户端空的请求。 浏览器有时会发送空请求,即:与服务端链接后没有发送标准的HTTP请求内容,直接与服务端断开链接。 此时服务端按照一问一答的处理流程在解析请求时请求行由于没有内容,在拆分后获取信息会出现数组 下标越界。 解决:当解析请求行时发现没有内容就对外抛出空请求异常(自定义的一个异常),并最终抛出给 ClientHandler,使其忽略后续的处理请求与发送响应的工作,直接与客户端断开来忽略本次操作。 实现: 1:在com.webserver.http包下新建自定义异常:EmptyRequestException,空请求异常 2:在HttpServletRequest的解析请求行方法parseRequestLine中,当读取请求行后发现是一个 空字符串则对外抛出空请求异常并通过构造方法继续对外抛出给ClientHandler 3:ClientHandler添加一个新的catch专门捕获空请求异常,捕获后无需做任何处理,目的仅仅是 忽略处理请求和响应客户端的工作 通过下两个版本完成用户注册业务 用户注册业务的大致流程: 1:用户访问注册页面,并在页面上输入注册信息后点击注册按钮 2:数据提交发到服务端,服务端解析页面提交上来的数据 3:根据解析出来的数据进行响应的注册处理 4:给用户回复一个注册处理结果的页面(注册成功或失败) 这里涉及的知识点: 1:页面如何将用户输入的信息提交给服务端 表单form的使用 2:服务端如何通过解析请求得到表单数据 3:DispatcherServlet如何区分请求是处理注册还是请求一个静态资源(页面,图片等) 本版本完成表单的提交以及请求的解析工作。这个工作是通用操作,无论将来处理何种业务,解析表单 数据的方式都是相同的。 实现: 1:在webapps/myweb下新建用户注册页面reg.html 在这个页面上我们学习form表单的使用 2:重构HttpServletRequest的解析工作,添加对表单数据的解析。