文章目录
- HttpServletRequest请求域接口
- HttpServletRequest请求域接口简介
- 关于请求域和应用域的区别
- 请求域接口中的相关方法
- 获取前端请求参数(getParameter系列方法)
- 存储请求域名参数(Attribute系列方法)
- 获取客户端的相关地址信息
- 获取项目的根路径
- 关于转发和重定向的细致剖析
- 转发代码实现及相关问题
- 重定向代码实现及相关问题
HttpServletRequest请求域接口
HttpServletRequest请求域接口简介
其实关于请求域这个词也蛮熟悉的, 因为我们之前学习过 应用域
这一概念, 应用域的生命周期很长, 伴随这服务器的启动和终止, 作用范围也很广, 对所有的处于当前 webapp
也就是 web 应用的所有Servlet对象都生效
-
HttpServletRequest
是位于jakarta.servlet.http.*
包下面的一个接口 -
继承了
ServletRequest接口
public interface HttpServletRequest extends ServletRequest
-
之前我们学习过HTTP协议的相关内容, 这个对象中封装的其实就是网络传输的时候, 发送的HTTP请求(Request)中封装的相关参数内容信息
-
实现这个接口是Tomcat服务器实现的, 传递对象封装参数也是Tomcat服务器完成好的内容, 我们作为Java程序员, 只需要学习获取其中封装的相关参数即可
关于请求域和应用域的区别
- 生命周期不同, 应用域伴随着Tomcat的生命周期 而 请求域 只作用域这一次请求之内, 而且http协议的特点就是, 一次请求一次创建一次请求域对象
- 而且在进行参数设定的时候, 尽量的去选择请求域的参数而不是应用域的参数, 因为小的域的对象占用的资源比较小
请求域接口中的相关方法
上面都说了, 请求域是封装了相关的http协议的参数信息, 所以必定提供了一些方法来让我们程序员获取到这些参数的信息…
获取前端请求参数(getParameter系列方法)
首先我们思考, 前端传递过来的参数应该采用什么数据结构来组织比较好
我们从下面的前端的页面中获取信息
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>个人信息</title>
</head>
<body>
<h2>个人信息</h2>
<form action="" method="get">
姓名:<input type="text" name="name" value=""><br>
年龄:<input type="text" name="age" value=""><br>
性别:<input type="radio" name="sex" value="男">
<input type="radio" name="sex" value="女"><br>
爱好:<input type="checkbox" name="hobby" value="吃饭">
<input type="checkbox" name="hobby" value="睡觉">
<input type="checkbox" name="hobby" value="打游戏"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
我们执行 http://127.0.0.1:8080/servlet08/test.html
我们对URL拆解如下(涉及URLEncoding)
我们可以发现, 前端向后端提交数据的格式其实并不是单纯的键值对的结构存储的, 因为如果是键值对结构存储的话, 一个key只能对应一个value, 但是复选框这种提交信息的结构, 一个key可以对应多个value信息
所以实际上, 我们的前端发来的数据存储格式是一个特殊的map集合
Map<String, String[]> map = new HashMap<>();
一个String类型的key可以对应一个String[] 数组, 也就是多个value(前端传递参数都是String类型)
上面是一个html页面, 其中包含test类型文本, 单选框, 复选框
上面的方法的解释:
- String getParameter(String name): 根据key返回String数组中的第一个参数
- String[] getParameterValues(String name): 根据key返回完整的String[]数组
- Enumeration< String > getParameterNames(): 返回一个由所有key组成的集合
- Map<String, String[]> getParameterMap(): 返回一个key和value组成的完整的集合
还拿我们上面写的那个html页面进行测试(设置一下传递的Servlet路径地址)
(注意, 我们下面的method其实写错了, 实际上是post请求)
Servlet对象的源码如下
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Enumeration;
import java.util.Map;
@WebServlet(urlPatterns = "/getparameter")
public class GetParameterServlet extends HttpServlet {
// 由于是form表单提交的数据, 我们尽量采用重写doPost的方式进行测试
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 最好还是设置一下字符集, 防止出现乱码
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// 1. 使用getParameterMap获取整个的map形式参数集合
out.print("<h3>使用getParameterMap获取整个集合</h3>");
Map<String, String[]> parameterMap = request.getParameterMap();
for(Map.Entry<String, String[]> entry : parameterMap.entrySet()){
out.print(entry.getKey() + "=");
for(String value : entry.getValue()){
out.print(value + " ");
}
}
out.print("<br>");
out.print("====================================<br>");
// 2. 使用getParameterNames获取整个参数集合的key
out.print("<h3>使用getParameterNames获取整个集合中的key</h3>");
Enumeration<String> parameterNames = request.getParameterNames();
while(parameterNames.hasMoreElements()){
String name = parameterNames.nextElement();
out.print(name + " ");
}
out.print("<br>");
out.print("====================================<br>");
// 3. 使用getParameterValues, 根据key获取参数集合的value数组
String[] hobbys = request.getParameterValues("hobby");
out.print("hobby=");
for(String hobby : hobbys){
out.print(hobby + " ");
}
out.print("<br>");
out.print("====================================<br>");
// 4. 使用getParameter, 根据key获取到value数组中的第一个值
String name = request.getParameter("name");
out.print("name=" + name);
out.print("<br>");
out.print("====================================<br>");
}
}
测试:
下面是form表单中提交的数据信息
下面是在浏览器中输出的内容
存储请求域名参数(Attribute系列方法)
Attribute
这个词其实我们很熟悉了, 因为之前学习ServletContext
就出现过这个词, 也出现了和下面一模一样的一系列方法, 当时是设置应用域对象, 但是现在是设置请求域对象
- void setAttribute(String name, Object o): 设置请求域参数
- Object getAttribute(String name): 获取请求域参数
- Enumeration< String > getAttributeNames(): 获取所有请求域的key组成的集合
- void removeAttribute(String name): 移除 key 为参数的请求域信息
没啥可说的, 直接上测试代码
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Enumeration;
// 使用注解来配置Servlet
@WebServlet("/attribute")
public class AttributeInfoServlet extends HttpServlet {
// 重写doGet方法
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
// 1. 使用 void setAttribute(String name, Object o) 设置请求域参数
request.setAttribute("name", "Jack");
request.setAttribute("age", 18);
// 2. 使用 Object getAttribute(String name) 获取请求域参数
Object name = request.getAttribute("name");
Object age = request.getAttribute("age");
out.print("<h3>" + name + " " + age + "</h3>");
out.print("<br>========================================<br>");
// 3. 使用 Enumeration< String > getAttributeNames() 获取所有的请求域参数key集合
Enumeration<String> attributeNames = request.getAttributeNames();
while (attributeNames.hasMoreElements()) {
String attributeName = attributeNames.nextElement();
out.print("<h3>" + attributeName + "</h3>");
}
out.print("<br>========================================<br>");
// 4. 使用 void removeAttribute(String name) 移除参数
request.removeAttribute("name");
Object name1 = request.getAttribute("name");
out.print("<h3>" + name1 + "</h3>");
}
}
测试结果如下
获取客户端的相关地址信息
我们需要掌握下面的三个获取地址相关信息的方法
- getRemoteAddr(): 获取客户端主机IP
- getRemotePort(): 获取客户端的应用端port(端口号)
- getRemoteHost(): 获取客户端主机名称
下面是关于上面的三个方法的测试代码(我们直接给出)
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.*;
// 使用注解代替web.xml进行Servlet的配置
@WebServlet(urlPatterns = "/addr")
public class GetAddr extends HttpServlet {
// 重写doGet方法
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置返回的类型以及获取输出流信息
response.setContentType("text/html");
PrintWriter out = response.getWriter();
// 1. 使用getRemoteAddr获取客户端的IP信息
String remoteAddr = request.getRemoteAddr();
out.print("<h3>客户端的IP地址为</h3><br>");
out.print(remoteAddr);
out.print("<br>=====================================");
// 2. 使用getRemotePort获取客户端的端口号的信息
int remotePort = request.getRemotePort();
out.print("<h3>客户端的端口号为</h3><br>");
out.print(remotePort);
out.print("<br>======================================");
// 3. 使用getRemoteHost获取客户端的主机名称
String remoteHost = request.getRemoteHost();
out.print("<h3>客户端的主机名号为</h3><br>");
out.print(remoteHost);
}
}
在浏览器上面访问这个资源, 可以得到下面的内容
注意我们的端口号其实不是固定的, 每一次请求的端口号都是在一个范围之内进行随机的, 因为我们的规范建议客户端的端口号设置为变化的, 服务器端的端口号设置为不变的…
获取项目的根路径
这个方法其实用的还是很多的, 因为我们在大量的场景中都需要动态获取根路径, 也就是项目路径, 我们在先前的内容中其实也提到过这个方法…
- getContextPath(): 获取项目部署的路径…
测试就省略了, 主要是想说这个方法的作用非常的重要, 我们好多地方获取项目的路径都需要这个方法…
关于转发和重定向的细致剖析
首先要了解, 不管是转发和重定向, 其目的都是为了实现资源的跳转
也就是Java中有两种方式实现资源的跳转
- 转发
- 重定向
转发代码实现及相关问题
getRequestDispatch(String servletName)
: 通过给定的转发的ServletName地址获取一个分发器对象forward(request, response)
: 把当前Servlet对象的请求响应对象作为参数传递到转发当中去, 从而实现位于同一个请求域的作用…
转发的代码实现
首先创建一个AServlet对象(相关注释都在代码中)
import bean.User;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.*;
// 使用注解信息简化Servlet配置
// 我们把这个 AServlet 作为资源访问的入口, 然后对 BServlet进行资源的转发(所以二者本质上还是一次请求, 共享同一个请求域)
@WebServlet(urlPatterns = "/a")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 因为要测试转发是不是在一次请求之内转发(也就是多个Servlet共享同一个request和response对象)
// 我们设置相关的请求域参数(我们把用户定义在了另一个包当中, 等会我们复制代码就不展示User类了, 应该可以看懂)
request.setAttribute("user", new User("huahua", "19", "zz"));
// 获取分发器对象, 调用分发器对象的forward方法对这次请求进行转发
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/b");
requestDispatcher.forward(request, response);
}
}
创建一个BServlet对象作为AServlet的转发请求的地址
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import bean.User;
import java.io.*;
// 使用注解简化Servlet配置
// 这个BServlet作为转发的接收方, 接收AServlet的转发
@WebServlet(urlPatterns = "/b")
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
// 获取AServlet中的应用域的参数
Object user = request.getAttribute("user");
// 强制类型转化
User us = (User) user;
// 输出其中的信息内容
out.print(us);
}
}
我们现在在浏览器中访问AServlet的资源, 显然, 跳转到了BServlet中
- 可以发现, 在A中设置的请求域参数, B中同样可以获取, 所以可以判定二者位于同一个请求域
- 通过URL可以发现, 虽然资源跳转到了BServlet, 但是URL中的地址还是显示的AServlet的地址, 所以我们可以了解到, 其实转发是一种Tomcat服务器内部进行的资源跳转, 和浏览器无关(
和重定向区分的重要依据
) - 根据上面的提示, 我们可以了解到, 转发是同一次请求的转发, 也就是只能在一种方法当中之间进行转发, 全部都在doGet内部转发, 或者全部都在doPost请求中进行转发…
转发不可以在不同的方法之间完成跳转, 测试如下
假设我们把 BServlet中的 doGet
方法转换为 doPost
方法
其他代码完全不变, 此时再次向AServlet发送请求
会发现直接报错, 报错信息是 405 method not allowed
其实针对上述问题, 我们还是有解决方案的, 只需要在doGet方法内部调用doPost就可以避免这种问题
继续访问AServlet, 会发现程序还是可以正常执行
重定向代码实现及相关问题
和转发不同, 重定向调用的API位于response对象中
通过 response
对象调用 sendReDirect(/项目路径/Servlet路径)
方法进行重定向
代码测试
CServlet如下
import bean.User;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.*;
// 使用注解简化开发
@WebServlet(urlPatterns = "/c")
public class CServlet extends HttpServlet {
// 重写doGet方法
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 在CServlet类中设置请求域参数, 然后在DServlet中获取这个请求域参数, 查看是否可以获取得到...
request.setAttribute("user", new User("huahua", "19", "zz"));
// 调用 sendRedirt 进行重定向操作(重定向要加上项目的地址), 重定向至DServlet
response.sendRedirect(request.getContextPath() + "/d");
}
}
DServlet
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.*;
// 使用注解简化开发
@WebServlet(urlPatterns = "/d")
public class DServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
// 尝试接收CServlet中设置的请求域参数, 查看是不是可以获取到(其实本质是查看是不是一次请求)
Object user = request.getAttribute("user");
out.println(user == null ? "不是一个请求" : "是一个请求");
}
}
向CServlet发送请求
获取响应结果如下
- 很明显的看到URL中的资源明显的发生了改变
我们不妨抓个包看一看刚才发生了什么
会发现出现了两次请求…
第一次向CServlet发送了请求, 这是第一次的请求响应信息
可以发现, 响应时的状态码是 302 Found
也就是发生了重定向的操作
第二次请求是直接通过浏览器向DServlet发送了一个请求而不是Tomcat资源内部的跳转, 具体不再演示了