文章目录
- 1. 前言
- 2. Request 对象
- 2.1 Request 继承体系
- 2.2 Request 获取请求参数
- 1. 获取请求行数据
- 2. 获取请求头数据
- 3. 获取请求体数据
- 4. 获取请求参数的通用方式
- 3. IDEA中快速创建 Servlet
你问我青春还剩几年?我的回答是,趁现在,正当时。身边朋友都在问我怎样学好一门编程语言,怎样学好Java?怎样通过 Java 找到一份满意的工作?推荐学习此专栏:Java编程基础教程系列(零基础小白搬砖逆袭)
1. 前言
最近一直在更新 Servlet 的文章,在前面一篇中说到 sevice() 方法由 Servlet 容器调用执行,而该方法中有两个重要的参数,分别是 Request 对象和 Response 对象,下面我们就来探讨这两个参数究竟有何作用。
- 都2023年了,Servlet还有必要学习吗?一文带你快速了解Servlet
其中,Request 是请求对象,而 Response 是响应对象,例如:
@WebServlet("/demo")
public class ServletDemo implements Servlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Hello~");
}
//省略了Servlet接口中其他方法的重写
}
上面的例子中,Request 用于获取请求数据。浏览器发送 HTTP 请求到 TomCat 服务器,HTTP 请求中包含了很多请求数据,请求行,请求头等。服务器会对 HTTP 请求中的数据进行解析并把解析的结果存入一个对象中,这个对象就是 Request 对象。
服务器解析请求数据并将数据封装在 Request 对象中,此时就可以根据需求进行业务处理,业务处理完以后,服务器需要给客户端浏览器返回响应数据,即业务处理的结果。于是把数据封装在 Response 对象中。服务器会解析 Response 对象中的响应数据并通过 HTTP 协议发送给客户端浏览器展示给用户。
下面是关于 request 和 response 的一个小 demo,我们先来快速感受一下两者的作用:
@WebServlet("/demo")
public class ServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用request对象 获取请求数据
String name = request.getParameter("name");//url?name=zhangsan
//使用response对象 设置响应数据
response.setHeader("content-type","text/html;charset=utf-8");
response.getWriter().write("<h1>"+name+",欢迎您!</h1>");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Post...");
}
}
在 idea 中快速启动 TomCat 服务器,通过浏览器来访问刚才的 demo,此时浏览器就会根据传入的不同参数在页面上展示不同的数据。
示例1:
示例2:
现在我们已经知道了 Request 对象用于封装请求数据,Response 对象用于封装响应数据,但是他们具体是怎么实现的呢?我们就需要更加深入的学习。首先我们从下面三个方面来了解 Request 对象:
- Request 的继承体系
- Request 如何获取请求参数
- Request 请求转发
2. Request 对象
2.1 Request 继承体系
在 Request 继承体系中,Java 提供了请求对象的根接口,并且还提供了对 HTTP 协议封装的请求对象接口,而由 TomCat 定义了实现类,三者属于继承关系。
其中 ServletRequest 和 HttpServletRequest 是继承关系,并且都是 Java 提供的接口,我们可以在 JavaEE API 文档中查看这两个类。
之前我们定义的 Servlet 类实现 Servlet 接口或者继承 HttpServlet 类时,在 service() 方法中传入的参数是不同的,如下:
而 ServletRequest 和 HttpServletRequest 都是接口,无法用来实例化对象,那么 service() 方法中的参数对象是由谁来创建的呢?这里就需要用到 Request 继承体系中的 RequestFacade 类,该类实现了 HttpServletRequest 接口,间接实现了 ServletRequest 接口。
Servlet 类中的 service() 方法,doGet() 方法,doPost() 等方法,都是由 Web 服务器 TomCat 来调用的,所以 TomCat 提供了方法参数接口的具体实现类,并完成了对象的创建。
示例:编写一个 Servlet,在对应的方法中直接打印 request 对象。
@WebServlet("/demo")
public class ServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(request);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
重启服务器,在浏览器中访问这个资源,此时控制台就会答应相关内容。如下:
不难看出,这里的 request 是一个 RequestFacade 类对象。
2.2 Request 获取请求参数
HTTP 请求数据一共分为 3 部分,分别是请求行,请求头和请求体。学习 Request 获取请求参数可以从这三部分分别来学习。
1. 获取请求行数据
请求行一共包含 3 部分内容,分别是请求方式,请求资源路径和 HTTP 协及版本。如下:
对于这 3 部分内容,Request 对象提供了对应的 API 方法来获取,具体如下:
获取请求方式:
String getMethod();
//示例:GET
获取请求的虚拟目录(项目访问路径):
String getContextPath();
//示例:/servlet-project
获取URL(统一资源定位符):
StringBuffer getRequestURL();
//示例:http://localhost:8080/servlet-project/demo
获取 URI(统一资源标识符):
String getRequestURI();
//示例:/servlet-project/demo
获取请求参数(GET 请求):
String getQueryString();
//示例:username=zhangsan
案例:
@WebServlet("/demo")
public class ServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求方式
String method = request.getMethod();
System.out.println(method);
//获取虚拟目录(项目访问目录)
String contextPath = request.getContextPath();
System.out.println(contextPath);
//获取URL
StringBuffer requestURL = request.getRequestURL();
System.out.println(requestURL);
//获取URI
String requestURI = request.getRequestURI();
System.out.println(requestURI);
//获取GET请求方式的请求参数
String queryString = request.getQueryString();
System.out.println(queryString);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
重启服务器,在浏览器中访问 htpp://localhost:8080/servlet-project/demo?name=zhangsan
,结果如下:
2. 获取请求头数据
在请求头数据中,一般格式为 key : value
。如下:
我们可以根据请求头的名称获取其值,方法如下:
String getHeader(String name);
案例:在 Java 代码中获取浏览器版本信息。
@WebServlet("/demo")
public class ServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String agent = request.getHeader("user-agent");
System.out.println(agent);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
结果:
3. 获取请求体数据
我们在浏览器地址栏中输入访问地址后,一般发送 GET 请求给服务器,GET 请求是没有请求体的,其请求参数存放在请求行中。请求体中的数据格式如下:
Request 对象提供了两种方式来获取请求体中数据,分别是:
获取字节输入流:
//该方法用于获取字节数据,例如文件数据
ServletInputStream getInputStream();
获取字符输入流:
//该方法用于获取字符数据,例如纯文本数据
BufferedReader getReader();
我们可以添加一个 form 表单,并修改其 method 属性为 post ,用来验证 POST 请求获取请求体数据。在 webapps 文件目录下创建一个 HTML 页面 req.html ,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--
action:form表单提交的请求地址
method:请求方式,指定为post
-->
<form action="/servlet-project/demo" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit">
</form>
</body>
</html>
在 Servlet 类中的 doPost() 方法中获取请求体数据,如下:
@WebServlet("/demo")
public class ServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取字符输入流
BufferedReader br = request.getReader();
//2.获取数据
String s = br.readLine();
System.out.println(s);
}
}
在浏览器中访问:htpp://localhost:8080/servlet-project/req.html
,在浏览器页面输入数据,点击提交,此时可以在控制台看到客户端浏览器发送的请求体数据。如图:
4. 获取请求参数的通用方式
在上面的例子中,客户端浏览器发送不同的请求,服务器端会执行不同的方法,例如浏览器发送了 GET 请求,在会在服务器端执行 doGet() 方法,这样就导致了不同的方法中出现了大量的相同业务代码,这样程序的维护性和可读性都会降低。
为了解决这个问题,我们可以在 doPost() 方法中调用 doGet() 方法,并同时传入需要的参数信息。
例如:
@WebServlet("/demo")
public class ServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//采用request提供的获取请求参数的通用方式来获取请求参数
//编写其他的业务代码...
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request,response);
}
}
然而,两种不同的请求方式获取请求参数的方法是不同的,我们该如何使用一种统一的方法来获取请求参数,从而统一不同方法里面的代码呢?
解决方案一:
判断浏览器发送的请求方式,根据不同的请求方式,使用不同的方法来获取请求参数。示例:
@WebServlet("/demo")
public class ServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String method = request.getMethod();
String params=" ";
if(method.equals("GET")){
params = request.getQueryString();
} else if (method.equals("POST")) {
BufferedReader reader = request.getReader();
params = reader.readLine();
}
System.out.println(params);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request,response);
}
}
解决方案二:
使用方案一的方法时,每个 Servlet 都要写相同的代码,并且获取到的请求参数都是全部的数据作为一个字符串,实现起来非常的麻烦。
request 对象对上述获取请求参数的方法进行了封装,使得操作更加的便捷,我们只需要调用对应的方法就可以轻松的获取请求参数。
其底层是怎么实现的呢?
首先,根据不同的请求方式获取请求参数,如下:
接下来,把获取到的请求参数分割,如下:
最后,将数据存放在 Map 集合中,由于参数的值可能不止一个,所以使用字符串数组,如下:
request 为我们提供了以下的方法,让我们可以快捷的获取请求参数。
获取所有参数 Map 集合:
Map<String,String[]> getParameterMap();
根据名称获取参数值(数组):
String[] getParameterValue(String name);
根据名称获取参数值(单个值):
String getParameter(String name);
接下来,通过小 demo 练习上面获取请求参数的方法。
准备工作:在 webapps 目录下添加 html 页面,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/servlet-project/demo" method="get">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<input type="checkbox" name="hobby" value="1"> 游泳
<input type="checkbox" name="hobby" value="2"> 爬山 <br>
<input type="submit">
</form>
</body>
</html>
第二步:获取 GET 请求方式的所有请求参数
@WebServlet("/demo")
public class ServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//GET请求逻辑
System.out.println("get....");
//1. 获取所有参数的Map集合
Map<String, String[]> map = req.getParameterMap();
for (String key : map.keySet()) {
// username:zhangsan lisi
System.out.print(key+":");
//获取值
String[] values = map.get(key);
for (String value : values) {
System.out.print(value + " ");
}
System.out.println();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
第三步:获取 GET 请求方式中的 hobby 数据,结果为数组。
@WebServlet("/req2")
public class ServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//GET请求逻辑代码
//...
String[] hobbies = req.getParameterValues("hobby");
for (String hobby : hobbies) {
System.out.println(hobby);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
第四步:获取 GET 请求方式中的用于名和密码,结果为单个数据。
@WebServlet("/demo")
public class ServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//GET请求逻辑代码
//...
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username);
System.out.println(password);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
同样的,我们还可以使用此方法获取 POST 等请求方式的请求参数,只需要将 form 表单中的 method 属性修改为 POST ,并且将逻辑代码写入 doPost 代码块中,这里不过多赘述。
上面的编码方式大大的提高了编码的效率,减少了重复代码,提高了代码的可读性和维护性,所以后期在编写 Servlet 代码时,我们就可以使用下面的固定格式来编写代码。
@WebServlet("/demo")
public class ServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//采用request提供的获取请求参数的通用方式来获取请求参数
//编写其他的业务代码...
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request,response);
}
}
既然每次创建 Servlet 都可以使用上面固定的模板,那么我们有没有更加高效的创建方法呢?其实,idea 中提供了模板来快速的创建一个 Servlet。
3. IDEA中快速创建 Servlet
第一步:
按照自己的需求,修改 Servlet 创建模板的内容。如图:
第二步:
使用 Servlet 模板创建 Servlet 类,右击项目中的包 / new / Servlet 。如图:
接下来给 Servlet 类命名等,我们就成功通过模板快速的创建了一个 Servlet 类,当然我们也可以在设置中修改模板,让我们在快速创建 Servlet 时按照自己理想的模板创建。
下期见。