在前面的文章里,我们学些了部分Servlet的相关知识,相信Servelt已经成功的引起了你的注意(😝,猜测如此),今天我们来一起宏观的了解下Servlet的运行过程,然后一起来看下HttpServletRequest中提供的获取各种数据的方法。
1.Servlet的运行过程
对于用户来讲,在客户端发起一次请求(比如说查询某类商品),到页面上显示出结果(比如查询到了n件商品,页面上显示了第一页的内容),对于用户来讲就是一次点击鼠标左键,但是在服务器,要做的可远不止一次点击这么简单,下面然我们来一起看下服务器都做了哪些操作。
首先,我们来看下面这张图:
从图中,我们可以看到,
- 客户端的网络请求首先会被Http服务器接收(也叫Web服务器、web容器,其需要提供web应用运行所需的环境,接收客户端的Http请求);
- Web服务器根据请求的路径将请求转交给对应的Servlet容器(也称Servlet引擎,为Servlet的运行提供环境支持,可以理解为tomcat或其他服务器);
- Servlet容器根据对应的虚拟路径(@WebServlet中配置的)来加载Servlet,如果Serlvet没有被实例化则创建该Servlet的一个实例(调用init方法);
- Servlet容器根据用户的HTTP请求,创建一个ServletRequest对象(HTTP的请求信息被封装在其中)和一个可以对HTTP请求进行响应的ServletResponse对象(类似于寄信,并在信中说明回信的地址),然后调用HttpServlet中重写的service(ServletRequest req, ServletResponse res)方法,并在这个方法中,将ServletRequest、ServletResponse这两个对象向下转型,得到我们非常熟悉的HttpServletRequest和HttpServletResponse两个对象,然后将客户端的请求转发到HttpServlet中protected修饰的service(HttpServletRequest req, HttpServletResponse resp)(此过程在Servlet入门中有讲述过);
- service(HttpServletRequest req, HttpServletResponse resp)根据请求的method(get、post、put、delete、head、options、trace)来调用不同的方法,如doGet、doPost;
- 服务端处理完Http的请求后,根据HttpServletResponse对象将处理结果作为Http响应返回给客户端。
上面就是一个客户端发起请求与接收响应之前服务器所执行的操作,对于一个Servlet来讲,其执行过程就等同于它的生命周期:1.被Servlet容器加载------>2.接收servlet容器转发的来自客户端的Http请求------->3.处理完毕后,将处理结果返回至客户端------>4.web服务终止时被销毁。其中的2、3步骤,在web服务运行期间,可能会因为客户端的多次请求而执行多次,1、4步骤也有可能因为服务的重启或者主动销毁而多次执行。
2.Http请求中包含的信息
HttpServletRequest中包含了客户端HTTP请求的所有信息,其中主要为三部分信息:请求行、请求头、请求正文,这样说大家可能会不太明白,下面我们通过几张图片来说明一下:
上图还是我们的老朋友HelloServlet在Chrome中的运行页面(前几篇博客中有说明),我们打开可以通过右击–>检查(或F12)打开开发者工具主面板,点击NetWork,点击请求的连接(HttpServlet)可以查看客户端向服务器的Http请求信息,红框中的信息即为Http请求中的部分信息为总览;
Http请求中还包含请求头信息,其中包含许多的header;
如果Http请求中携带参数(url中携带的参数),可以在Query String Parameters中查看到(本次为了显示此部分内容,手动的在url中添加了"?username=liaizhu&password=123456");
如果Http请求方式为post,装在请求体中的数据可以在Form Data中看到,Query String Parameters和Form Data是可以共存的,即Http协议既允许我们通过url传参,也可以通过请求体传参,Get、Post方式更多的是对请求进行规范化,开发中还是尽量只使用一种方式传参。
3.HttpServletRequest中获取请求行信息的方法
Http的请求行中,会包含请求方法、请求资源名、请求路径、Http版本等信息,我们可以在tomcat的安装目录–>logs—>localhost_access_log.xxx.txt中查看在某日期中(xxx为日期,格式为yyyy-MM-dd)请求本Tomcat的所有Http请求的请求头信息,我们在上面请求HelloServlet的请求行信息如下:
其中"“括起来的为请求行信息,GET表示请求方式,”/FirstProject/HelloServlet?username=liaizhu&password=123456"为请求url,"HTTP/1.1"为请求的协议及版本。请求行后跟的200为Http响应的状态码(Status Code),293为响应内容的长度(Content-Length)。
为了获取请求行中对应的信息,HttpServletRequest中实现了一揽子的方法,让我们的操作变得更加简单快捷。相关方法如下:
为了更好的理解,我们通过一个例子来看下每个方法的获取的返回值。我们创建一个RequestTestServlet,其doGet方法如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置返回客户端的contentType
// text/plain :纯文本格式 设置为text/html println的换行会失效,可以添加<br>换行标签
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
// 获取请求行的相关信息
out.println("HttpServletRequest对象获取请求行信息方法示例:<br>");
out.println("getMethod : " + request.getMethod() + "<br>");
out.println("getRequestURI : " + request.getRequestURI() + "<br>");
out.println("getQueryString:" + request.getQueryString() + "<br>");
out.println("getProtocol : " + request.getProtocol() + "<br>");
out.println("getContextPath:" + request.getContextPath() + "<br>");
out.println("getServletPath : " + request.getServletPath() + "<br>");
out.println("getRemoteAddr : " + request.getRemoteAddr() + "<br>");
out.println("getRemoteHost : " + request.getRemoteHost() + "<br>");
out.println("getRemotePort : " + request.getRemotePort() + "<br>");
out.println("getLocalAddr : " + request.getLocalAddr() + "<br>");
out.println("getLocalName : " + request.getLocalName() + "<br>");
out.println("getLocalPort : " + request.getLocalPort() + "<br>");
out.println("getServerName : " + request.getServerName() + "<br>");
out.println("getServerPort : " + request.getServerPort() + "<br>");
out.println("getScheme : " + request.getScheme() + "<br>");
out.println("getRequestURL : " + request.getRequestURL() + "<br>");
}
浏览器上输入对应的url运行结果如下图所示:
我们可以看到,HttpServletRequest提供的方法几乎可以获取我们想要的任何请求头中的信息,还可以获得客户端的的ip地址(客户端出口的公网ip),在上例中,getRemoteAddr等四个方法获取到的值全为0:0:0:0:0:0:0:1,这是因为客户端和服务器端都在一个主机上,hosts文件解析地址的时候将localhost解析为ipv6了,我们可以将localhost改为127.0.0.1,即可获得ipv4的地址。其中的getRemotePort获取的是客户端与服务器管建立的Tcp连接所使用的端口号。
4.HttpServletRequest中获取请求头的相关方法
当客户端请求Servlet时,需要通过请求头向服务器传递附加信息,例如客户端可以接受的数据类型、请求源、消息正文的长度、是否保持TCP连接等,我们先来看下Http中常见的请求头信息:
同样的,为了方便的获取请求头中对应的信息,HttpServletRequest也提供了一系列的方法,相关方法如下:
为了测试上述方法,我们在RequestTestServlet中的doGet方法增加如下代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取请求行信息
//...
out.println("<hr/>");
out.println("HttpServletRequest对象获取请求头信息方法示例:<br>");
out.println("getHeaderNames,all headers info as follows:" + "<br>");
// 获取请求消息中所有头字段
Enumeration headerNames = request.getHeaderNames();
// 使用循环遍历所有请求头,并通过getHeader()方法获取一个指定名称的头字段
while (headerNames.hasMoreElements()) {
String headerName = (String) headerNames.nextElement();
out.print(headerName + " : " + request.getHeader(headerName) + "<br>");
}
out.println("getCookies,all cookies info as follows:" + "<br>");
Cookie []cookies = request.getCookies();
for(Cookie cookie: cookies) {
out.println(cookie.getName() + ":" + cookie.getValue() + "<br>");
}
}
并且为了测试getCookies方法,我们在chrome中手动添加如下两个cookie:
浏览器中输入对应url的执行结果如下图所示:
其中红框部分为通过getHeaderNames获取请求头name的Enumeration对象,并通过迭代的方式获取了request上传的所有的请求头信息,可以看到,cookie数据也是通过请求头来传递到服务器的。绿框部分为getCookies方法获取的Cookie对象的数组,并迭代输出数组中的所有cookie的name和value。
5.HttpServletRequest中获取请求参数
上面讲了这么多,其实都是HttpServletRequest提供的锦上添花的方法,HttpServletRequest最重要的,就是可以获取用户提交来的数据,比如表单数据或者一些查询参数。因为Servlet在MVC架构中是充当controller这个角色的,其负责响应用户的请求,也就需要和用户进行交互,负责获取从前端(JSP,客户端)获取数据(用户输入、或者查询条件等),并在处理结束后,给客户端一个响应。如果无法获取页面数据,那么后续的操作也就无从谈起。
当然,为了方便获取页面中的参数,HttpServletRequest也也也提供了一系列的方法,相关方法如下:
为了展示上述方法的使用,我们创建一个PersonalMessage.html的页面,页面代码如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<body>
<form action="RequestTestServlet" method="post">
姓名: <input type="text" name="name" style="width: 150px" />
<p />
年龄: <input type="text" name="age" style="width: 150px" />
<p />
爱好:
<input type="checkbox" name="hobby" value="sing">篮球
<input type="checkbox" name="hobby" value="dance">羽毛球
<input type="checkbox" name="hobby" value="football">足球
<p />
<input type="submit" value="提交" />
<p />
</form>
</body>
</html>
并且,为了和前两个例子区分开,我们form的提交方式设置为post,RequestTestServlet中对应的doPost方法如下:
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置request对象的解码方式
request.setCharacterEncoding("utf-8");
String name = request.getParameter("name");
String age = request.getParameter("age");
System.out.println("姓 名:" + name);
System.out.println("年 龄:" + age);
// 获取参数名为“hobby”的值
String[] hobbys = request.getParameterValues("hobby");
System.out.print("爱 好: ");
for (int i = 0; i < hobbys.length; i++) {
System.out.print(hobbys[i] + ",");
}
Map<String, String[]> paramMap = request.getParameterMap();
//...
}
其运行结果如下图所示:
6.总结
本文主要讲解了Http请求的三部分内容,分别为请求行、请求头和请求体,以及如何通过HttpServletRequest获取对应的信息,通常来讲Servlet获取客户端数据的数据是第一步,因此HttpServletRequest对象是非常重要的,对其中的方法做到熟练掌握也可让我们在开发的过程中更加的得心应手。
又到了分隔线以下,本文到此就结束了,本文内容全部都是由博主自己进行整理并结合自身的理解进行总结,如果有什么错误,还请批评指正。
Java web这一专栏会是一个系列博客,喜欢的话可以持续关注,如果本文对你有所帮助,还请还请点赞、评论加关注。
有任何疑问,可以评论区留言。
转载:https://blog.csdn.net/qq_34666857/article/details/104677407