1。HTTP协议解读 什么是协议? 协议实际上是某些人,或者某些组织提前制定好的一套规范,大家都按照这个规范来,这样可以做到沟通无障碍。协议就是一套规范,就是一套标准。由其他人或其他组织来负责制定的。我说的话你能听懂,你说的话,我也能听懂,这说明我们之间是有一套规范的,一套协议的,这套协议就是:中国普通话协议。我们都遵守这套协议,我们之间就可以沟通无障碍。但是如果我们遵守不同的协议规范的话,就无法通信了,比如:你说的是阿拉伯语,我说的是汉语,我听不懂你说什么,你也听不同我说什么。你我两者之间无法通信交流。1。1什么是HTTP协议?HTTP协议:是W3C制定的一种超文本传输协议。(通信协议:发送消息的模板提前被制定好。)W3C:https:www。w3。org万维网联盟组织负责制定标准的:HTTPHTML4。0HTML5XMLDOM等规范都是W3C制定的。万维网之父:蒂姆伯纳斯李 什么是超文本?超文本说的就是:不是普通文本,比如流媒体:声音、视频、图片等。HTTP协议支持:不但可以传送普通字符串,同样支持传递声音、视频、图片等流媒体信息。这种协议游走在B(浏览器)和S(服务器)之间。B向S发数据要遵循HTTP协议。S向B发数据同样需要遵循HTTP协议。这样B和S才能解耦合。 什么是解耦合? 这里指定是:B(浏览器)不依赖S(服务器),S也不依赖B BS表示:BS结构的系统(浏览器访问WEB服务器的系统) HTTP协议的请求(request)和响应(response)浏览器向WEB服务器发送数据,叫做:请求(request)。WEB服务器向浏览器发送数据,叫做:响应(response)。 HTTP协议包括:请求协议浏览器向WEB服务器发送数据的时候,这个发送的数据需要遵循一套标准,这套标准中规定了发送的数据具体格式。响应协议WEB服务器向浏览器发送数据的时候,这个发送的数据需要遵循一套标准,这套标准中规定了发送的数据具体格式。 HTTP协议总结: HTTP协议就是提前制定好的一种消息模板。不管你是哪个品牌的浏览器,都是这么发,同样的不管你是哪个品牌的WEB服务器,都是这么发。火狐浏览器可以向Tomcat发送请求,也可以向Jetty服务器发送请求。浏览器不依赖具体的服务器品牌。WEB服务器也不依赖具体的浏览器品牌。可以是FF浏览器,也可以是Chrome浏览器,可以是IE,都行。1。2HTTP请求协议的具体报文 注意:HTTP请求协议中有多种请求方式。这里我们说的是常用的get请求和post请求。在该文章的下文有更加详细的说明。 HTTP的请求协议(B(浏览器)S(服务器)) HTTP的请求协议包括:如下4个部分:请求行,请求头,空白行,请求体。 如何查看我们提交的数据报文,在浏览器当中 1、我们可以在提交数据的时候按F12快捷键查看,或者是右键鼠标浏览器,在弹出的窗口当中,选择点击检查。如下图所示: 2、在选择点击检查按钮之后,弹出如下窗口,选择其中的Network(网络)选项。如下图所示:只要当我们在前端提交了数据,就可以在如下的窗口当中捕获到并显示出相应的报文信息。 3、测试如下,看看是否可以捕获到我们的请求报文的信息数据。如下图所示: 4、查看我们提交的数据信息:点击我们捕获到的报文信息,再点击Paload选项。 如下是:HTTP请求协议的具体报文:GET请求 首先编写一个是Get请求提交的表单html文件,具体的代码编写如下:!DOCTYPEhtmlhtmllangenheadmetacharsetUTF8titleHTTP请求登录titleheadbodyh2get请求h2h2post请求h2bodyhtml Accept:texthtml,applicationxhtmlxml,q0。9,imageavif,imagewebp,imageapng,;q0。8,vb3;q0。7AcceptEncoding:gzip,deflate,brAcceptLanguage:zhCN,q0。9,enUS;q0。8,q0。7Connection:keepalive下面一行是请求行Host:127。0。0。1:8080下面的是请求头Referer:http:127。0。0。1:8080servlet08login。htmlsecchua:GoogleCv111,Not(A:Bv8,Cv111secchuamobile:?0secchuaplatform:WindowsSecFetchDest:documentSecFetchMode:navigateSecFetchSite:sameoriginSecFetchUser:?1UpgradeInsecureRequests:1UserAgent:Mozilla5。0(WindowsNT10。0;Win64;x64)AppleWebKit537。36(KHTML,likeGecko)Chrome111。0。0。0Safari537。36下面是空白行:用于分隔请求头与请求体下面是请求体:向服务器发送的具体数据。usernameHellouserpswd123 如下是另一种的格式下get请求的具体报文信息GETservlet05getServlet?usernamelucyuserpwd1111HTTP1。1请求行Host:localhost:8080请求头Connection:keepalivesecchua:GoogleCv95,Cv95,;NotABv99secchuamobile:?0secchuaplatform:WindowsUpgradeInsecureRequests:1UserAgent:Mozilla5。0(WindowsNT10。0;Win64;x64)AppleWebKit537。36(KHTML,likeGecko)Chrome95。0。4638。54Safari537。36Accept:texthtml,applicationxhtmlxml,q0。9,imageavif,imagewebp,imageapng,;q0。8,vb3;q0。9SecFetchSite:sameoriginSecFetchMode:navigateSecFetchUser:?1SecFetchDest:documentReferer:http:localhost:8080servlet05index。htmlAcceptEncoding:gzip,deflate,brAcceptLanguage:zhCN,q0。9空白行请求体 如下post请求信息 Accept:texthtml,applicationxhtmlxml,q0。9,imageavif,imagewebp,imageapng,;q0。8,vb3;q0。7AcceptEncoding:gzip,deflate,brAcceptLanguage:zhCN,q0。9,enUS;q0。8,q0。7CacheControl:maxage0Connection:keepaliveContentLength:25ContentType:applicationxwwwformurlencoded请求头Host:127。0。0。1:8080请求行Origin:http:127。0。0。1:8080Referer:http:127。0。0。1:8080servlet08login。html?usernameHellouserpswd123secchua:GoogleCv111,Not(A:Bv8,Cv111secchuamobile:?0secchuaplatform:WindowsSecFetchDest:documentSecFetchMode:navigateSecFetchSite:sameoriginSecFetchUser:?1UpgradeInsecureRequests:1UserAgent:Mozilla5。0(WindowsNT10。0;Win64;x64)AppleWebKit537。36(KHTML,likeGecko)Chrome111。0。0。0Safari537。36空白行请求体 如下是另一种的格式下post请求的具体报文信息 POSTservlet05postServletHTTP1。1请求行Host:localhost:8080请求头Connection:keepaliveContentLength:25CacheControl:maxage0secchua:GoogleCv95,Cv95,;NotABv99secchuamobile:?0secchuaplatform:WindowsUpgradeInsecureRequests:1Origin:http:localhost:8080ContentType:applicationxwwwformurlencodedUserAgent:Mozilla5。0(WindowsNT10。0;Win64;x64)AppleWebKit537。36(KHTML,likeGecko)Chrome95。0。4638。54Safari537。36Accept:texthtml,applicationxhtmlxml,q0。9,imageavif,imagewebp,imageapng,;q0。8,vb3;q0。9SecFetchSite:sameoriginSecFetchMode:navigateSecFetchUser:?1SecFetchDest:documentReferer:http:localhost:8080servlet05index。htmlAcceptEncoding:gzip,deflate,brAcceptLanguage:zhCN,q0。9空白行请求体usernamelisiuserpwd123 请求行:由三部分组成:请求方式,URI,HTTP版本号。请求行:第一部分:请求方式(7种):get(常用的),post(常用的),delete,put,head,options,trace。请求行:第二部分:URI URL:统一资源定位符。代表网络中某个资源。注意:可以通过URL定位到该资源,可以直接在浏览器当中输入URI访问网络当中的资源)。比如:http:localhost:8080servlet05index。html这是URL。 URI:统一资源标识符。代表网络中某个资源的名字。注意:URI是无法定位资源的(就是无法通过直接在浏览器当中输入URI访问网络当中的资源)。比如:servlet05index。html这是URI。 URI和URL什么关系,有什么区别? URL是包括了URI的。请求行:第三部分:HTTP协议版本号 请求头:包含:请求的主机的IP地址,主机的端口号,浏览器信息,平台信息,cookie等信息。 空白行:空白行是用来区分请求头和请求体 请求体:向服务器发送的具体数据。1。3HTTP响应协议的具体报文 HTTP的响应协议(S(浏览器)B(客户端))HTTP的响应协议包括4部分:状态行,响应头,空白行,响应体。 HTTP响应协议的具体报文: AcceptRanges:bytesConnection:keepaliveContentLength:598ContentType:texthtml响应头Date:Thu,30Mar202302:48:49GMTETag:W5981680144315095KeepAlive:timeout20LastModified:Thu,30Mar202302:45:15GMT空白行!DOCTYPEhtml响应体:其实就是源代码的信息htmllangenheadmetacharsetUTF8titleHTTP请求登录titleheadbodyh2get请求h2h2post请求h2bodyhtml 另外一种post响应的具体数据报文信息。HTTP1。1200ok状态行ContentType:charsetUTF8响应头ContentLength:160Date:Mon,08Nov202113:19:32GMTKeepAlive:timeout20Connection:keepalive空白行!DOCTYPEhtml响应体:其实就是源代码的信息htmllangenheadmetacharsetUTF8titleHTTP请求登录titleheadbodyh2get请求h2h2post请求h2bodyhtml 状态行:由三部分组成:协议版本号,状态码,状态描述信息。状态行,第一部分:协议版本号(HTTP1。1)状态行,第二部分:状态码(HTTP)协议中规定的响应状态号,不同的响应结果对应不同的号码)。 如下是一些常见的状态码的意义: 200:表示请求响应成功,正常结束。404:表示访问的资源不存在,通常是因为要么你填写的路径写错了,要么是你路径写对了,但是服务器中对应的资源并没有启动成。总之404错误是基本是前端错误。405:表示前端发送的请求方式与后端接受处理(该请求)的方式不一致时发生的:基本上时如下两种情况:前端是POST请求,后端的处理方式按照get方式进行处理时,发生405前端是GET请求,后端的处理方式按照post方式进行处理时,发生405具体的该文章后面有详细的说明。所以请不要走开。500表示服务器端的程序出现了异常。一般会认为是服务器端的错误导致的。总结:以4开始的,一般是浏览器端的错误导致的。以5开始的,一般是服务器端的错误导致的。 状态行,第三部分:状态的描述信息:OK表示正则成结束。notfound:表示资源找不到。 空白行:用来分隔响应头和响应体的。 响应体:响应体就是响应的正文,这些内容是一个长的字符串,这个字符串被浏览器渲染,解释并执行,最终展示出效果。简单的说就是html对应的源代码。2。GET请求和POST请求有什么区别? 怎么向服务器发送Get请求,怎么向服务器发送Post请求?到目前为止,只有一种情况可以发送post请求:就是用form表单,并且form标签当中的method的属性值必须为methodpost才行。 其他所有情况一律都是get请求: 在浏览器地址栏上直接输入URL,敲回车,属于get请求。在浏览器上直接点击超链接,属于get请求。使用form表单提交数据时,form标签中没有写method属性,默认就是get或者使用form的时候,form标签中method属性值为:methodget。。。。 GET请求和POST请求有什么区别? Get请求:get请求发送数据的时候,数据会挂在URI的后面,并且在URI后面添加一个?,?后面的就是数据了,这样会导致发送的数据回显到浏览器的地址栏上如下显示的:。(get请求在请求行上发送数据)。 get请求只能发送普通的字符串数据,并且发送字符串的长度有限制,不同的浏览器限制不同,这个没有明确的规范。并不能发送流媒体信息:比如:图片,声音,视频等等。get请求无法发送大数据量。get请求在W3C中是这样介绍的:get请求比较适合从服务器端获取数据。get请求是安全的,get请求是安全的,因为:get请求只是为了从服务器上获取数据,不会对服务器造成威胁,注意:get请求本身是安全的,你不要用错了,用错了之后,就冤枉get请求不安全。这不是get请求的问题,而是你使用的问题。比如说:一个注册表单的信息的提交,应该使用的post请求,而却使用的是get请求,导致提交的信息回显到了地址栏上了。get请求是支持缓存了。 补充点: 任何一个get请求最终的响应结果都会被浏览器缓存起来。 在浏览器缓存当中:一个get请求的路径a对应一个资源。一个get请求的路径b对应一个资源。一个get请求的路径c对应一个资源。。。。。。。 实际上,你只要发送get请求,浏览器做的第一件事都是先从本地浏览器缓存中找,找不到的时候才会去服务器上获取。这种缓存机制目的是为了提高用户的体验。 有没有这样一个需求:我们不希望get请求走缓存,怎么办?怎么避免走缓存?我希望每一次这个get请求都去服务器上找资源,我不想从本地浏览器的缓存中取。 只要每一次get请求的请求路径不同即可。https:n。sinaimg。cnfinance590w240h350202111017cabc342ff5b9dc018b4b00cc。jpg?t789789787897898https:n。sinaimg。cnfinance590w240h350202111017cabc342ff5b9dc018b4b00cc。jpg?t789789787897899https:n。sinaimg。cnfinance590w240h350202111017cabc342ff5b9dc018b4b00cc。jpg?t系统毫秒数怎么解决?可以在路径的后面添加一个每时每刻都在变化的时间戳,这样,每一次的请求路径都不一样(缓存的路径也是不一样的),浏览器就不走缓存了。 Post请求:post请求发送的数据的时候,在请求体当中发送的,不会回显到浏览器的地址栏上,也就是说Post发送的数据,在浏览器的地址栏上不能看到的。(post请求在请求体当中发送数据)。post请求可以发送任何类型的数据,包括普通字符串,流媒体等信息:图片,视频,声音等。post请求可以发送大数据量,理论上没有限制的。post请求在W3C是这样说的:post请求比较适合向服务器传送数据。post请求是危险的,因为:post请求时向服务器提交数据,如果这些数据是通过后门的方式进入到服务器当中,服务器是很危险的。另外post是为了提交数据,所以一般情况下拦截请求的时候,大部分选择的是拦截(监听)post请求。post是不支持缓存的。因为:(POST是用来修改服务器端的资源的。)post请求之后,服务器响应的结果不会被浏览器缓存起来。因为这个缓存没有意义。2。1GET请求和POST请求如何选择,什么时候使用GET请求,什么时候使用POST请求?怎么选择GET请求和POST请求呢?衡量标准是什么呢?你这个请求是想获取服务器端的数据,还是想向服务器发送数据。如果你是想从服务器上获取资源,建议使用GET请求,如果你这个请求是为了向服务器提交数据,建议使用POST请求。大部分的form表单提交,都是post方式,因为form表单中要填写大量的数据,这些数据是收集用户的信息,一般是需要传给服务器,服务器将这些数据保存修改等。如果表单中有敏感信息,还是建议适用post请求,因为get请求会回显敏感信息到浏览器地址栏上。(例如:密码信息)做文件上传,一定是post请求。要传的数据不是普通文本。其他情况都可以使用get请求。 Get请求与Post请求的共性 不管你是get请求还是post请求,发送的请求数据格式是完全相同的,只不过位置不同,格式都是统一的:都是:namevaluenamevaluenamevaluenamevalue 其中的name表示:以form表单为例:form表单中的input标签当中的name。 其中的value表示的是:同样的以form表单为例:form表单中的input标签当中的value。3。HttpServlet源码分析 HttpServlet类是专门为HTTP协议准备的。比GenericServlet更加适合HTTP协议下的开发。HttpServlet在哪个包下?这是在Tomcat10的基础上:jakarta。servlet。http。HttpServlet,而后面Tomcat9之前的(包括Tomcat9)以内的包是在:javax。servlet。http。HttpServlet包下的。到目前为止我们接触了servlet规范中哪些接口?。注意:如下的是基于Tomcat10的基础上的,而Tomcat9之前的包括9将如下的jakarta替换为javax就可以了,具体原因大家可以移步至:javaEEWeb(Tomcat)深度理解和Servlet的本质ChinaRainbowSea的博客CSDN博客中。jakarta。servlet。Servlet核心接口(接口)jakarta。servlet。ServletConfigServlet配置信息接口(接口)jakarta。servlet。ServletContextServlet上下文接口(接口)jakarta。servlet。ServletRequestServlet请求接口(接口)jakarta。servlet。ServletResponseServlet响应接口(接口)jakarta。servlet。ServletExceptionServlet异常(类)jakarta。servlet。GenericServlet标准通用的Servlet类(抽象类)http包下都有哪些类和接口呢?jakarta。servlet。http。jakarta。servlet。http。HttpServlet(HTTP协议专用的Servlet类,抽象类)jakarta。servlet。http。HttpServletRequest(HTTP协议专用的处理请求对象)jakarta。servlet。http。HttpServletResponse(HTTP协议专用的处理响应对象)HttpServletRequest对象中封装了什么信息?HttpServletRequest简称为request对象。HttpServletRequest中封装了请求协议的全部内容。Tomcat服务器(WEB服务器)将请求协议中的数据全部解析出来,然后将这些数据全部封装到request对象当中了。也就是说,我们只要面向HttpServletRequest接口编程,就可以获取请求协议当中的数据了。HttpServletRequest,简称request对象。HttpServletResponse对象是专门用来响应HTTP协议到浏览器的。 回忆Servlet生命周期?用户第一次请求Tomcat服务器通过反射机制,调用无参数构造方法。创建Servlet对象。(web。xml文件中配置的Servlet类对应的对象。)Tomcat服务器调用Servlet对象的init方法完成初始化。Tomcat服务器调用Servlet对象的service方法处理请求。用户第二次请求Tomcat服务器调用Servlet对象的service方法处理请求。用户第三次请求Tomcat服务器调用Servlet对象的service方法处理请求。。。。。Tomcat服务器调用Servlet对象的service方法处理请求。服务器关闭Tomcat服务器调用Servlet对象的destroy方法,做销毁之前的准备工作。Tomcat服务器销毁Servlet对象。 HttpServlet源码分析:publicclassHelloServletextendsHttpServlet{用户第一次请求,创建HelloServlet对象的时候,会执行这个无参数构造方法。publicHelloServlet(){}override重写doGet方法override重写doPost方法}publicabstractclassGenericServletimplementsServlet,ServletConfig,java。io。Serializable{用户第一次请求的时候,HelloServlet对象第一次被创建之后,这个init方法会执行。publicvoidinit(ServletConfigconfig)throwsServletException{this。this。init();}用户第一次请求的时候,带有参数的init(ServletConfigconfig)执行之后,会执行这个没有参数的init()publicvoidinit()throwsServletException{NOOPbydefault}}HttpServlet模板类。publicabstractclassHttpServletextendsGenericServlet{用户发送第一次请求的时候这个service会执行用户发送第N次请求的时候,这个service方法还是会执行。用户只要发送一次请求,这个service方法就会执行一次。Overridepublicvoidservice(ServletRequestreq,ServletResponseres)throwsServletException,IOException{HttpServletRHttpServletRtry{将ServletRequest和ServletResponse向下转型为带有Http的HttpServletRequest和HttpServletResponserequest(HttpServletRequest)response(HttpServletResponse)}catch(ClassCastExceptione){thrownewServletException(lStrings。getString(http。nonhttp));}调用重载的service方法。service(request,response);}这个service方法的两个参数都是带有Http的。这个service是一个模板方法。在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成。protectedvoidservice(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{获取请求方式这个请求方式最终可能是:注意:request。getMethod()方法获取的是请求方式,可能是七种之一:GETPOSTPUTDELETEHEADOPTIONSTRACEStringmethodreq。getMethod();如果请求方式是GET请求,则执行doGet方法。if(method。equals(METHODGET)){longlastModifiedgetLastModified(req);if(lastModified1){servletdoesntsupportifmodifiedsince,noreasontogothroughfurtherexpensivelogicdoGet(req,resp);}else{longifModifiedStry{ifModifiedSincereq。getDateHeader(HEADERIFMODSINCE);}catch(IllegalArgumentExceptioniae){InvaliddateheaderproceedasifnonewassetifModifiedSince1;}if(ifModifiedSince(lastModified10001000)){Iftheservletmodtimeislater,calldoGet()RounddowntothenearestsecondforapropercompareAifModifiedSinceof1willalwaysbelessmaybeSetLastModified(resp,lastModified);doGet(req,resp);}else{resp。setStatus(HttpServletResponse。SCNOTMODIFIED);}}}elseif(method。equals(METHODHEAD)){longlastModifiedgetLastModified(req);maybeSetLastModified(resp,lastModified);doHead(req,resp);}elseif(method。equals(METHODPOST)){如果请求方式是POST请求,则执行doPost方法。doPost(req,resp);}elseif(method。equals(METHODPUT)){doPut(req,resp);}elseif(method。equals(METHODDELETE)){doDelete(req,resp);}elseif(method。equals(METHODOPTIONS)){doOptions(req,resp);}elseif(method。equals(METHODTRACE)){doTrace(req,resp);}else{NotethatthismeansNOservletsupportswhatevermethodwasrequested,anywhereonthisserver。StringerrMsglStrings。getString(http。methodnotimplemented);Object〔〕errArgsnewObject〔1〕;errArgs〔0〕errMsgMessageFormat。format(errMsg,errArgs);resp。sendError(HttpServletResponse。SCNOTIMPLEMENTED,errMsg);}}protectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{报405错误StringmsglStrings。getString(http。methodgetnotsupported);sendMethodNotAllowed(req,resp,msg);}protectedvoiddoPost(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{报405错误StringmsglStrings。getString(http。methodpostnotsupported);sendMethodNotAllowed(req,resp,msg);}}通过以上源代码分析:假设前端发送的请求是get请求,后端程序员重写的方法是doPost假设前端发送的请求是post请求,后端程序员重写的方法是doGet会发生什么呢?发生405这样的一个错误。405表示前端的错误,发送的请求方式不对。和服务器不一致。不是服务器需要的请求方式。通过以上源代码可以知道:只要HttpServlet类中的doGet方法或doPost方法执行了,必然405。怎么避免405的错误呢?后端重写了doGet方法,前端一定要发get请求。后端重写了doPost方法,前端一定要发post请求。这样可以避免405错误。这种前端到底需要发什么样的请求,其实应该后端说了算。后端让发什么方式,前端就得发什么方式。有的人,你会看到为了避免405错误,在Servlet类当中,将doGet和doPost方法都进行了重写。这样,确实可以避免405的发生,但是不建议,405错误还是有用的。该报错的时候就应该让他报错。如果你要是同时重写了doGet和doPost,那还不如你直接重写service方法好了。这样代码还能少写一点。3。1HttpServlet处理get请求和post请求源码分析 上面我们在:HTTP响应协议的具体报文模块当中提到的状态行当中的状态码部分中的一个为405前端用户提交的getpost请求与后端服务器不一致导致的错误。如下测试 当:前端发送的请求是get请求。后端程序员重写的方法是doPost 对于前端用户提交数据的html代码设计如下:注意:这里我们前端提交的是get请求。!DOCTYPEhtmlhtmllangenheadmetacharsetUTF8titleHTTP请求登录titleheadbodyh2get请求h2!action项目名web。xml当中url的映射的路径bodyhtml 如下的是对应后端服务器Servlet的代码设计。注意了,这里我们Servlet服务器端处理的是doPost请求的packagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEimportjava。io。PrintWpublicclassHttpServletTestextendsHttpServlet{OverrideprotectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{设置浏览器当中显示的格式response。setContentType(charSetutf8);PrintWriterwriterresponse。getWriter();writer。println(h1HelloWorldh1);}} 如下运行结果:报了405错误,原因是:我们前后端的处理请求的不一致。 当:前端发送的请求是post请求,后端程序员重写的方法是doGet出现的错误 对于前端用户提交数据的html代码设计如下:注意:这里我们前端提交的是post请求。!DOCTYPEhtmlhtmllangenheadmetacharsetUTF8titleHTTP请求登录titleheadbodyh2post请求h2!action项目名web。xml当中url的映射的路径bodyhtml 如下的是对应后端服务器Servlet的代码设计。注意了,这里我们Servlet服务器端处理的是doGet请求的。packagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEimportjava。io。PrintWpublicclassHttpServletTestextendsHttpServlet{OverrideprotectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{设置浏览器当中显示的格式response。setContentType(charSetutf8);PrintWriterwriterresponse。getWriter();writer。println(h1HelloWorldh1);}} 如下运行结果:报了405错误,原因是:我们前后端的处理请求的不一致。 通过以上源代码分析:假设前端发送的请求是get请求,后端程序员重写的方法是doPost 假设前端发送的请求是post请求,后端程序员重写的方法是doGet 会发生什么呢?发生405这样的一个错误。405表示前端的错误,发送的请求方式不对。和服务器不一致。不是服务器需要的请求方式。 为什么为发生405错误呢? 我们从HttpServlet源码上分析:HttpServlet模板类。publicabstractclassHttpServletextendsGenericServlet{用户发送第一次请求的时候这个service会执行用户发送第N次请求的时候,这个service方法还是会执行。用户只要发送一次请求,这个service方法就会执行一次。Overridepublicvoidservice(ServletRequestreq,ServletResponseres)throwsServletException,IOException{HttpServletRHttpServletRtry{将ServletRequest和ServletResponse向下转型为带有Http的HttpServletRequest和HttpServletResponserequest(HttpServletRequest)response(HttpServletResponse)}catch(ClassCastExceptione){thrownewServletException(lStrings。getString(http。nonhttp));}调用重载的service方法。service(request,response);}这个service方法的两个参数都是带有Http的。这个service是一个模板方法。在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成。protectedvoidservice(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{获取请求方式这个请求方式最终可能是:注意:request。getMethod()方法获取的是请求方式,可能是七种之一:GETPOSTPUTDELETEHEADOPTIONSTRACEStringmethodreq。getMethod();如果请求方式是GET请求,则执行doGet方法。if(method。equals(METHODGET)){longlastModifiedgetLastModified(req);if(lastModified1){servletdoesntsupportifmodifiedsince,noreasontogothroughfurtherexpensivelogicdoGet(req,resp);}else{longifModifiedStry{ifModifiedSincereq。getDateHeader(HEADERIFMODSINCE);}catch(IllegalArgumentExceptioniae){InvaliddateheaderproceedasifnonewassetifModifiedSince1;}if(ifModifiedSince(lastModified10001000)){Iftheservletmodtimeislater,calldoGet()RounddowntothenearestsecondforapropercompareAifModifiedSinceof1willalwaysbelessmaybeSetLastModified(resp,lastModified);doGet(req,resp);}else{resp。setStatus(HttpServletResponse。SCNOTMODIFIED);}}}elseif(method。equals(METHODHEAD)){longlastModifiedgetLastModified(req);maybeSetLastModified(resp,lastModified);doHead(req,resp);}elseif(method。equals(METHODPOST)){如果请求方式是POST请求,则执行doPost方法。doPost(req,resp);}elseif(method。equals(METHODPUT)){doPut(req,resp);}elseif(method。equals(METHODDELETE)){doDelete(req,resp);}elseif(method。equals(METHODOPTIONS)){doOptions(req,resp);}elseif(method。equals(METHODTRACE)){doTrace(req,resp);}else{NotethatthismeansNOservletsupportswhatevermethodwasrequested,anywhereonthisserver。StringerrMsglStrings。getString(http。methodnotimplemented);Object〔〕errArgsnewObject〔1〕;errArgs〔0〕errMsgMessageFormat。format(errMsg,errArgs);resp。sendError(HttpServletResponse。SCNOTIMPLEMENTED,errMsg);}}protectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{报405错误StringmsglStrings。getString(http。methodgetnotsupported);sendMethodNotAllowed(req,resp,msg);}protectedvoiddoPost(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{报405错误StringmsglStrings。getString(http。methodpostnotsupported);sendMethodNotAllowed(req,resp,msg);} 通过以上源代码阅读以及运行测试可以知道:只要没有重写对应HttpServlet类中的doGet方法或doPost方法,就会报405错误,因为没有重写了doGet或doPost方法就会执行其中的HttpServlet当中编写的doGetdoPost方法,而如果执行了的是HttpServlet当中doGetdoPost方法就会报405错误,提示你没有重写对应的doGetdoPost方法。 怎么避免405的错误呢? 后端重写了doGet方法,前端一定要发get请求。后端重写了doPost方法,前端一定要发post请求。这样可以避免405错误。这种前端到底需要发什么样的请求,其实应该后端说了算。后端让发什么方式,前端就得发什么方式。 补充 有的人,你会看到为了避免405错误,在Servlet类当中,将doGet和doPost方法都进行了重写。 这样,确实可以避免405的发生,但是不建议,405错误还是有用的。该报错的时候就应该让他报错。 如果你要是同时重写了doGet和doPost,那还不如你直接重写service方法好了。这样代码还能少写一点。 我们编写的HelloServlet直接继承HttpServlet,直接重写HttpServlet类中的service()方法行吗?可以,只不过你享受不到405错误。享受不到HTTP协议专属的东西。 一个Servlet类的开发步骤:第一步:编写一个Servlet类,直接继承HttpServletpackagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEimportjava。io。PrintWpublicclassHttpServletTestextendsHttpServlet{}第二步:重写doGet方法或者重写doPost方法,到底重写谁,javaweb程序员说了算。注意:前后端请求处理是要保持一致的。packagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEimportjava。io。PrintWpublicclassHttpServletTestextendsHttpServlet{OverrideprotectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{设置浏览器当中显示的格式response。setContentType(charSetutf8);PrintWriterwriterresponse。getWriter();writer。println(h1HelloWorldh1);}}第三步:将Servlet类配置到web。xml文件当中。?xmlversion1。0encodingUTF8?webappxmlnshttp:xmlns。jcp。orgxmlnsjavaeexmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:xmlns。jcp。orgxmlnsjavaeehttp:xmlns。jcp。orgxmlnsjavaeewebapp40。xsdversion4。0servletservletnameHttpServletTestservletnameservletclasscom。RainbowSea。servlet。HttpServletTestservletclassservletservletmappingservletnameHttpServletTestservletnameurlpatternTesturlpatternservletmappingwebapp第四步:准备前端的页面(form表单),form表单中指定请求路径即可!DOCTYPEhtmlhtmllangenheadmetacharsetUTF8titleHTTP请求登录titleheadbodyh2get请求h2!action项目名web。xml当中url的映射的路径bodyhtml4。HttpServletRequest接口详解 HttpServletRequest是一个接口,全限定名称:jakarta。servlet。http。HttpServletRequest这个是基于Tomcat10下的对应的包路径,如果是Tomcat9(包括9)就是将其中的jakarta修改为javax就可以了。HttpServletRequest接口是Servlet规范中的一员。HttpServletRequest接口的父接口:ServletRequestpublicinterfaceHttpServletRequestextendsServletRequest{}HttpServletRequest接口的实现类谁写的?HttpServletRequest对象是谁给创建的?通过测试:就是通过request。getClass()方法获取到该接口的实现类,org。apache。catalina。connector。RequestFacade实现了HttpServletRequest接口publicclassRequestFacadeimplementsHttpServletRequest{} 测试结果说明:Tomcat服务器(WEB服务器、WEB容器)实现了HttpServletRequest接口,还是说明了Tomcat服务器实现了Servlet规范。而对于我们javaweb程序员来说,实际上不需要关心这个,我们只需要面向接口编程即可。我们关心的是HttpServletRequest接口中有哪些方法,这些方法可以完成什么功能!!!!4。0。1HttpServletRequest对象中都有什么信息?都包装了什么信息?HttpServletRequest对象是Tomcat服务i其负责创建的,这个对象中封装了如下信息,以及如下HTTP的请求协议?实际上是用户发送请求的时候,遵循了HTTP协议,发送的是遵循了HTTP的请求协议的信息内容(namevaluenamevalue。。。),Tomcat服务器会将客户端遵循HTTP协议发送的信息内容获取到,并将其数据全部解析出来,然后Tomcat服务器把这些信息封装到HttpServletRequest对象当中,传给了我们Javaweb程序员。所以Javaweb程序员面向HttpServletRequest接口编程,调用其中的方法就可以获取到用户发送的请求信息了。4。1获取前端用户提交的数据信息 request和response对象的生命周期? request对象和response对象,一个是请求对象,一个是响应对象。这两个对象只在当前请求中有效。一次请求对应一个request。两次请求则对应两个request。。。。。。 我们后端如何怎么获取前端浏览器用户提交的数据? 想要获取到前端浏览器用户提交的数据,使用interfaceServletRequest接口当中抽象方法,也就是HttpServletRequest实现了ServletRequest接口的具体实现类中重写其中的抽象方法。MapString,String〔〕getParameterMap()这个是获取用户提交的数据,并存储到Map集合当中前端的name,前端的valueEnumerationStringgetParameterNames()这个是获取Map集合中所有的key就是前端中所有的name值String〔〕getParameterValues(Stringname)根据(前端的name值)key获取Map集合的(前端的value值)valueStringgetParameter(Stringname)获取value这个一维数组当中的第一个元素。这个方法最常用。以上的4个方法,和获取用户提交的数据有关系注意:这里为什么value的值是用String〔〕数组存储的。后面有说明。 思考:如果是你,前端的form表单提交了数据之后,你准备怎么存储这些数据,你准备采用什么样的数据结构去存储这些数据呢? 前端提交的数据格式:usernameabcuserpwd111aihaolearnaihaoprogrammeaihaoplaybasketball 注意:前端表单提交数据的时候,假设提交了120这样的数字,其实是以字符串120的方式提交的,所以服务器端获取到的一定是一个字符串的120,而不是一个数字。(前端永远提交的是字符串,后端获取的也永远是字符串。)所以都是用String字符串。 我会采用Map集合来存储:MapString,Stringkey存储Stringvalue存储String这种想法对吗?不对。如果采用以上的数据结构存储会发现key重复的时候value覆盖。比如这里的nameaihao是多选框内容,有多个值aihaosaihaodaihaottkeyvalueusernameabcuserpwd111aihaolearnaihaoprogrammeaihaoplaybasketball这样是不行的,因为map的key不能重复。MapString,String〔〕key存储Stringvalue存储String〔〕将value值用String数组存储起来。这样就避免了上述的key重复后value内容上的覆盖,而没有都存储起来的缺点。keyvalueusername{abc}userpwd{111}aihao{learn,programme,playbasketball} 举例使用上述四个方法获取前端用户提交的数据 第一步:编写设计好html前端显示页面,如下:注意,这里我们前端使用的是post请求,后端的Servlet就要用doPost请求处理,前后端保持一致,不然报405错误。!DOCTYPEhtmlhtmllangenheadmetacharsetUTF8titleuserregistertitleheadbodyh1userregisterh1!注意:项目名Servlet类的映射路径bodyhtml 第二步:编写对应处理前端用户提交的请求的Servlet,注意前后端请求处理保持一致:这里举例使用的是上述四个获取用户请求的其中的一个方法:MapString,String〔〕getParameterMap()这个是获取用户提交的数据,并存储到Map集合当中前端的name,前端的valuepackagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEimportjava。util。Iimportjava。util。Mimportjava。util。SpublicclassRequestServletTestextendsHttpServlet{OverrideprotectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{获取到前端用户提交的数据信息(name,value),并存储到Map集合当中去。MapString,String〔〕parameterMaprequest。getParameterMap();遍历存储了前端用户提交数据的Map集合获取到Map当中所有的key值(也就是前端的用户提交的所有的name值)SetStringstringsparameterMap。keySet();获取到set的迭代器IteratorStringiteratorstrings。iterator();while(iterator。hasNext()){Stringnameiterator。next();System。out。print(name);根据key获取到对应的value值(也就是前端用户提交的value值数据)String〔〕valueparameterMap。get(name);遍历value数组for(Strings:value){System。out。print(s);}System。out。println();}}} 第三步:对相关的Servlet类配置到对应webapp项目的web。xml文件中去。?xmlversion1。0encodingUTF8?webappxmlnshttp:xmlns。jcp。orgxmlnsjavaeexmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:xmlns。jcp。orgxmlnsjavaeehttp:xmlns。jcp。orgxmlnsjavaeewebapp40。xsdversion4。0servletservletnameRequestServletTestservletnameservletclasscom。RainbowSea。servlet。RequestServletTestservletclassservletservletmappingservletnameRequestServletTestservletnameurlpatternRequesturlpatternservletmappingwebapp 第四步:运行测试。 再举例:这里的Serlvelt获取前端用户提交的数据使用:如下两个方法EnumerationStringgetParameterNames()这个是获取Map集合中所有的key就是前端中所有的name值String〔〕getParameterValues(Stringname)根据(前端的name值)key获取Map集合的(前端的value值)valuepackagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEimportjava。util。EpublicclassRequestServletTestextendsHttpServlet{OverrideprotectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{获取到前端用户提交的数据的所有name值,并存储到EnumerationString〔〕枚举泛型中EnumerationStringnamesrequest。getParameterNames();while(names。hasMoreElements()){判断是否还有数据,有返回true,没有返回falseStringnamenames。nextElement();获取到其中上述EnumerationString存储到的元素数据,System。out。print(name);同时向下移动下标String〔〕valuesrequest。getParameterValues(name);根据name值获取到对应的value值,前端用户提交的。并存储到String〔〕字符串数组当中去。遍历数组for(Stringv:values){System。out。print(v);}System。out。println();}}} 再举例:这里的Serlvelt获取前端用户提交的数据使用:如下两个方法StringgetParameter(Stringname)获取value这个一维数组当中的第一个元素。这个方法最常用。String〔〕getParameterValues(Stringname)根据(前端的name值)key获取Map集合的(前端的value值)value 注意:上述两个方法的,合理使用,当我们的name的对应多个value值的话,需要使用getParameterValues()获取到其中的多个value值,而如果这是时候,我们使用的是getParameter()方法的话,就仅仅只会获取到其中的第一个value值,无法获取到对应name后面的value值的。packagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEpublicclassRequestServletTestextendsHttpServlet{OverrideprotectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{注意:这里直接指明对应的name值,我们最好去前端编写的html当直接复制其中的name值不然你可以会手动编写错误,哪怕只有其中的一个字母错误了,该方法都是无法获取到对应前端提交的value数据的。所以为了避免错误,建议直接复制。Stringusernamerequest。getParameter(username);因为该name的value值只有一个System。out。println(usernameusername);Stringuserpasswordrequest。getParameter(userpassword);同样的该name值的value也是只有一个System。out。println(userpassworduserpassword);注意:该如下名为aihaos的value值是一个多个值的,需要使用数组存储起来。String〔〕aihaosrequest。getParameterValues(aihao);System。out。print(aihaos);for(Strings:aihaos){System。out。print(s);}}} 直接手动编写name值的注意事项 如果我们使用如下方法,其中的参数name是我们手动编写的话,存在一个安全隐患,就是:如果我们手动输入的name的值,在我们对应前端当中不存在的话(因为当其中的我们手动编写错了,比如说,多少了字母的话),就无法获取到其中对应的value值了。StringgetParameter(Stringname)获取value这个一维数组当中的第一个元素。这个方法最常用。 举例如下: 4。2请求域对象的详解 HttpServletRequest对象实际上又称为请求域对象。 这里我们回顾一下应用域对象是什么? ServletContext应用域对象(Servlet上下文对象)。 什么情况下会考虑向ServletContext这个应用域当中绑定数据呢? 第一:所有用户的共享数据第二:这个共享数据量很少第三:这个共享数据很少进行修改操作,尽可能没有修改操作。在以上三个条件都满足的情况下,使用这个应用域对象,可以大大提高我们程序执行效率。实际上向应用域当中绑定数据,就相当于把数据放到了缓存(Cache)当中,然后用户访问的时候直接从缓存中取,减少IO的操作(IO访问磁盘,其耗费的时间代价十分的大),大大提升系统的性能,所以缓存技术是提高系统性能的重要手段。关于ServletContext具体的使用大家可以移步至:解读Servlet源码:GenericServlet,ServletConfig,ServletContextChinaRainbowSea的博客CSDN博客 你见过哪些缓存技术呢? 字符串常量池整数型常量池〔128127〕,但凡是在这个范围当中Integer对象不再创建新对象,而是直接从这个整数型常量池中获取,大大提升系统性能。数据库连接池(提前创建好N个连接对象,将连接对象放到集合当中,使用连接对象的时候,直接从缓存中拿,省去了连接对象的创建过程,效率提升。)线程池(Tomcat服务器就是支持多线程的),所谓的线程池就是提前先创建好N个线程对象,将线程对象存储到集合中,然后用户直接去线程池当中获取线程对象,直接拿来用。提升系统性能。)后期你还会学习更多的缓存技术,例如:redis、mongoDB。。。。。 拉回来,这里我们的主角是请求域对象:请求域对象要比应用域对象范围小很多。生命周期短很多。请求域只在一次请求内有效。一个请求对象request对应一个请求域对象。一次请求结束之后,这个请求域就销毁了。请求域对象也有这三个方法:voidsetAttribute(Stringname,Objectobj);向请求域当中绑定数据。ObjectgetAttribute(Stringname);从请求域当中根据name获取数据。注意:这里的参数是你向请求域添加数据时,设置的对应数据的,setAttribute(Stringname,Objectobj)一个name值保持一致,尽可以使用复制的方式,防止手动编写错误。导致无法找到,从而为null值。voidremoveAttribute(Stringname);将请求域当中绑定的数据移除以上的操作类似于Map集合的操作。MapString,Omap。put(name,obj);向map集合中放key和valueObjectobjmap。get(name);通过map集合的key获取valuemap。remove(name);通过Map集合的key删除key和value这个键值对。 请求域和应用域的选用原则? 尽量使用小的域对象,因为小的域对象占用的资源较少。 举例:1。将数据存储到请求域当中,2。从请求域当中取出数据packagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEimportjava。io。PrintWimportjava。util。DpublicclassAServletextendsHttpServlet{OverrideprotectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{设置,在浏览器上响应的格式类型response。setContentType(charSetutf8);PrintWriterwriterresponse。getWriter();DatenowTimenewDate();创建当前时间的Date对象1。将nowTime的数据存储(绑定)到请求域当中request。setAttribute(sysTime,nowTime);2。取出请求域当中的数据:这里的name值与上面setAttribute(Stringname,Objectobj)保持一致。ObjectsysTimerequest。getAttribute(sysTime);writer。println(sysTime);显示到浏览器页面当中的数据}}?xmlversion1。0encodingUTF8?webappxmlnshttp:xmlns。jcp。orgxmlnsjavaeexmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:xmlns。jcp。orgxmlnsjavaeehttp:xmlns。jcp。orgxmlnsjavaeewebapp40。xsdversion4。0servletservletnameAServletservletnameservletclasscom。RainbowSea。servlet。AServletservletclassservletservletmappingservletnameAServletservletnameurlpatternAurlpatternservletmappingwebapp 关于request对象中两个非常容易混淆的方法: uri?usernamezhangsanuserpwd123sex1Stringusernamerequest。getParameter(username);获取到的是前端用户提交的数据之前一定是执行过:request。setAttribute(name,newObject())将数据绑定存储到请求域当中Objectobjrequest。getAttribute(name);获取到的是绑定到请求域当中的数据以上两个方法的区别是什么?第一个方法:获取的是用户在浏览器上提交的数据。第二个方法:获取的是请求域当中绑定的数据。4。3跳转(两个Servlet共享数据) 如果我们想要将两个Servlet的请求域当中的数据共享:比如将AServlet类当中的请求域存储的数据,在BServelt类当中将其存储到AServlet请求域当中的数据取出来。如下 观察,思考如下代码:是否可以实现两个Servlet类的数据共享packagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEimportjava。io。PrintWimportjava。util。DpublicclassAServletextendsHttpServlet{OverrideprotectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{设置,在浏览器上响应的格式类型response。setContentType(charSetutf8);PrintWriterwriterresponse。getWriter();DatenowTimenewDate();创建当前时间的Date对象将nowTime的数据存储(绑定)到请求域当中request。setAttribute(sysTime,nowTime);这样做可以吗?在AServlet当中new一个BServlet对象,然后调用BServlet对象的doGet()方法,把request对象传过去因为我们数据是存储该request请求域当中的。所以我们将该数据传给BServlet,让BServlet将其中的请求域当中的数据取出来,这么做可以吗?BServletbServletnewBServlet();bServlet。doGet(request,response);}}packagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEimportjava。io。PrintWpublicclassBServletextendsHttpServlet{OverrideprotectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{设置,在浏览器上响应的格式类型response。setContentType(charSetutf8);PrintWriterwriterresponse。getWriter();取出请求域当中的数据:这里的name值与上面setAttribute(Stringname,Objectobj)保持一致。ObjectsysTimerequest。getAttribute(sysTime);writer。println(sysTimesysTime);显示到浏览器页面当中的数据}} 如下:运行结果 注意: 注意:上述这个代码虽然可以实现功能,但是到那时Servlet对象不能自己由程序员来new,因为自己new的Servlet的对象的生命周期不受Tomcat服务器的管理。如果不是被Tomcat管理了就无法实现合理的关闭销毁对应的Servlet的资源了。 哪要如何合理的将两个Servlet数据共享呢? 可以,使用转发机制。将数据放到ServletContext应用域当中,当然是可以的,但是应用域范围太大,占用资源太多。不建议使用。可以将数据放到request域当中,然后AServlet转发到BServlet,保证AServlet和BServlet在同一次请求当中,这样就可以做到两个Servlet,或者多个Servlet共享同一份数据。转发(一次请求的核心方法)第一步:获取请求转发器对象注意:转发的时候,路径的写法要注意,转发的路径以开始,不加项目名。后接对应转发的Servelt在web。xml配置文件当中uRl映射路径即可。RequestDispatcherdispatcherrequest。getRequestDispatcher(B);第二步:调用转发器的forward方法完成跳转转发dispatcher。forward(request,response);第一步和第二步代码可以联合在一起。request。getRequestDispatcher(B)。forward(request,response); 举例:转发机制,将AServlett类当中的信息转发到BServlet当中去packagecom。RainbowSea。importjavax。servlet。RequestDimportjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEimportjava。util。DpublicclassAServletextendsHttpServlet{OverrideprotectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{设置,在浏览器上响应的格式类型DatenowTimenewDate();创建当前时间的Date对象将nowTime的数据存储(绑定)到请求域当中request。setAttribute(sysTime,nowTime);第一步:获取到转发对象,注意:开始,不家项目名,对应跳转的Servlet当中的web。xml当中的url映射的路径RequestDispatcherrequestDispatcherrequest。getRequestDispatcher(B);第二步:调用转发器的forward方法完成跳转转发requestDispatcher。forward(request,response);}}packagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEimportjava。io。PrintWpublicclassBServletextendsHttpServlet{OverrideprotectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{设置,在浏览器上响应的格式类型response。setContentType(charSetutf8);PrintWriterwriterresponse。getWriter();取出请求域当中的数据:这里的name值与上面setAttribute(Stringname,Objectobj)保持一致。ObjectsysTimerequest。getAttribute(sysTime);writer。println(sysTimesysTime);显示到浏览器页面当中的数据}} 转发的下一个资源必须是一个Servlet吗? 不一定,只要是Tomcat服务器当中的合法资源,都是可以转发的。例如:html。。。。 举例:转发一个html文件 注意:如果对应的不是Servlet,默认是从项目的中的web目录开始的,如果是转发web的目录下的子目录的话,需要指定对应的子目录的文件。 packagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEpublicclassTestServletextendsHttpServlet{OverrideprotectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{转发的下一个资源不一定是Servlet资源,只要是Tomcat服务器当中合法的资源,都是可以转发的,例如:html。。。注意:转发的时候,路径的写法要注意,转发的路径以开始,不加项目名默认是从项目的中的web目录开始的,如果是转发web的目录下的子目录的话,需要指定对应的子目录如下是含有子目录的表示web目录request。getRequestDispatcher(testtest。html)。forward(request,response);}} 4。4post请求request乱码问题? 如下:如果我们在前端post请求中提交中文字符串信息,当我们在后端Servlet接受的时候,会存在一个乱码问题?但是英文不会存在这个现象。如下:所示!DOCTYPEhtmlhtmllangenheadmetacharsetUTF8titleHTTP请求登录titleheadbodyh2Post请求h2!action项目名web。xml当中url的映射的路径bodyhtmlpackagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEpublicclassTestServletextendsHttpServlet{OverrideprotectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{Stringusernamerequest。getParameter(username);System。out。println(usernameusername);Stringuserpswdrequest。getParameter(userpswd);System。out。println(userpswduserpswd);}} 如下我们:使用中文,提交数据出现,乱码。 解决方案: 在显示获取前端post请求时,执行如下代码就可以解决post乱码问题?注意:仅仅只能解决Post请求的乱码问题,不能解决get请求的乱码问题。post请求在请求体中提交数据。设置请求体的字符集。(显然这个方法是处理POST请求的乱码问题。这种方式并不能解决get请求的乱码问题。)Tomcat10之后,request请求体当中的字符集默认就是UTF8,不需要设置字符集,不会出现乱码问题。Tomcat9前(包括9在内),如果前端请求体提交的是中文,后端获取之后出现乱码,怎么解决这个乱码?执行以下代码。request。setCharacterEncoding(UTF8);packagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEpublicclassTestServletextendsHttpServlet{OverrideprotectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{post请求在请求体中提交数据。设置请求体的字符集。(显然这个方法是处理POST请求的乱码问题。这种方式并不能解决get请求的乱码问题。)Tomcat10之后,request请求体当中的字符集默认就是UTF8,不需要设置字符集,不会出现乱码问题。Tomcat9前(包括9在内),如果前端请求体提交的是中文,后端获取之后出现乱码,怎么解决这个乱码?执行以下代码。request。setCharacterEncoding(UTF8);Stringusernamerequest。getParameter(username);System。out。println(usernameusername);Stringuserpswdrequest。getParameter(userpswd);System。out。println(userpswduserpswd);}} 4。5response响应到浏览器中的乱码问题? 如果我们想在Servlet直接向前端页面中响应中文信息,会存在乱码问题。如下:packagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEimportjava。io。PrintWpublicclassTestServletextendsHttpServlet{OverrideprotectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{设置在页面当显示的格式类型response。setContentType(texthtml);PrintWriterwriterresponse。getWriter();writer。println(h1你好世界h1);}} 解决方案:在Tomcat9之前(包括9),响应中文也是有乱码的,怎么解决这个响应的乱码?可以在响应操作之前,先执行如下代码,设置在页面当显示的格式,以及字符集编码response。setContentType(charsetUTF8);在Tomcat10之后,包括10在内,响应中文的时候就不在出现乱码问题了。以上代码就不需要设置UTF8了。packagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEimportjava。io。PrintWpublicclassTestServletextendsHttpServlet{OverrideprotectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{在Tomcat9之前(包括9),响应中文也是有乱码的,怎么解决这个响应的乱码?response。setContentType(charsetUTF8);在Tomcat10之后,包括10在内,响应中文的时候就不在出现乱码问题了。以上代码就不需要设置UTF8了。PrintWriterwriterresponse。getWriter();writer。println(h1你好世界h1);}} 4。6get请求的request乱码问题? 如果我们使用get请求中,处理中文乱码问题。!DOCTYPEhtmlhtmllangenheadmetacharsetUTF8titleHTTP请求登录titleheadbodyh2get请求h2!action项目名web。xml当中url的映射的路径bodyhtmlpackagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEpublicclassTestServletextendsHttpServlet{OverrideprotectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{Stringusernamerequest。getParameter(username);System。out。println(usernameusername);Stringuserpswdrequest。getParameter(userpswd);System。out。println(userpswduserpswd);}} 解决方案: 首先找我们安装的Tomcat的安装目录,在conf目录下,找到一个名为server。xml的文件名,如下 打开该server。xml文件,找到其中的如下这一段代码 Connectorport8080protocolHTTP1。1connectionTimeout20000redirectPort8443 在该server。xml配置文件当中的上述对应的一段代码上,多添加上一个字符集设置URIEncodingUTF8注意结尾就可以了。Connectorport8080protocolHTTP1。1connectionTimeout20000redirectPort8443URIEncodingUTF8get请求乱码问题怎么解决?get请求发送的时候,数据是在请求行上提交的,不是在请求体当中提交的。get请求乱码怎么解决方案:修改CATALINAHOMEconfserver。xml配置文件ConnectorURIEncodingUTF8注意:从Tomcat8之后,URIEncoding的默认值就是UTF8,所以GET请求也没有乱码问题了。 4。7HttpServletRequest接口的其他常用方法获取客户端的IP地址StringremoteAddrrequest。getRemoteAddr();。获取应用的根路径StringcontextPathrequest。getContextPath();获取请求方式Stringmethodrequest。getMethod();获取请求的URIStringurirequest。getRequestURI();获取servletpathStringservletPathrequest。getServletPath(); 举例上述方法的使用: ?xmlversion1。0encodingUTF8?webappxmlnshttp:xmlns。jcp。orgxmlnsjavaeexmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:xmlns。jcp。orgxmlnsjavaeehttp:xmlns。jcp。orgxmlnsjavaeewebapp40。xsdversion4。0servletservletnameTestServletservletnameservletclasscom。RainbowSea。servlet。TestServletservletclassservletservletmappingservletnameTestServletservletnameurlpatternTesturlpatternservletmappingwebapppackagecom。RainbowSea。importjavax。servlet。ServletEimportjavax。servlet。http。HttpSimportjavax。servlet。http。HttpServletRimportjavax。servlet。http。HttpServletRimportjava。io。IOEpublicclassTestServletextendsHttpServlet{OverrideprotectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{获取客户端的IP地址Stringiprequest。getRemoteAddr();System。out。println(客户端的ip地址:ip);获取用户的请求方式Stringmethodrequest。getMethod();System。out。println(用户的请求方式:method);获取webapp的根路径StringcontextPathrequest。getContextPath();System。out。println(webapp的根路径:contextPath);获取请求的URIStringrequestURIrequest。getRequestURI();System。out。println(请求的URI:requestURI);获取Servletpath路径StringservletPathrequest。getServletPath();System。out。println(Servlet的路径:servletPath);}} 5。补充: 在WEBINF目录下新建了一个文件:welcome。html打开浏览器访问:http:localhost:8080servlet07WEBINFwelcome。html出现了404错误。 注意:放在WEBINF目录下的资源是受保护的。在浏览器上不能够通过路径直接访问。所以像HTML、CSS、JS、image等静态资源一定要放到WEBINF目录之外。如果非要访问的话,也有方法,详细内容请关注我,后续为您更新。6。总结:HTTP的请求协议包括:如下4个部分:请求行,请求头,空白行,请求体。HTTP的响应协议(S(浏览器)B(客户端))HTTP的响应协议包括4部分:状态行,响应头,空白行,响应体。GET请求和POST请求有什么区别?不管你是get请求还是post请求,发送的请求数据格式是完全相同的,只不过位置不同,格式都是统一的:都是:namevaluenamevaluenamevaluenamevalueGET请求和POST请求如何选择,什么时候使用GET请求,什么时候使用POST请求?HttpServlet类是专门为HTTP协议准备的。比GenericServlet更加适合HTTP协议下的开发。HttpServlet在哪个包下?这是在Tomcat10的基础上:jakarta。servlet。http。HttpServlet,而后面Tomcat9之前的(包括Tomcat9)以内的包是在:javax。servlet。http。HttpServlet包下的。只要没有重写对应HttpServlet类中的doGet方法或doPost方法,就会报405错误,因为没有重写了doGet或doPost方法就会执行其中的HttpServlet当中编写的doGetdoPost方法,而如果执行了的是HttpServlet当中doGetdoPost方法就会报405错误,提示你没有重写对应的doGetdoPost方法。怎么避免405的错误呢?后端重写了doGet方法,前端一定要发get请求。后端重写了doPost方法,前端一定要发post请求。这样可以避免405错误。这种前端到底需要发什么样的请求,其实应该后端说了算。后端让发什么方式,前端就得发什么方式。request对象和response对象,一个是请求对象,一个是响应对象。这两个对象只在当前请求中有效。一次请求对应一个request。两次请求则对应两个request。后端如何获取前端浏览器用户提交的数据的常用方法。MapString,String〔〕getParameterMap()这个是获取用户提交的数据,并存储到Map集合当中前端的name,前端的valueEnumerationStringgetParameterNames()这个是获取Map集合中所有的key就是前端中所有的name值String〔〕getParameterValues(Stringname)根据(前端的name值)key获取Map集合的(前端的value值)valueStringgetParameter(Stringname)获取value这个一维数组当中的第一个元素。这个方法最常用。以上的4个方法,和获取用户提交的数据有关系注意:这里为什么value的值是用String〔〕数组存储的。后面有说明。HttpServletRequest对象又称为:请求域请求域对象要比应用域对象范围小很多。生命周期短很多。请求域只在一次请求内有效。一个请求对象request对应一个请求域对象。一次请求结束之后,这个请求域就销毁了。如下是请求域的增删改操作的方法。voidsetAttribute(Stringname,Objectobj);向请求域当中绑定数据。ObjectgetAttribute(Stringname);从请求域当中根据name获取数据。注意:这里的参数是你向请求域添加数据时,设置的对应数据的,setAttribute(Stringname,Objectobj)一个name值保持一致,尽可以使用复制的方式,防止手动编写错误。导致无法找到,从而为null值。voidremoveAttribute(Stringname);将请求域当中绑定的数据移除以上的操作类似于Map集合的操作。MapString,Omap。put(name,obj);向map集合中放key和valueObjectobjmap。get(name);通过map集合的key获取valuemap。remove(name);通过Map集合的key删除key和value这个键值对。跳转(两个Servlet共享数据)Post请求以及get请求,response响应的三者的中文乱码问题。HttpServletRequest接口的常用方法。