经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Servlet » 查看文章
Sevlet规范:HttpServlet类 和 HttpServletRequest接口 源码解析 - Rainbow-Sea
来源:cnblogs  作者:Rainbow-Sea  时间:2023/4/6 8:44:35  对本文有异议

Sevlet规范:HttpServlet类 和 HttpServletRequest接口 源码解析

在这里插入图片描述

每博一文案

  1. 命运总是不如人愿,但往往是在无数的痛苦总,在重重的矛盾和艰辛中,才是人成熟起来。
  2. 你,为这瞬间的辉煌,忍耐了多少暗淡无光的日月,你会死亡,但你也会证明生命有多强大。
  3. 一次邂逅,一次目光的交融,就是永远的合二为一,就是与上帝的契约;总是风暴雷电,
  4. 也无法分解这种心灵的粘结。
  5. 直到在哪远离故乡的地方发生过那场刻苦铭心的感谢悲剧后,他才理解了人活在世界上有多少幸福,
  6. 又有多少苦难!生活不能等待别人来安排,要自己去争取和奋斗;而不论其结果是喜和悲,但可以慰籍,你总不枉在这
  7. 世界上活了一场,有了这样的认识,你就会珍重生活,而不会玩世不恭;同时也会给人自身注入一种强大的内在力量......
  8. —————— 《平凡的世界》路遥

@

1. HTTP协议解读

什么是协议?

  • 协议实际上是某些人,或者某些组织提前制定好的一套规范,大家都按照这个规范来,这样可以做到沟通无障碍。
  • 协议就是一套规范,就是一套标准。由其他人或其他组织来负责制定的。
  • 我说的话你能听懂,你说的话,我也能听懂,这说明我们之间是有一套规范的,一套协议的,这套协议就是:中国普通话协议。我们都遵守这套协议,我们之间就可以沟通无障碍。但是如果我们遵守不同的协议规范的话,就无法通信了,比如:你说的是阿拉伯语,我说的是汉语,我听不懂你说什么,你也听不同我说什么。你我两者之间无法通信交流。

1.1 什么是HTTP协议 ?

  • HTTP协议:是W3C制定的一种超文本传输协议。(通信协议:发送消息的模板提前被制定好。)
  • W3C:https://www.w3.org/
    • 万维网联盟组织
    • 负责制定标准的:HTTP HTML4.0 HTML5 XML DOM等规范都是W3C制定的。
    • 万维网之父:蒂姆·伯纳斯·李

什么是超文本 ?

  • 超文本说的就是:不是普通文本,比如流媒体:声音、视频、图片等。

  • HTTP协议支持:不但可以传送普通字符串,同样支持传递声音、视频、图片等流媒体信息。

  • 这种协议游走在B (浏览器)和S (服务器)之间。BS发数据要遵循HTTP协议。SB发数据同样需要遵循HTTP协议。这样B和S才能解耦合

什么是解耦合?

这里指定是: B (浏览器) 不 依赖 S(服务器) ,S 也不依赖 B

B/S表示:B/S结构的系统(浏览器访问WEB服务器的系统)

HTTP协议的请求 (request) 和 响应(response)

  • 浏览器 向 ?? WEB服务器发送数据,叫做:请求(request)
  • WEB服务器 向 ?? 浏览器发送数据,叫做:响应(response)

HTTP协议包括:

  • 请求协议
    • 浏览器 向?? WEB服务器发送数据的时候,这个发送的数据需要遵循一套标准,这套标准中规定了发送的数据具体格式。
  • 响应协议
    • WEB服务器 向 ?? 浏览器发送数据的时候,这个发送的数据需要遵循一套标准,这套标准中规定了发送的数据具体格式。

HTTP协议总结:

HTTP协议就是提前制定好的一种消息模板。

  • 不管你是哪个品牌的浏览器,都是这么发,同样的不管你是哪个品牌的WEB服务器,都是这么发。
  • 火狐浏览器 可以向 Tomcat发送请求,也可以向Jetty服务器发送请求。浏览器不依赖具体的服务器品牌。
  • WEB服务器也不依赖具体的浏览器品牌。可以是FF浏览器,也可以是Chrome浏览器,可以是IE,都行。

1.2 HTTP请求协议的具体报文

注意:HTTP请求协议中有多种请求方式。这里我们说的是常用的 get 请求和 post 请求。在该文章的下文有更加详细的说明。

HTTP的请求协议(B (浏览器)-> S(服务器))

HTTP的请求协议包括:如下 4 个部分: 请求行,请求头,空白行,请求体

如何查看我们提交的数据报文,在浏览器当中

  1. 我们可以在提交数据的时候按 F12 快捷键查看,或者是右键鼠标浏览器 ,在弹出的窗口当中,选择点击 检查。如下图所示:

在这里插入图片描述

  1. 在选择点击检查按钮之后,弹出如下窗口,选择其中的 Network (网络)选项。如下图所示:只要当我们在前端提交了数据,就可以在如下的窗口当中捕获到并显示出相应的报文信息。

在这里插入图片描述

  1. 测试如下,看看是否可以捕获到我们的请求报文的信息数据。如下图所示:

在这里插入图片描述

  1. 查看我们提交的数据信息:点击我们捕获到的报文信息,再点击 ——> Paload 选项。

在这里插入图片描述

如下是:HTTP请求协议的具体报文:GET请求

首先编写一个是 Get 请求提交的表单 html 文件,具体的代码编写如下:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>HTTP请求登录</title>
  6. </head>
  7. <body>
  8. <h2>get 请求</h2>
  9. <form action="" method="get">
  10. username: <input type="text" name="username" /> <br>
  11. userpassword: <input type="password" name="userpswd" /> <br>
  12. <input type="submit" value="get" />
  13. </form>
  14. <h2>post 请求</h2>
  15. <form action="" method="post">
  16. username: <input type="text" name="username" /> <br>
  17. userpassword: <input type="password" name="userpswd" /> <br>
  18. <input type="submit" value="post" />
  19. </form>
  20. </body>
  21. </html>

在这里插入图片描述

  1. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
  2. Accept-Encoding: gzip, deflate, br
  3. Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
  4. Connection: keep-alive
  5. # 下面一行是请求行
  6. Host: 127.0.0.1:8080
  7. # 下面的是请求头
  8. Referer: http://127.0.0.1:8080/servlet08/login.html
  9. sec-ch-ua: "Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"
  10. sec-ch-ua-mobile: ?0
  11. sec-ch-ua-platform: "Windows"
  12. Sec-Fetch-Dest: document
  13. Sec-Fetch-Mode: navigate
  14. Sec-Fetch-Site: same-origin
  15. Sec-Fetch-User: ?1
  16. Upgrade-Insecure-Requests: 1
  17. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
  18. # 下面是空白行:用于分隔请求头 与 请求体
  19. # 下面是请求体:向服务器发送的具体数据。
  20. username=Hello&userpswd=123

如下是另一种的格式下 get 请求的具体报文信息

  1. GET /servlet05/getServlet?username=lucy&userpwd=1111 HTTP/1.1
  2. # 请求行
  3. Host: localhost:8080
  4. # 请求头
  5. Connection: keep-alive
  6. sec-ch-ua: "Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"
  7. sec-ch-ua-mobile: ?0
  8. sec-ch-ua-platform: "Windows"
  9. Upgrade-Insecure-Requests: 1
  10. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
  11. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
  12. Sec-Fetch-Site: same-origin
  13. Sec-Fetch-Mode: navigate
  14. Sec-Fetch-User: ?1
  15. Sec-Fetch-Dest: document
  16. Referer: http://localhost:8080/servlet05/index.html
  17. Accept-Encoding: gzip, deflate, br
  18. Accept-Language: zh-CN,zh;q=0.9
  19. # 空白行
  20. # 请求体

如下 post 请求信息

在这里插入图片描述

  1. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
  2. Accept-Encoding: gzip, deflate, br
  3. Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
  4. Cache-Control: max-age=0
  5. Connection: keep-alive
  6. Content-Length: 25
  7. Content-Type: application/x-www-form-urlencoded
  8. # 请求头
  9. Host: 127.0.0.1:8080
  10. # 请求行
  11. Origin: http://127.0.0.1:8080
  12. Referer: http://127.0.0.1:8080/servlet08/login.html?username=Hello&userpswd=123
  13. sec-ch-ua: "Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"
  14. sec-ch-ua-mobile: ?0
  15. sec-ch-ua-platform: "Windows"
  16. Sec-Fetch-Dest: document
  17. Sec-Fetch-Mode: navigate
  18. Sec-Fetch-Site: same-origin
  19. Sec-Fetch-User: ?1
  20. Upgrade-Insecure-Requests: 1
  21. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
  22. # 空白行
  23. # 请求体

如下是另一种的格式下 post 请求的具体报文信息

  1. POST /servlet05/postServlet HTTP/1.1
  2. # 请求行
  3. Host: localhost:8080
  4. # 请求头
  5. Connection: keep-alive
  6. Content-Length: 25
  7. Cache-Control: max-age=0
  8. sec-ch-ua: "Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"
  9. sec-ch-ua-mobile: ?0
  10. sec-ch-ua-platform: "Windows"
  11. Upgrade-Insecure-Requests: 1
  12. Origin: http://localhost:8080
  13. Content-Type: application/x-www-form-urlencoded
  14. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
  15. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
  16. Sec-Fetch-Site: same-origin
  17. Sec-Fetch-Mode: navigate
  18. Sec-Fetch-User: ?1
  19. Sec-Fetch-Dest: document
  20. Referer: http://localhost:8080/servlet05/index.html
  21. Accept-Encoding: gzip, deflate, br
  22. Accept-Language: zh-CN,zh;q=0.9
  23. # 空白行
  24. # 请求体
  25. username=lisi&userpwd=123

请求行: 由三部分组成: 请求方式,URI ,HTTP版本号

  • 请求行:第一部分:请求方式 (7种): get(常用的),post(常用的),delete,put,head,options,trace 。

  • 请求行:第二部分: URI

URL : 统一资源定位符。 代表网络中某个资源。注意: 可以通过 URL 定位到该资源,可以直接在浏览器当中输入URI 访问网络当中的资源)。比如: http://localhost:8080/servlet05/index.html 这是URL。

URI : 统一资源标识符。代表网络中某个资源的名字。注意: URI 是无法定位资源的(就是无法通过直接在浏览器当中输入URI 访问网络当中的资源)。比如:/servlet05/index.html 这是URI。

URI 和 URL 什么关系,有什么区别?

URL 是包括了 URI 的。

  • 请求行:第三部分: HTTP协议版本号

请求头: 包含 : 请求的主机的IP地址,主机的端口号,浏览器信息,平台信息,cookie 等信息。

空白行: 空白行是用来区分“请求头”和“请求体”

请求体: 向服务器发送的具体数据。

1.3 HTTP响应协议的具体报文

HTTP的响应协议(S(浏览器) --> B(客户端))HTTP的响应协议包括4部分 : 状态行,响应头,空白行,响应体

HTTP响应协议的具体报文:

在这里插入图片描述

在这里插入图片描述

  1. Accept-Ranges: bytes
  2. Connection: keep-alive
  3. Content-Length: 598
  4. Content-Type: text/html # 响应头
  5. Date: Thu, 30 Mar 2023 02:48:49 GMT
  6. ETag: W/"598-1680144315095"
  7. Keep-Alive: timeout=20
  8. Last-Modified: Thu, 30 Mar 2023 02:45:15 GMT
  9. # 空白行
  10. <!DOCTYPE html> # 响应体:其实就是源代码的信息
  11. <html lang="en">
  12. <head>
  13. <meta charset="UTF-8">
  14. <title>HTTP请求登录</title>
  15. </head>
  16. <body>
  17. <h2>get 请求</h2>
  18. <form action="" method="get">
  19. username: <input type="text" name="username" /> <br>
  20. userpassword: <input type="password" name="userpswd" /> <br>
  21. <input type="submit" value="get" />
  22. </form>
  23. <h2>post 请求</h2>
  24. <form action="" method="post">
  25. username: <input type="text" name="username" /> <br>
  26. userpassword: <input type="password" name="userpswd" /> <br>
  27. <input type="submit" value="post" />
  28. </form>
  29. </body>
  30. </html>

另外一种 post 响应的具体数据报文信息。

  1. HTTP/1.1 200 ok # 状态行
  2. Content-Type: text/html;charset=UTF-8 # 响应头
  3. Content-Length: 160
  4. Date: Mon, 08 Nov 2021 13:19:32 GMT
  5. Keep-Alive: timeout=20
  6. Connection: keep-alive
  7. # 空白行
  8. <!DOCTYPE html> # 响应体:其实就是源代码的信息
  9. <html lang="en">
  10. <head>
  11. <meta charset="UTF-8">
  12. <title>HTTP请求登录</title>
  13. </head>
  14. <body>
  15. <h2>get 请求</h2>
  16. <form action="" method="get">
  17. username: <input type="text" name="username" /> <br>
  18. userpassword: <input type="password" name="userpswd" /> <br>
  19. <input type="submit" value="get" />
  20. </form>
  21. <h2>post 请求</h2>
  22. <form action="" method="post">
  23. username: <input type="text" name="username" /> <br>
  24. userpassword: <input type="password" name="userpswd" /> <br>
  25. <input type="submit" value="post" />
  26. </form>
  27. </body>
  28. </html>

状态行 : 由三部分组成:协议版本号,状态码,状态描述信息。

  • 状态行,第一部分: 协议版本号 (HTTP/1.1)
  • 状态行,第二部分: 状态码(HTTP)协议中规定的响应状态号,不同的响应结果对应不同的号码)。

在这里插入图片描述

如下是一些常见的状态码的意义:

  • 200 : 表示请求响应成功,正常结束。
  • 404: 表示访问的资源不存在,通常是因为要么你填写的路径写错了,要么是你路径写对了,但是服务器中对应的 资源并没有启动成。总之 404 错误是基本是前端错误。
  • 405 : 表示前端发送的请求方式与后端接受处理(该请求)的方式不一致时发生的:基本上时如下两种情况:
    • 前端是POST请求,后端的处理方式按照get方式进行处理时,发生405
    • 前端是GET请求,后端的处理方式按照post方式进行处理时,发生405
    • 具体的该文章后面有详细的说明。所以请不要走开。
  • 500 表示服务器端的程序出现了异常。一般会认为是服务器端的错误导致的。
  • 总结:
    • 以4开始的,一般是浏览器端的错误导致的。
    • 以5开始的,一般是服务器端的错误导致的。

状态行,第三部分: 状态的描述信息:

  • OK 表示正则成结束。
  • not found: 表示资源找不到。

空白行: 用来分隔“响应头”和“响应体”的。

响应体: 响应体就是响应的正文,这些内容是一个长的字符串,这个字符串被浏览器渲染,解释并执行,最终展示出效果。简单的说就是html 对应的源代码。

2. GET请求和POST请求有什么区别?

怎么向服务器发送Get 请求,怎么向服务器发送 Post 请求?

到目前为止,只有一种情况可以发送post 请求:就是用 form 表单,并且 form 标签当中的 method 的属性值必须为 method = "post" 才行。

其他所有情况一律都是get请求:

  • 在浏览器地址栏上直接输入URL,敲回车,属于get请求。
  • 在浏览器上直接点击超链接,属于get请求。
  • 使用form表单提交数据时,form标签中没有写method属性,默认就是get
  • 或者使用form的时候,form标签中method属性值为:method="get"
  • ....

GET请求和POST请求有什么区别 ?

Get请求:

  • get 请求发送数据的时候,数据会挂在URI的后面,并且在 URI 后面添加一个 "?" ," ? " 后面的就是数据了,这样会导致发送的数据回显到浏览器的地址栏上如下显示的:。(get 请求在 “请求行”上发送数据)。

在这里插入图片描述

  • get 请求只能发送普通的字符串数据,并且发送字符串的长度有限制,不同的浏览器限制不同,这个没有明确的规范。并不能发送流媒体信息:比如:图片,声音,视频等等.
  • get 请求无法发送大数据量。
  • get 请求在 W3C 中是这样介绍的:get 请求比较适合从服务器端获取数据。
  • get 请求是安全的,get 请求是安全的,因为:get 请求只是为了从服务器上获取数据,不会对服务器造成威胁,注意:get请求本身是安全的,你不要用错了,用错了之后,就冤枉get 请求不安全。这不是 get 请求的问题,而是你使用的问题。比如说:一个注册表单的信息的提交,应该使用的 post 请求,而却使用的是 get 请求,导致提交的信息回显到了地址栏上了。
  • get 请求是支持缓存了。

补充点:

Post请求

  • post 请求发送的数据的时候,在请求体当中发送的,不会回显到浏览器的地址栏上,也就是说 Post 发送的数据,在浏览器的地址栏上不能看到的。(post 请求在 “请求体”当中发送数据)。
  • post请求可以发送任何类型的数据,包括普通字符串,流媒体等信息:图片,视频,声音等。
  • post请求可以发送大数据量,理论上没有限制的。
  • post 请求在 W3C 是这样说的 : post 请求比较适合向服务器传送数据。
  • post 请求是危险的,因为: post 请求时向服务器提交数据,如果这些数据是通过后门的方式进入到服务器当中,服务器是很危险的。另外post 是为了提交数据,所以一般情况下拦截请求的时候,大部分选择的是拦截(监听) post 请求。
  • post 是不支持缓存的。因为:(POST是用来修改服务器端的资源的。)post请求之后,服务器“响应的结果”不会被浏览器缓存起来。因为这个缓存没有意义。

2.1 GET请求和POST请求如何选择,什么时候使用GET请求,什么时候使用POST请求 ?

  • 怎么选择GET请求和POST请求呢?衡量标准是什么呢?你这个请求是想获取服务器端的数据,还是想向服务器发送数据。如果你是想从服务器上获取资源,建议使用GET请求,如果你这个请求是为了向服务器提交数据,建议使用POST请求。
  • 大部分的form表单提交,都是post方式,因为form表单中要填写大量的数据,这些数据是收集用户的信息,一般是需要传给服务器,服务器将这些数据保存/修改等。
  • 如果表单中有敏感信息,还是建议适用post请求,因为get请求会回显敏感信息到浏览器地址栏上。(例如:密码信息)
  • 做文件上传,一定是post请求。要传的数据不是普通文本。
  • 其他情况都可以使用get请求。

Get 请求与 Post 请求的共性

不管你是get请求还是post请求,发送的请求数据格式是完全相同的,只不过位置不同,格式都是统一的:都是 :name=value&name=value&name=value&name=value

其中的 name 表示:以 form 表单为例:form 表单中的 input 标签当中的 name 。

  1. <form>
  2. <input type="text" name="username" >
  3. </form>

其中的 value 表示的是:同样的以 form 表单为例 :form 表单中的 input 标签当中的 value。

  1. <form>
  2. interest:
  3. smoke<input type="checkbox" name="aihao" value="s"/>
  4. drink <input type="checkbox" name="aihao" value="d"/>
  5. tangtou <input type="checkbox" name="aihao" value="tt"/>
  6. </form>

3. HttpServlet源码分析

在这里插入图片描述

在这里插入图片描述

  • HttpServlet类是专门为 HTTP协议准备的。比 GenericServlet更加适合 HTTP协议下的开发。

  • HttpServlet在哪个包下?

    • 这是在 Tomcat 10 的基础上:jakarta.servlet.http.HttpServlet,而后面 Tomcat 9 之前的(包括Tomcat 9 )以内的包是在 : javax.servlet.http.HttpServlet 包下的。
  • 到目前为止我们接触了servlet规范中哪些接口 ?。注意: 如下的是基于 Tomcat 10 的基础上的,而 Tomcat 9 之前的包括 9 将如下的 jakarta 替换为 javax 就可以了,具体原因大家可以移步至:javaEE Web(Tomcat)深度理解 和 Servlet的本质_ChinaRainbowSea的博客-CSDN博客 中。

    • jakarta.servlet.Servlet 核心接口(接口)
    • jakarta.servlet.ServletConfig Servlet配置信息接口(接口)
    • jakarta.servlet.ServletContext Servlet上下文接口(接口)
    • jakarta.servlet.ServletRequest Servlet请求接口(接口)
    • jakarta.servlet.ServletResponse Servlet响应接口(接口)
    • jakarta.servlet.ServletException Servlet异常(类)
    • 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源码分析:

  1. public class HelloServlet extends HttpServlet {
  2. // 用户第一次请求,创建HelloServlet对象的时候,会执行这个无参数构造方法。
  3. public HelloServlet() {
  4. }
  5. //override 重写 doGet方法
  6. //override 重写 doPost方法
  7. }
  8. public abstract class GenericServlet implements Servlet, ServletConfig,
  9. java.io.Serializable {
  10. // 用户第一次请求的时候,HelloServlet对象第一次被创建之后,这个init方法会执行。
  11. public void init(ServletConfig config) throws ServletException {
  12. this.config = config;
  13. this.init();
  14. }
  15. // 用户第一次请求的时候,带有参数的init(ServletConfig config)执行之后,会执行这个没有参数的init()
  16. public void init() throws ServletException {
  17. // NOOP by default
  18. }
  19. }
  20. // HttpServlet模板类。
  21. public abstract class HttpServlet extends GenericServlet {
  22. // 用户发送第一次请求的时候这个service会执行
  23. // 用户发送第N次请求的时候,这个service方法还是会执行。
  24. // 用户只要发送一次请求,这个service方法就会执行一次。
  25. @Override
  26. public void service(ServletRequest req, ServletResponse res)
  27. throws ServletException, IOException {
  28. HttpServletRequest request;
  29. HttpServletResponse response;
  30. try {
  31. // 将ServletRequest和ServletResponse向下转型为带有Http的HttpServletRequest和HttpServletResponse
  32. request = (HttpServletRequest) req;
  33. response = (HttpServletResponse) res;
  34. } catch (ClassCastException e) {
  35. throw new ServletException(lStrings.getString("http.non_http"));
  36. }
  37. // 调用重载的service方法。
  38. service(request, response);
  39. }
  40. // 这个service方法的两个参数都是带有Http的。
  41. // 这个service是一个模板方法。
  42. // 在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成。
  43. protected void service(HttpServletRequest req, HttpServletResponse resp)
  44. throws ServletException, IOException {
  45. // 获取请求方式
  46. // 这个请求方式最终可能是:""
  47. // 注意:request.getMethod()方法获取的是请求方式,可能是七种之一:
  48. // GET POST PUT DELETE HEAD OPTIONS TRACE
  49. String method = req.getMethod();
  50. // 如果请求方式是GET请求,则执行doGet方法。
  51. if (method.equals(METHOD_GET)) {
  52. long lastModified = getLastModified(req);
  53. if (lastModified == -1) {
  54. // servlet doesn't support if-modified-since, no reason
  55. // to go through further expensive logic
  56. doGet(req, resp);
  57. } else {
  58. long ifModifiedSince;
  59. try {
  60. ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
  61. } catch (IllegalArgumentException iae) {
  62. // Invalid date header - proceed as if none was set
  63. ifModifiedSince = -1;
  64. }
  65. if (ifModifiedSince < (lastModified / 1000 * 1000)) {
  66. // If the servlet mod time is later, call doGet()
  67. // Round down to the nearest second for a proper compare
  68. // A ifModifiedSince of -1 will always be less
  69. maybeSetLastModified(resp, lastModified);
  70. doGet(req, resp);
  71. } else {
  72. resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
  73. }
  74. }
  75. } else if (method.equals(METHOD_HEAD)) {
  76. long lastModified = getLastModified(req);
  77. maybeSetLastModified(resp, lastModified);
  78. doHead(req, resp);
  79. } else if (method.equals(METHOD_POST)) {
  80. // 如果请求方式是POST请求,则执行doPost方法。
  81. doPost(req, resp);
  82. } else if (method.equals(METHOD_PUT)) {
  83. doPut(req, resp);
  84. } else if (method.equals(METHOD_DELETE)) {
  85. doDelete(req, resp);
  86. } else if (method.equals(METHOD_OPTIONS)) {
  87. doOptions(req,resp);
  88. } else if (method.equals(METHOD_TRACE)) {
  89. doTrace(req,resp);
  90. } else {
  91. //
  92. // Note that this means NO servlet supports whatever
  93. // method was requested, anywhere on this server.
  94. //
  95. String errMsg = lStrings.getString("http.method_not_implemented");
  96. Object[] errArgs = new Object[1];
  97. errArgs[0] = method;
  98. errMsg = MessageFormat.format(errMsg, errArgs);
  99. resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
  100. }
  101. }
  102. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  103. throws ServletException, IOException{
  104. // 报405错误
  105. String msg = lStrings.getString("http.method_get_not_supported");
  106. sendMethodNotAllowed(req, resp, msg);
  107. }
  108. protected void doPost(HttpServletRequest req, HttpServletResponse resp)
  109. throws ServletException, IOException {
  110. // 报405错误
  111. String msg = lStrings.getString("http.method_post_not_supported");
  112. sendMethodNotAllowed(req, resp, msg);
  113. }
  114. }
  115. /*
  116. 通过以上源代码分析:
  117. 假设前端发送的请求是get请求,后端程序员重写的方法是doPost
  118. 假设前端发送的请求是post请求,后端程序员重写的方法是doGet
  119. 会发生什么呢?
  120. 发生405这样的一个错误。
  121. 405表示前端的错误,发送的请求方式不对。和服务器不一致。不是服务器需要的请求方式。
  122. 通过以上源代码可以知道:只要HttpServlet类中的doGet方法或doPost方法执行了,必然405.
  123. 怎么避免405的错误呢?
  124. 后端重写了doGet方法,前端一定要发get请求。
  125. 后端重写了doPost方法,前端一定要发post请求。
  126. 这样可以避免405错误。
  127. 这种前端到底需要发什么样的请求,其实应该后端说了算。后端让发什么方式,前端就得发什么方式。
  128. 有的人,你会看到为了避免405错误,在Servlet类当中,将doGet和doPost方法都进行了重写。
  129. 这样,确实可以避免405的发生,但是不建议,405错误还是有用的。该报错的时候就应该让他报错。
  130. 如果你要是同时重写了doGet和doPost,那还不如你直接重写service方法好了。这样代码还能
  131. 少写一点。
  132. */

3.1 HttpServlet 处理 get 请求和 post 请求 源码分析

上面我们在:HTTP响应协议的具体报文模块当中提到的 状态行当中的状态码部分中的一个为 405 前端用户提交的get/post请求与后端服务器不一致导致的错误。如下测试

当: 前端发送的请求是 get 请求 。后端程序员重写的方法是doPost

对于前端用户提交数据的html 代码设计如下:注意:这里我们前端提交的是 get请求。

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>HTTP请求登录</title>
  6. </head>
  7. <body>
  8. <h2>get 请求</h2>
  9. <!--action = /项目名 + web.xml当中url的映射的路径-->
  10. <form action="/servlet08//Test" method="get">
  11. username: <input type="text" name="username"/> <br>
  12. userpassword: <input type="password" name="userpswd"/> <br>
  13. <input type="submit" value="get"/>
  14. </form>
  15. </body>
  16. </html>

在这里插入图片描述

如下的是对应后端服务器 Servlet 的代码设计。注意了,这里我们 Servlet 服务器端处理的是 doPost 请求的

  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. import java.io.PrintWriter;
  8. public class HttpServletTest extends HttpServlet {
  9. @Override
  10. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  11. IOException {
  12. // 设置浏览器当中显示的格式
  13. response.setContentType("text/html;charSet=utf-8");
  14. PrintWriter writer = response.getWriter();
  15. writer.println("<h1>Hello World<h1>");
  16. }
  17. }

如下运行结果: 报了 405 错误,原因是:我们前后端的处理请求的不一致。

在这里插入图片描述

当:前端发送的请求是post请求,后端程序员重写的方法是doGet 出现的错误

对于前端用户提交数据的html 代码设计如下:注意:这里我们前端提交的是 post请求。

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>HTTP请求登录</title>
  6. </head>
  7. <body>
  8. <h2>post 请求</h2>
  9. <!--action = /项目名 + web.xml当中url的映射的路径-->
  10. <form action="/servlet08//Test" method="post">
  11. username: <input type="text" name="username"/> <br>
  12. userpassword: <input type="password" name="userpswd"/> <br>
  13. <input type="submit" value="get"/>
  14. </form>
  15. </body>
  16. </html>

在这里插入图片描述

如下的是对应后端服务器 Servlet 的代码设计。注意了,这里我们 Servlet 服务器端处理的是 doGet 请求的。

  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. import java.io.PrintWriter;
  8. public class HttpServletTest extends HttpServlet {
  9. @Override
  10. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  11. IOException {
  12. // 设置浏览器当中显示的格式
  13. response.setContentType("text/html;charSet=utf-8");
  14. PrintWriter writer = response.getWriter();
  15. writer.println("<h1>Hello World<h1>");
  16. }
  17. }

如下运行结果: 报了 405 错误,原因是:我们前后端的处理请求的不一致。

在这里插入图片描述

通过以上源代码分析:

假设前端发送的请求是get请求,后端程序员重写的方法是doPost

假设前端发送的请求是post请求,后端程序员重写的方法是doGet
会发生什么呢?发生405这样的一个错误。405表示前端的错误,发送的请求方式不对。和服务器不一致。不是服务器需要的请求方式。

为什么为发生 405 错误呢?

我们从HttpServlet 源码上分析:

  1. // HttpServlet模板类。
  2. public abstract class HttpServlet extends GenericServlet {
  3. // 用户发送第一次请求的时候这个service会执行
  4. // 用户发送第N次请求的时候,这个service方法还是会执行。
  5. // 用户只要发送一次请求,这个service方法就会执行一次。
  6. @Override
  7. public void service(ServletRequest req, ServletResponse res)
  8. throws ServletException, IOException {
  9. HttpServletRequest request;
  10. HttpServletResponse response;
  11. try {
  12. // 将ServletRequest和ServletResponse向下转型为带有Http的HttpServletRequest和HttpServletResponse
  13. request = (HttpServletRequest) req;
  14. response = (HttpServletResponse) res;
  15. } catch (ClassCastException e) {
  16. throw new ServletException(lStrings.getString("http.non_http"));
  17. }
  18. // 调用重载的service方法。
  19. service(request, response);
  20. }
  21. // 这个service方法的两个参数都是带有Http的。
  22. // 这个service是一个模板方法。
  23. // 在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成。
  24. protected void service(HttpServletRequest req, HttpServletResponse resp)
  25. throws ServletException, IOException {
  26. // 获取请求方式
  27. // 这个请求方式最终可能是:""
  28. // 注意:request.getMethod()方法获取的是请求方式,可能是七种之一:
  29. // GET POST PUT DELETE HEAD OPTIONS TRACE
  30. String method = req.getMethod();
  31. // 如果请求方式是GET请求,则执行doGet方法。
  32. if (method.equals(METHOD_GET)) {
  33. long lastModified = getLastModified(req);
  34. if (lastModified == -1) {
  35. // servlet doesn't support if-modified-since, no reason
  36. // to go through further expensive logic
  37. doGet(req, resp);
  38. } else {
  39. long ifModifiedSince;
  40. try {
  41. ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
  42. } catch (IllegalArgumentException iae) {
  43. // Invalid date header - proceed as if none was set
  44. ifModifiedSince = -1;
  45. }
  46. if (ifModifiedSince < (lastModified / 1000 * 1000)) {
  47. // If the servlet mod time is later, call doGet()
  48. // Round down to the nearest second for a proper compare
  49. // A ifModifiedSince of -1 will always be less
  50. maybeSetLastModified(resp, lastModified);
  51. doGet(req, resp);
  52. } else {
  53. resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
  54. }
  55. }
  56. } else if (method.equals(METHOD_HEAD)) {
  57. long lastModified = getLastModified(req);
  58. maybeSetLastModified(resp, lastModified);
  59. doHead(req, resp);
  60. } else if (method.equals(METHOD_POST)) {
  61. // 如果请求方式是POST请求,则执行doPost方法。
  62. doPost(req, resp);
  63. } else if (method.equals(METHOD_PUT)) {
  64. doPut(req, resp);
  65. } else if (method.equals(METHOD_DELETE)) {
  66. doDelete(req, resp);
  67. } else if (method.equals(METHOD_OPTIONS)) {
  68. doOptions(req,resp);
  69. } else if (method.equals(METHOD_TRACE)) {
  70. doTrace(req,resp);
  71. } else {
  72. //
  73. // Note that this means NO servlet supports whatever
  74. // method was requested, anywhere on this server.
  75. //
  76. String errMsg = lStrings.getString("http.method_not_implemented");
  77. Object[] errArgs = new Object[1];
  78. errArgs[0] = method;
  79. errMsg = MessageFormat.format(errMsg, errArgs);
  80. resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
  81. }
  82. }
  83. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  84. throws ServletException, IOException{
  85. // 报405错误
  86. String msg = lStrings.getString("http.method_get_not_supported");
  87. sendMethodNotAllowed(req, resp, msg);
  88. }
  89. protected void doPost(HttpServletRequest req, HttpServletResponse resp)
  90. throws ServletException, IOException {
  91. // 报405错误
  92. String msg = lStrings.getString("http.method_post_not_supported");
  93. sendMethodNotAllowed(req, resp, msg);
  94. }

通过以上源代码阅读以及运行测试可以知道:只要没有重写对应 HttpServlet类中的doGet方法或doPost方法,就会报 405 错误,因为没有重写了doGet 或 doPost 方法就会执行其中的 HttpServlet 当中编写的 doGet / doPost 方法,而如果执行了的是 HttpServlet 当中 doGet / doPost 方法就会报 405 错误,提示你没有重写 对应的 doGet / doPost 方法。

怎么避免405的错误呢?

后端重写了doGet方法,前端一定要发get请求。后端重写了doPost方法,前端一定要发post请求。这样可以避免405错误。这种前端到底需要发什么样的请求,其实应该后端说了算。后端让发什么方式,前端就得发什么方式。

补充

有的人,你会看到为了避免405错误,在Servlet类当中,将doGet和doPost方法都进行了重写。
这样,确实可以避免405的发生,但是不建议,405错误还是有用的。该报错的时候就应该让他报错。
如果你要是同时重写了doGet和doPost,那还不如你直接重写service方法好了。这样代码还能少写一点。

我们编写的HelloServlet直接继承HttpServlet,直接重写HttpServlet类中的service()方法行吗?

  • 可以,只不过你享受不到405错误。享受不到HTTP协议专属的东西。

一个Servlet类的开发步骤:

  • 第一步:编写一个Servlet类,直接继承HttpServlet
  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. import java.io.PrintWriter;
  8. public class HttpServletTest extends HttpServlet {
  9. }
  • 第二步:重写doGet方法或者重写doPost方法,到底重写谁,javaweb程序员说了算。注意:前后端请求处理是要保持一致的。
  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. import java.io.PrintWriter;
  8. public class HttpServletTest extends HttpServlet {
  9. @Override
  10. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  11. IOException {
  12. // 设置浏览器当中显示的格式
  13. response.setContentType("text/html;charSet=utf-8");
  14. PrintWriter writer = response.getWriter();
  15. writer.println("<h1>Hello World<h1>");
  16. }
  17. }
  • 第三步:将Servlet类配置到web.xml文件当中。
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5. version="4.0">
  6. <servlet>
  7. <servlet-name>HttpServletTest</servlet-name>
  8. <servlet-class>com.RainbowSea.servlet.HttpServletTest</servlet-class>
  9. </servlet>
  10. <servlet-mapping>
  11. <servlet-name>HttpServletTest</servlet-name>
  12. <url-pattern>/Test</url-pattern>
  13. </servlet-mapping>
  14. </web-app>
  • 第四步:准备前端的页面(form表单),form表单中指定请求路径即可
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>HTTP请求登录</title>
  6. </head>
  7. <body>
  8. <h2>get 请求</h2>
  9. <!--action = /项目名 + web.xml当中url的映射的路径-->
  10. <form action="/servlet08//Test" method="get">
  11. username: <input type="text" name="username"/> <br>
  12. userpassword: <input type="password" name="userpswd"/> <br>
  13. <input type="submit" value="get"/>
  14. </form>
  15. </body>
  16. </html>

4. HttpServletRequest接口详解

在这里插入图片描述

在这里插入图片描述

  • HttpServletRequest是一个接口,全限定名称:jakarta.servlet.http.HttpServletRequest 这个是基于 Tomcat 10 下的对应的包路径,如果是 Tomcat 9 (包括9)就是将其中的 jakarta 修改为 javax 就可以了。

  • HttpServletRequest接口是Servlet规范中的一员。

  • HttpServletRequest接口的父接口:ServletRequest

      1. public interface HttpServletRequest extends ServletRequest {}
  • HttpServletRequest接口的实现类谁写的? HttpServletRequest对象是谁给创建的?

    • 通过测试:就是通过 request.getClass() 方法获取到该接口的实现类, org.apache.catalina.connector.RequestFacade 实现了 HttpServletRequest接口
    1. public class RequestFacade implements HttpServletRequest {}

在这里插入图片描述

在这里插入图片描述

测试结果说明:Tomcat服务器(WEB服务器、WEB容器)实现了HttpServletRequest接口,还是说明了Tomcat服务器实现了Servlet规范。而对于我们javaweb程序员来说,实际上不需要关心这个,我们只需要面向接口编程即可。我们关心的是HttpServletRequest接口中有哪些方法,这些方法可以完成什么功能!!!!

4.0.1 HttpServletRequest对象中都有什么信息?都包装了什么信息 ?

  • HttpServletRequest 对象是 Tomcat 服务i其负责创建的,这个对象中封装了如下信息,以及如下 HTTP的请求协议?
  • 实际上是用户发送请求的时候,遵循了HTTP协议,发送的是 遵循了HTTP的请求协议的信息内容(name=value&name=value&...),Tomcat 服务器会将客户端遵循HTTP协议发送的信息内容获取到,并将其数据全部解析出来,然后Tomcat服务器把这些信息封装到 HttpServletRequest 对象当中,传给了我们 Javaweb 程序员。
  • 所以 Javaweb 程序员面向 HttpServletRequest 接口编程,调用其中的方法就可以获取到用户发送的请求信息了。

4.1 获取前端用户提交的数据信息

request和response对象的生命周期?

  • request对象和response对象,一个是请求对象,一个是响应对象。这两个对象只在当前请求中有效。
  • 一次请求对应一个request。
  • 两次请求则对应两个request。
  • .....

我们后端如何怎么获取前端浏览器用户提交的数据 ?

想要获取到前端浏览器用户提交的数据,使用interface ServletRequest 接口当中抽象方法,也就是HttpServletRequest 实现了 ServletRequest 接口的具体实现类中重写其中的抽象方法。

  1. Map<String,String[]> getParameterMap() 这个是获取用户提交的数据,并存储到Map集合当中<前端的name,前端的value>
  2. Enumeration<String> getParameterNames() 这个是获取Map集合中所有的key就是前端中所有的name
  3. String[] getParameterValues(String name) 根据(前端的name值)key获取Map集合的(前端的value值)value
  4. String getParameter(String name) 获取value这个一维数组当中的第一个元素。这个方法最常用。
  5. // 以上的4个方法,和获取用户提交的数据有关系
  6. // 注意: 这里为什么 value 的值是用String[] 数组存储的。后面有说明。

思考:如果是你,前端的form表单提交了数据之后,你准备怎么存储这些数据,你准备采用什么样的数据结构去存储这些数据呢?

前端提交的数据格式:username=abc&userpwd=111&aihao=learn&aihao=programme&aihao=playbasketball

注意: 前端表单提交数据的时候,假设提交了120这样的“数字”,其实是以字符串"120"的方式提交的,所以服务器端获取到的一定是一个字符串的"120",而不是一个数字。(前端永远提交的是字符串,后端获取的也永远是字符串。)所以都是用 String字符串。

我会采用Map集合来存储:

  1. Map<String,String>
  2. key存储String
  3. value存储String
  4. 这种想法对吗?不对。
  5. 如果采用以上的数据结构存储会发现key重复的时候value覆盖。比如这里的 name=aihao 是多选框内容,有多个值aihao=s&aihao=d&aihao=tt
  6. key value
  7. ---------------------
  8. username abc
  9. userpwd 111
  10. aihao learn
  11. aihao programme
  12. aihao playbasketball
  13. 这样是不行的,因为mapkey不能重复。
  14. Map<String, String[]>
  15. key存储String
  16. value存储String[] // 将 value 值用 String 数组存储起来。这样就避免了上述的key 重复后 value内容上的覆盖,而没有都存储起来的缺点。
  17. key value
  18. -------------------------------
  19. username {"abc"}
  20. userpwd {"111"}
  21. aihao {"learn","programme","playbasketball"}

举例使用上述四个方法获取前端用户提交的数据

第一步:编写设计好 html 前端显示页面,如下:注意,这里我们前端使用的是 post 请求,后端的Servlet 就要用 doPost 请求处理,前后端保持一致,不然报 405错误。

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>user register</title>
  6. </head>
  7. <body>
  8. <h1>user register</h1>
  9. <!--注意:/项目名+/Servlet类的映射路径-->
  10. <form action="/servlet08//Request" method="post">
  11. username:<input type="text" name="username"/><br>
  12. password:<input type="password" name="userpassword"/> <br>
  13. interest:
  14. 学习:<input type="checkbox" name="aihao" value="learn"/>
  15. 编程: <input type="checkbox" name="aihao" value="programme"/>
  16. 打篮球: <input type="checkbox" name="aihao" value="playbasketball"/>
  17. <br>
  18. <input type="submit" value="register"/>
  19. </form>
  20. </body>
  21. </html>

第二步: 编写对应处理前端用户提交的请求的 Servlet ,注意前后端请求处理保持一致 : 这里举例使用的是上述四个获取用户请求的其中的一个方法:

  1. Map<String,String[]> getParameterMap() 这个是获取用户提交的数据,并存储到Map集合当中<前端的name,前端的value>
  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. import java.util.Iterator;
  8. import java.util.Map;
  9. import java.util.Set;
  10. public class RequestServletTest extends HttpServlet {
  11. @Override
  12. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  13. IOException {
  14. // 获取到前端用户提交的数据信息(name,value),并存储到Map集合当中去。
  15. Map<String, String[]> parameterMap = request.getParameterMap();
  16. // 遍历存储了前端用户提交数据的Map集合
  17. // 获取到 Map 当中所有的 key 值(也就是前端的用户提交的所有的name 值)
  18. Set<String> strings = parameterMap.keySet();
  19. // 获取到 set 的迭代器
  20. Iterator<String> iterator = strings.iterator();
  21. while(iterator.hasNext()) {
  22. String name = iterator.next();
  23. System.out.print(name +"=");
  24. // 根据 key 获取到对应的 value值 (也就是前端用户提交的value值数据)
  25. String[] value = parameterMap.get(name);
  26. // 遍历 value 数组
  27. for(String s : value) {
  28. System.out.print(s);
  29. }
  30. System.out.println();
  31. }
  32. }
  33. }

第三步: 对相关的Servlet 类配置到对应 webapp 项目的 web.xml 文件中去。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5. version="4.0">
  6. <servlet>
  7. <servlet-name>RequestServletTest</servlet-name>
  8. <servlet-class>com.RainbowSea.servlet.RequestServletTest</servlet-class>
  9. </servlet>
  10. <servlet-mapping>
  11. <servlet-name>RequestServletTest</servlet-name>
  12. <url-pattern>/Request</url-pattern>
  13. </servlet-mapping>
  14. </web-app>

第四步: 运行测试。

在这里插入图片描述

在这里插入图片描述



再举例: 这里的 Serlvelt 获取前端用户提交的数据使用: 如下两个方法

  1. Enumeration<String> getParameterNames() 这个是获取Map集合中所有的key就是前端中所有的name
  2. String[] getParameterValues(String name) 根据(前端的name值)key获取Map集合的(前端的value值)value
  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. import java.util.Enumeration;
  8. public class RequestServletTest extends HttpServlet {
  9. @Override
  10. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  11. IOException {
  12. // 获取到前端用户提交的数据的所有 name 值,并存储到 Enumeration<String[]> 枚举泛型中
  13. Enumeration<String> names = request.getParameterNames();
  14. while (names.hasMoreElements()) { // 判断是否还有数据,有返回 true,没有返回 false
  15. String name = names.nextElement(); // 获取到其中上述 Enumeration<String> 存储到的元素数据,
  16. System.out.print(name + "=");
  17. // 同时向下移动下标
  18. String[] values = request.getParameterValues(name);// 根据 name 值获取到对应的 value值,前端用户提交的。
  19. // 并存储到String[] 字符串数组当中去.
  20. // 遍历数组
  21. for (String v : values) {
  22. System.out.print(v);
  23. }
  24. System.out.println();
  25. }
  26. }
  27. }

在这里插入图片描述

再举例: 这里的 Serlvelt 获取前端用户提交的数据使用: 如下两个方法

  1. String getParameter(String name) 获取value这个一维数组当中的第一个元素。这个方法最常用。
  2. String[] getParameterValues(String name) 根据(前端的name值)key获取Map集合的(前端的value值)value

注意: 上述两个方法的,合理使用,当我们的 name 的对应多个 value 值的话,需要使用 getParameterValues() 获取到其中的多个 value 值,而如果这是时候,我们使用的是 getParameter() 方法的话,就仅仅只会获取到其中的第一个value值,无法获取到对应name 后面的 value值的。

  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. public class RequestServletTest extends HttpServlet {
  8. @Override
  9. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  10. IOException {
  11. // 注意:这里直接指明对应的name 值,我们最好去前端编写的html当直接复制其中的name 值
  12. // 不然你可以会手动编写错误,哪怕只有其中的一个字母错误了,该方法都是无法获取到对应
  13. // 前端提交的value数据的。所以为了避免错误,建议直接复制。
  14. String username = request.getParameter("username"); // 因为该name的value值只有一个
  15. System.out.println("username =" + username );
  16. String userpassword = request.getParameter("userpassword"); // 同样的该name值的value也是只有一个
  17. System.out.println("userpassword = " + userpassword);
  18. // 注意:该如下名为 aihaos 的value值是一个多个值的,需要使用数组存储起来。
  19. String[] aihaos = request.getParameterValues("aihao");
  20. System.out.print("aihaos =");
  21. for (String s: aihaos) {
  22. System.out.print(s);
  23. }
  24. }
  25. }

在这里插入图片描述

直接手动编写name值的注意事项

如果我们使用如下方法,其中的参数 name 是我们手动编写的话,存在一个安全隐患,就是:如果我们手动输入的name 的值,在我们对应前端当中不存在的话(因为当其中的我们手动编写错了,比如说,多/少了字母的话),就无法获取到其中对应的value值了。

  1. String getParameter(String name) 获取value这个一维数组当中的第一个元素。这个方法最常用。

举例如下:

在这里插入图片描述

4.2 请求域对象的详解

HttpServletRequest 对象实际上又称为“请求域”对象。

这里我们回顾一下应用域对象是什么 ?

ServletContext 应用域对象(Servlet 上下文对象)。

什么情况下会考虑向 ServletContext 这个应用域当中绑定数据呢 ?

  • 第一:所有用户的共享数据
  • 第二:这个共享数据量很少
  • 第三:这个共享数据很少进行修改操作,尽可能没有修改操作。
  • 在以上三个条件都满足的情况下,使用这个应用域对象,可以大大提高我们程序执行效率。
  • 实际上向应用域当中绑定数据,就相当于把数据放到了缓存(Cache) 当中,然后用户访问的时候直接从缓存中取,减少 IO 的操作(IO访问磁盘,其耗费的时间代价十分的大),大大提升系统的性能,所以缓存技术是提高系统性能的重要手段。
  • 关于 ServletContext 具体的使用大家可以移步至: 解读 Servlet 源码:GenericServlet,ServletConfig,ServletContext_ChinaRainbowSea的博客-CSDN博客

你见过哪些缓存技术呢 ?

  • 字符串常量池
  • 整数型常量池[-128~127] ,但凡是在这个范围当中 Integer 对象不再创建新对象,而是直接从这个整数型常量池中获取,大大提升系统性能。
  • 数据库连接池(提前创建好 N 个连接对象,将连接对象放到集合当中,使用连接对象的时候,直接从缓存中拿,省去了连接对象的创建过程,效率提升。)
  • 线程池(Tomcat 服务器就是支持多线程的),所谓的线程池就是提前先创建好 N 个线程对象,将线程对象存储到集合中,然后用户直接去线程池当中获取线程对象,直接拿来用。提升系统性能。)
  • 后期你还会学习更多的缓存技术,例如:redis、mongoDB.....

拉回来,这里我们的主角是 "请求域" 对象:

  • “请求域”对象要比“应用域”对象范围小很多。生命周期短很多。请求域只在一次请求内有效。

  • 一个请求对象request对应一个请求域对象。一次请求结束之后,这个请求域就销毁了。

  • 请求域对象也有这三个方法:

  1. void setAttribute(String name, Object obj); // 向请求域当中绑定数据。
  2. Object getAttribute(String name); // 从请求域当中根据name获取数据。 注意: 这里的参数是 你向请求域添加数据时,设置的对应数据的,setAttribute(String name ,Object obj)一个 name 值保持一致,尽可以使用复制的方式,防止手动编写错误。导致无法找到,从而为 null 值。
  3. void removeAttribute(String name); // 将请求域当中绑定的数据移除
  4. // 以上的操作类似于Map集合的操作。
  5. Map<String, Object> map;
  6. map.put("name", obj); // 向map集合中放key和value
  7. Object obj = map.get("name"); // 通过map集合的key获取value
  8. map.remove("name"); // 通过Map集合的key删除key和value这个键值对。

请求域和应用域的选用原则 ?

尽量使用小的域对象,因为小的域对象占用的资源较少。

举例: 1.将数据存储到请求域当中,2.从请求域当中取出数据

  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. import java.io.PrintWriter;
  8. import java.util.Date;
  9. public class AServlet extends HttpServlet {
  10. @Override
  11. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  12. IOException {
  13. // 设置,在浏览器上响应的格式类型
  14. response.setContentType("text/html;charSet=utf-8");
  15. PrintWriter writer = response.getWriter();
  16. Date nowTime = new Date(); // 创建当前时间的 Date 对象
  17. // 1. 将 nowTime 的数据存储(绑定)到请求域当中
  18. request.setAttribute("sysTime",nowTime);
  19. // 2. 取出请求域当中的数据: 这里的name值与上面setAttribute(String name,Object obj) 保持一致。
  20. Object sysTime = request.getAttribute("sysTime");
  21. writer.println(sysTime); // 显示到浏览器页面当中的数据
  22. }
  23. }
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5. version="4.0">
  6. <servlet>
  7. <servlet-name>AServlet</servlet-name>
  8. <servlet-class>com.RainbowSea.servlet.AServlet</servlet-class>
  9. </servlet>
  10. <servlet-mapping>
  11. <servlet-name>AServlet</servlet-name>
  12. <url-pattern>/A</url-pattern>
  13. </servlet-mapping>
  14. </web-app>

在这里插入图片描述

关于 request对象中两个非常容易混淆的方法:

  1. // uri?username=zhangsan&userpwd=123&sex=1
  2. String username = request.getParameter("username"); // 获取到的是前端用户提交的数据
  3. // 之前一定是执行过:request.setAttribute("name", new Object()) // 将数据绑定/存储到请求域当中
  4. Object obj = request.getAttribute("name");// 获取到的是绑定到请求域当中的数据
  5. // 以上两个方法的区别是什么?
  6. // 第一个方法:获取的是用户在浏览器上提交的数据。
  7. // 第二个方法:获取的是请求域当中绑定的数据。

4.3 跳转 (两个Servlet 共享数据)

如果我们想要将两个 Servlet 的请求域当中的数据共享:比如 将 AServlet 类当中的请求域存储的数据,在 BServelt 类当中将其存储到AServlet 请求域当中的数据取出来。如下

在这里插入图片描述

观察,思考如下代码:是否可以实现 两个 Servlet 类的数据共享

  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. import java.io.PrintWriter;
  8. import java.util.Date;
  9. public class AServlet extends HttpServlet {
  10. @Override
  11. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  12. IOException {
  13. // 设置,在浏览器上响应的格式类型
  14. response.setContentType("text/html;charSet=utf-8");
  15. PrintWriter writer = response.getWriter();
  16. Date nowTime = new Date(); // 创建当前时间的 Date 对象
  17. // 将 nowTime 的数据存储(绑定)到请求域当中
  18. request.setAttribute("sysTime",nowTime);
  19. // 这样做可以吗?
  20. // 在AServlet当中new 一个BServlet对象,然后调用BServlet 对象的doGet()方法,把request 对象传过去
  21. // 因为我们数据是存储该 request 请求域当中的。所以我们将该数据传给 BServlet ,让BServlet
  22. // 将其中的请求域当中的数据取出来,这么做可以吗 ?
  23. BServlet bServlet = new BServlet();
  24. bServlet.doGet(request,response);
  25. }
  26. }
  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. import java.io.PrintWriter;
  8. public class BServlet extends HttpServlet {
  9. @Override
  10. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  11. IOException {
  12. // 设置,在浏览器上响应的格式类型
  13. response.setContentType("text/html;charSet=utf-8");
  14. PrintWriter writer = response.getWriter();
  15. // 取出请求域当中的数据: 这里的name值与上面setAttribute(String name,Object obj) 保持一致。
  16. Object sysTime = request.getAttribute("sysTime");
  17. writer.println("sysTime = " + sysTime); // 显示到浏览器页面当中的数据
  18. }
  19. }

如下:运行结果

在这里插入图片描述

注意:

注意: 上述这个代码虽然可以实现功能,但是到那时 Servlet 对象不能自己由程序员来new,因为自己new 的Servlet 的对象的生命周期不受Tomcat 服务器的管理。如果不是被 Tomcat 管理了就无法实现合理的关闭销毁对应的Servlet 的资源了。

哪要如何合理的将两个 Servlet 数据共享呢 ?*

可以,使用转发机制。

  • 将数据放到ServletContext应用域当中,当然是可以的,但是应用域范围太大,占用资源太多。不建议使用。
  • 可以将数据放到request域当中,然后AServlet转发到BServlet,保证AServlet和BServlet在同一次请求当中,这样就可以做到两个Servlet,或者多个Servlet共享同一份数据。
  • 转发(一次请求的核心方法)
  1. // 第一步:获取请求转发器对象
  2. // 注意:转发的时候,路径的写法要注意,转发的路径以“/”开始,不加项目名。/ 后接 对应转发的Servelt 在web.xml配置文件当中 uRl 映射路径即可。
  3. RequestDispatcher dispatcher = request.getRequestDispatcher("/B");
  4. // 第二步:调用转发器的forward方法完成跳转/转发
  5. dispatcher.forward(request,response);
  6. // 第一步和第二步代码可以联合在一起。
  7. request.getRequestDispatcher("/B").forward(request,response);

举例:转发机制,将AServlett 类当中的信息转发到 BServlet 当中去

  1. package com.RainbowSea.servlet;
  2. import javax.servlet.RequestDispatcher;
  3. import javax.servlet.ServletException;
  4. import javax.servlet.http.HttpServlet;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import java.io.IOException;
  8. import java.util.Date;
  9. public class AServlet extends HttpServlet {
  10. @Override
  11. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  12. IOException {
  13. // 设置,在浏览器上响应的格式类型
  14. Date nowTime = new Date(); // 创建当前时间的 Date 对象
  15. // 将 nowTime 的数据存储(绑定)到请求域当中
  16. request.setAttribute("sysTime",nowTime);
  17. // 第一步: 获取到转发对象,注意:/ 开始,不家项目名 , / + 对应跳转的 Servlet 当中的 web.xml 当中的url映射的路径
  18. RequestDispatcher requestDispatcher = request.getRequestDispatcher("/B");
  19. // 第二步: 调用转发器的forward方法完成跳转/转发
  20. requestDispatcher.forward(request,response);
  21. }
  22. }
  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. import java.io.PrintWriter;
  8. public class BServlet extends HttpServlet {
  9. @Override
  10. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  11. IOException {
  12. // 设置,在浏览器上响应的格式类型
  13. response.setContentType("text/html;charSet=utf-8");
  14. PrintWriter writer = response.getWriter();
  15. // 取出请求域当中的数据: 这里的name值与上面setAttribute(String name,Object obj) 保持一致。
  16. Object sysTime = request.getAttribute("sysTime");
  17. writer.println("sysTime = " + sysTime); // 显示到浏览器页面当中的数据
  18. }
  19. }

在这里插入图片描述

在这里插入图片描述

转发的下一个资源必须是一个Servlet吗 ?

不一定,只要是Tomcat服务器当中的合法资源,都是可以转发的。例如:html....

举例:转发一个html文件

注意: 如果对应的不是 Servlet ,默认是从项目的中的web目录开始的,如果是转发web的目录下的子目录的话,需要指定对应的子目录的文件。

在这里插入图片描述

  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. public class TestServlet extends HttpServlet {
  8. @Override
  9. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  10. IOException {
  11. // 转发的下一个资源不一定是Servlet 资源,
  12. // 只要是Tomcat服务器当中合法的资源,都是可以转发的,例如: html...
  13. // 注意:转发的时候,路径的写法要注意,转发的路径以 “/” 开始,不加项目名
  14. // 默认是从项目的中的web目录开始的,如果是转发web的目录下的子目录的话,需要指定对应的子目录
  15. // 如下是含有子目录的 / 表示 web目录
  16. request.getRequestDispatcher("/test/test.html").forward(request,response);
  17. }
  18. }

在这里插入图片描述

4.4 post 请求 request 乱码问题 ?

如下:如果我们在前端 post 请求中提交中文字符串信息,当我们在后端 Servlet 接受的时候,会存在一个乱码问题 ?但是英文不会存在这个现象。如下:所示

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>HTTP请求登录</title>
  6. </head>
  7. <body>
  8. <h2>Post 请求</h2>
  9. <!--action = /项目名 + web.xml当中url的映射的路径-->
  10. <form action="/servlet08//Test" method="post">
  11. username: <input type="text" name="username"/> <br>
  12. userpassword: <input type="password" name="userpswd"/> <br>
  13. <input type="submit" value="post"/>
  14. </form>
  15. </body>
  16. </html>
  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. public class TestServlet extends HttpServlet {
  8. @Override
  9. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  10. IOException {
  11. String username = request.getParameter("username");
  12. System.out.println("username = " + username);
  13. String userpswd = request.getParameter("userpswd");
  14. System.out.println("userpswd = " + userpswd);
  15. }
  16. }

在这里插入图片描述

如下我们:使用中文,提交数据出现,乱码。

在这里插入图片描述

解决方案:

在显示获取前端 post 请求时,执行如下代码就可以解决 post 乱码问题? 注意: 仅仅只能解决 Post 请求的乱码问题,不能解决get请求的乱码问题。

  1. // post请求在请求体中提交数据。
  2. // 设置请求体的字符集。(显然这个方法是处理POST请求的乱码问题。这种方式并不能解决get请求的乱码问题。)
  3. // Tomcat10之后,request请求体当中的字符集默认就是UTF-8,不需要设置字符集,不会出现乱码问题。
  4. // Tomcat9前(包括9在内),如果前端请求体提交的是中文,后端获取之后出现乱码,怎么解决这个乱码?执行以下代码。
  5. request.setCharacterEncoding("UTF-8");
  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. public class TestServlet extends HttpServlet {
  8. @Override
  9. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  10. IOException {
  11. // post请求在请求体中提交数据。
  12. // 设置请求体的字符集。(显然这个方法是处理POST请求的乱码问题。这种方式并不能解决get请求的乱码问题。)
  13. // Tomcat10之后,request请求体当中的字符集默认就是UTF-8,不需要设置字符集,不会出现乱码问题。
  14. // Tomcat9前(包括9在内),如果前端请求体提交的是中文,后端获取之后出现乱码,怎么解决这个乱码?执行以下代码。
  15. request.setCharacterEncoding("UTF-8");
  16. String username = request.getParameter("username");
  17. System.out.println("username = " + username);
  18. String userpswd = request.getParameter("userpswd");
  19. System.out.println("userpswd = " + userpswd);
  20. }
  21. }

在这里插入图片描述

4.5 response 响应到浏览器中的 乱码问题 ?

如果我们想在 Servlet 直接向前端页面中响应中文信息,会存在乱码问题。如下:

  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. import java.io.PrintWriter;
  8. public class TestServlet extends HttpServlet {
  9. @Override
  10. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  11. IOException {
  12. // 设置在页面当显示的格式类型
  13. response.setContentType("text/html");
  14. PrintWriter writer = response.getWriter();
  15. writer.println("<h1> 你好世界 <h1>");
  16. }
  17. }

在这里插入图片描述

在这里插入图片描述

解决方案:

  1. // 在Tomcat9之前(包括9),响应中文也是有乱码的,怎么解决这个响应的乱码?可以在响应操作之前,
  2. // 先执行如下代码,设置在页面当显示的格式,以及字符集编码
  3. response.setContentType("text/html;charset=UTF-8");
  4. // 在Tomcat10之后,包括10在内,响应中文的时候就不在出现乱码问题了。以上代码就不需要设置UTF-8了。
  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. import java.io.PrintWriter;
  8. public class TestServlet extends HttpServlet {
  9. @Override
  10. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  11. IOException {
  12. // 在Tomcat9之前(包括9),响应中文也是有乱码的,怎么解决这个响应的乱码?
  13. response.setContentType("text/html;charset=UTF-8");
  14. // 在Tomcat10之后,包括10在内,响应中文的时候就不在出现乱码问题了。以上代码就不需要设置UTF-8了。
  15. PrintWriter writer = response.getWriter();
  16. writer.println("<h1> 你好 世界 <h1>");
  17. }
  18. }

在这里插入图片描述

4.6 get 请求的 request 乱码问题 ?

如果我们使用 get 请求中,处理中文乱码问题。

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>HTTP请求登录</title>
  6. </head>
  7. <body>
  8. <h2>get 请求</h2>
  9. <!--action = /项目名 + web.xml当中url的映射的路径-->
  10. <form action="/servlet08//Test" method="get">
  11. username: <input type="text" name="username"/> <br>
  12. userpassword: <input type="password" name="userpswd"/> <br>
  13. <input type="submit" value="get"/>
  14. </form>
  15. </body>
  16. </html>
  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. public class TestServlet extends HttpServlet {
  8. @Override
  9. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  10. IOException {
  11. String username = request.getParameter("username");
  12. System.out.println("username = " + username);
  13. String userpswd = request.getParameter("userpswd");
  14. System.out.println("userpswd = " + userpswd);
  15. }
  16. }

在这里插入图片描述

在这里插入图片描述

解决方案:

首先找我们安装的 Tomcat 的安装目录,在 conf目录下,找到一个名为 server.xml 的文件名,如下

在这里插入图片描述

打开该 server.xml 文件,找到其中的如下这一段代码

在这里插入图片描述

  1. <Connector port="8080" protocol="HTTP/1.1"
  2. connectionTimeout="20000"
  3. redirectPort="8443" />

在该 server.xml 配置文件当中的上述对应的一段代码上,多添加上一个字符集设置 URIEncoding="UTF-8" 注意 />结尾就可以了。

  1. <Connector port="8080" protocol="HTTP/1.1"
  2. connectionTimeout="20000"
  3. redirectPort="8443"
  4. URIEncoding="UTF-8" />
  1. // get请求乱码问题怎么解决?
  2. // get请求发送的时候,数据是在请求行上提交的,不是在请求体当中提交的。
  3. // get请求乱码怎么解决
  4. // 方案:修改CATALINA_HOME/conf/server.xml配置文件
  5. <Connector URIEncoding="UTF-8" />
  6. // 注意:从Tomcat8之后,URIEncoding的默认值就是UTF-8,所以GET请求也没有乱码问题了。

在这里插入图片描述

4.7 HttpServletRequest接口的其他常用方法

  1. // 获取客户端的IP地址
  2. String remoteAddr = request.getRemoteAddr();。
  3. // 获取应用的根路径
  4. String contextPath = request.getContextPath();
  5. // 获取请求方式
  6. String method = request.getMethod();
  7. // 获取请求的URI
  8. String uri = request.getRequestURI();
  9. // 获取 servlet path
  10. String servletPath = request.getServletPath();

举例上述方法的使用:

在这里插入图片描述

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5. version="4.0">
  6. <servlet>
  7. <servlet-name>TestServlet</servlet-name>
  8. <servlet-class>com.RainbowSea.servlet.TestServlet</servlet-class>
  9. </servlet>
  10. <servlet-mapping>
  11. <servlet-name>TestServlet</servlet-name>
  12. <url-pattern>/Test</url-pattern>
  13. </servlet-mapping>
  14. </web-app>
  1. package com.RainbowSea.servlet;
  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;
  7. public class TestServlet extends HttpServlet {
  8. @Override
  9. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
  10. IOException {
  11. // 获取 客户端的IP地址
  12. String ip = request.getRemoteAddr();
  13. System.out.println("客户端的ip地址: " + ip);
  14. // 获取用户的请求方式
  15. String method = request.getMethod();
  16. System.out.println("用户的请求方式: " + method);
  17. // 获取webapp的根路径
  18. String contextPath = request.getContextPath();
  19. System.out.println("webapp的根路径: " + contextPath);
  20. // 获取请求的URI
  21. String requestURI = request.getRequestURI();
  22. System.out.println("请求的URI:" + requestURI);
  23. // 获取 Servlet path 路径
  24. String servletPath = request.getServletPath();
  25. System.out.println("Servlet 的路径: " + servletPath);
  26. }
  27. }

在这里插入图片描述

5. 补充:

在WEB-INF目录下新建了一个文件:welcome.html

打开浏览器访问:http://localhost:8080/servlet07/WEB-INF/welcome.html 出现了404错误。

  • 注意:放在WEB-INF目录下的资源是受保护的。在浏览器上不能够通过路径直接访问。所以像HTML、CSS、JS、image等静态资源一定要放到WEB-INF目录之外。
  • 如果非要访问的话,也有方法,详细内容请关注我,后续为您更新。

6. 总结:

  1. HTTP的请求协议包括:如下 4 个部分: 请求行,请求头,空白行,请求体

  2. HTTP的响应协议(S(浏览器) --> B(客户端))HTTP的响应协议包括4部分 : 状态行,响应头,空白行,响应体

  3. GET请求和POST请求有什么区别 ?

  4. 不管你是get请求还是post请求,发送的请求数据格式是完全相同的,只不过位置不同,格式都是统一的:都是 :name=value&name=value&name=value&name=value

  5. GET请求和POST请求如何选择,什么时候使用GET请求,什么时候使用POST请求 ?

  6. HttpServlet类是专门为 HTTP协议准备的。比 GenericServlet更加适合 HTTP协议下的开发。

  7. HttpServlet在哪个包下?这是在 Tomcat 10 的基础上:jakarta.servlet.http.HttpServlet,而后面 Tomcat 9 之前的(包括Tomcat 9 )以内的包是在 : javax.servlet.http.HttpServlet 包下的。

  8. 只要没有重写对应 HttpServlet类中的doGet方法或doPost方法,就会报 405 错误,因为没有重写了doGet 或 doPost 方法就会执行其中的 HttpServlet 当中编写的 doGet / doPost 方法,而如果执行了的是 HttpServlet 当中 doGet / doPost 方法就会报 405 错误,提示你没有重写 对应的 doGet / doPost 方法。怎么避免405的错误呢?

    后端重写了doGet方法,前端一定要发get请求。后端重写了doPost方法,前端一定要发post请求。这样可以避免405错误。这种前端到底需要发什么样的请求,其实应该后端说了算。后端让发什么方式,前端就得发什么方式。

  9. request对象和response对象,一个是请求对象,一个是响应对象。这两个对象只在当前请求中有效。一次请求对应一个request。两次请求则对应两个request。

  10. 后端如何获取前端浏览器用户提交的数据的常用方法。

  1. Map<String,String[]> getParameterMap() 这个是获取用户提交的数据,并存储到Map集合当中<前端的name,前端的value>
  2. Enumeration<String> getParameterNames() 这个是获取Map集合中所有的key就是前端中所有的name
  3. String[] getParameterValues(String name) 根据(前端的name值)key获取Map集合的(前端的value值)value
  4. String getParameter(String name) 获取value这个一维数组当中的第一个元素。这个方法最常用。
  5. // 以上的4个方法,和获取用户提交的数据有关系
  6. // 注意: 这里为什么 value 的值是用String[] 数组存储的。后面有说明。
  1. HttpServletRequest 对象又称为:"请求域" 请求域”对象要比“应用域”对象范围小很多。生命周期短很多。请求域只在一次请求内有效。一个请求对象request对应一个请求域对象。一次请求结束之后,这个请求域就销毁了。如下是“请求域”的增删改操作的方法。
  1. void setAttribute(String name, Object obj); // 向请求域当中绑定数据。
  2. Object getAttribute(String name); // 从请求域当中根据name获取数据。 注意: 这里的参数是 你向请求域添加数据时,设置的对应数据的,setAttribute(String name ,Object obj)一个 name 值保持一致,尽可以使用复制的方式,防止手动编写错误。导致无法找到,从而为 null 值。
  3. void removeAttribute(String name); // 将请求域当中绑定的数据移除
  4. // 以上的操作类似于Map集合的操作。
  5. Map<String, Object> map;
  6. map.put("name", obj); // 向map集合中放key和value
  7. Object obj = map.get("name"); // 通过map集合的key获取value
  8. map.remove("name"); // 通过Map集合的key删除key和value这个键值对。
  1. 跳转 (两个Servlet 共享数据)
  2. Post 请求以及get请求,response 响应的三者的中文乱码问题。
  3. HttpServletRequest接口的常用方法。

7. 最后:

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,后会有期,江湖再见!!!

在这里插入图片描述

原文链接:https://www.cnblogs.com/TheMagicalRainbowSea/p/17285259.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号