请求转发
请求的转发,是服务器内部的独立行为,客户端用户是不可见的。出于复杂的业务需求或架构的需要,一个请求进入后,可能需要多个servlet配合完成。请求在一个servlet中完成处理后,就调用RequestDispatcher,将请求转发到下一个servlet,直至最后一个servlet处理完,响应给客户端。
RequestDispatcher 接口
javax.servlet 包中定义了一个 RequestDispatcher 接口,RequestDispatcher 对象由 Servlet 容器创建,用于封装由路径所标识的 Web 资源。利用 RequestDispatcher 对象可以把请求转发给其他的 Web 资源。
Servlet 可以通过 2 种方式获得 RequestDispatcher 对象:
-
调用 ServletContext 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,必须为绝对路径;
-
调用 ServletRequest 的 getRequestDispatcher(String path) 方法,参数 path 指定目标资源的路径,可以为绝对路径,也可以为相对路径。
绝对路径是指以符号“/”开头的路径,“/”表示当前 Web 应用的根目录。相对路径是指相对当前 Web 资源的路径,不以符号“/”开头。
返回值类型 | 方法 | 功能描述 |
---|---|---|
void | forward(ServletRequest request,ServletResponse response) | 将请求转发给另一个 Web 资源 |
void | include(ServletRequest request,ServletResponse response) | 将其他的资源作为当前响应内容包含进来 |
请求转发的特点
- 请求转发是应用内部行为,不支持跨域访问,只能跳转到当前应用中的资源。
- 请求转发之后,浏览器地址栏中的 URL 不会发生变化,用户无感。
- 参与请求转发的 Web 资源之间共享同一 request 对象和 response 对象。
- 由于 forward() 方法会先清空 response 缓冲区,因此只有转发到最后一个 Web 资源时,生成的响应才会被发送到客户端。
转发请求间的传值
通过request请求域的属性设置、读取方法实现。
转发示例
@WebServlet("/a")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("this is AServlet");
resp.getWriter().println("this is AServlet");
//请求转发
req.getRequestDispatcher("/b").forward(req,resp);
}
}
@WebServlet("/b")
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("this is BServlet");
resp.getWriter().println("this is BServlet");
}
}
重定向
重定向是服务端与客户端共同协商后的行为。服务端收到请求后,直接告知浏览器,我(窗口a)这边不接待了请前往窗口b,于是,客户端又跑到窗口b终于办成。客户端发出的第一个请求,响应回来的是302状态码,同时响应标头里面还有Location:XXX/b,浏览器看到302就知道要再重新发起下一个请求,请求地址就是响应标头里面的Location指定的值。
response.sendRedirect()方法
HttpServletResponse 接口中的sendRedirect 方法,可以实现重定向。
返回值类型 | 方法 | 描述 |
---|---|---|
void | sendRedirect(String location) | 返回状态码为 302 的响应结果,让浏览器访问新的 URL(响应标头参数 location 表示重定向的URL)。若指定的 URL 是相对路径,Servlet 容器会将相对路径转换为绝对路径。 |
重定向的特点
完全是通过两次请求完成,但是,重定向的过程由浏览器自动完成,重定向时请求头部的Referer会携带上值(必须通过点击浏览器中的超级链接,直接输入无值)。
重定向示例
@WebServlet("/re")
public class CRedirect extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("this is CRedirect");
resp.getWriter().println("this is CRedirect");
String s=req.getServletContext().getContextPath();
resp.sendRedirect(s+"/b");
}
}
@WebServlet("/d")
public class DServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("this is DServlet");
resp.getWriter().println("this is DServlet");
resp.getWriter().println("<a href='http://localhost:7070/ServletDemo_war_exploded/b' >测试Referer</a>");
}
}
@WebServlet("/b")
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("this is BServlet");
//这里有一个小问题要说明下。
//Referer 的正确英语拼法是referrer 。由于早期HTTP规范的拼写错误,为了保持向后兼容就将错就错了。其它网络技术的规范企图修正此问题,使用正确拼法,所以目前拼法不统一。还有它第一个字母是大写。
String referer = req.getHeader("Referer");
// 通过点击浏览器中的链接跳转到b是有值,302过来的没有值
// 点击d页面的超级链接先到a,a转发到b,referer内容为 d
//点击d 页面的超链接直接到b,refer内容为d
//浏览器中直接输入a,a转发到b,referer为null
System.out.println("Referer: "+referer);
resp.getWriter().println("this is BServlet");
}
}
请求转发与重定向的区别
区别 | 请求转发 | 重定向 |
---|---|---|
浏览器地址栏 URL 是否发生改变 | 否 | 是 |
是否支持跨域跳转 | 否 | 是 |
请求与响应的次数 | 一次请求和一次响应 | 两次请求和两次响应 |
是否共享 request 对象和 response 对象 | 是 | 否 |
传值 | 能通过 request 域对象传递数据 | 不能通过 request 域对象传递数据,可通过queryString传值 |
速度 | 相对要快 | 相对要慢 |
行为类型 | 服务器行为 | 服务端与客户端协商后行为 |