JavaWeb-HttpServletRequest请求域接口
文章目录
- 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的方式进行测试@Overrideprotected 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获取整个参数集合的keyout.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方法@Overrideprotected 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方法@Overrideprotected 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 {@Overrideprotected 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 {@Overrideprotected 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方法@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 在CServlet类中设置请求域参数, 然后在DServlet中获取这个请求域参数, 查看是否可以获取得到...request.setAttribute("user", new User("huahua", "19", "zz"));// 调用 sendRedirt 进行重定向操作(重定向要加上项目的地址), 重定向至DServletresponse.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 {@Overrideprotected 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资源内部的跳转, 具体不再演示了