【Java.Web】Servlet —— 请求的重定向

本文深入讲解HTTP协议中的重定向机制,包括302状态码的使用、sendRedirect方法的工作原理及注意事项,通过示例演示了如何实现重定向。

请求重定向

HTTP协议规定了一种重定向机制:

  • 用户在浏览器端输入特定的URL,请求访问服务器端的某个组件
  • 服务器端的组件返回一个状态代码为302的响应结果,该响应结果的含义为:让浏览器端再请求访问另一个Web组件。在响应的结果中提供了另一个Web组件的URL。另一个Web组件有可能在同一个Web服务器上,也有可能不在同一个Web服务器上
  • 当浏览器端接受到这种响应结果后,再立即自动请求另一个Web组件
  • 浏览器端接受到来自另一个Web组件的响应结果


在Java Servlet API中,HttpServeltResponse接口的sendRedirect(String location)方法用于重定向,而在ServletResponse接口中没有这个方法,因为重定向机制是由HTTP协议规定的。

javax.servlet.http
public interface HttpServletResponse
extends ServletResponse{

    void	sendRedirect(String location)
    Sends a temporary redirect response to the client using the specified redirect location URL and clears the buffer
}

  • 调用sendRedirect()方法会清空response buffer,因此Servlet源组件生成的响应结果不会被发送到客户端;客户端最后接受的目标组件的响应结果
  • 如果源组件在进行重定向之前,已经提交了响应结果(flush(),close()),那么sendRedirect()方法将会抛出IllegalStateException;为了避免异常,不应该在源组件中提交响应结果
  • 在源组件中,sendRedirect()调用后面的代码也会被执行
  • 源组件和目标组件不共享同一个ServletRequest对象,因此不共享请求范围内的共享数据
  • 对于response.sendRedirect(String location)方法中的参数location,如果不以“/”开头,表示相对于当前源组件的路径;如果以“/”开头,表示相对于当前服务器根路径的URL,如果以“http://”开头,表示一个完整的internet上的路径;
  • 目标组件不必是同一个服务器上的同一个Web应用的组件,它可以是Internet上的任意一个有效的网页



示例:

创建一个源组件,在其中将请求转发给另一个组件:

package com.gof.test.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletContext;

public class RedirectSourceServlet extends HttpServlet {
	/**
	 * 
	 */
	private static final long serialVersionUID = -729582654304035822L;

	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException{
		PrintWriter out = resp.getWriter();
		
		// try to get the parameter form request
		String username = req.getParameter("username");
		String message = null;
		if (username == null){
			message = "Please input username.";
		}else{
			message = "Hello, " + username;
		}
		
		// try to add an attribute into the request
		req.setAttribute("msg", message);
		
		out.println("Output from RedirectSourceServlet before redirecting.");
		// print to console
		System.out.println("Output from RedirectSourceServlet before redirecting.");
		
		// redirect
		//resp.sendRedirect("/base-webapp/redirectdest?msg=" + message);
                resp.sendRedirect("redirectdest?msg=" + message);
		
		out.println("Output from RedirectSourceServlet after redirecting.");
		// print to console
		System.out.println("Output from RedirectSourceServlet after redirecting.");
	}
}

目标组件为:

package com.gof.test.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletContext;

public class RedirectDestinationServlet extends HttpServlet {
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException{
		String message = (String)req.getAttribute("msg");
		System.out.println("The attribute added by Source Sevelt is " + message);
		
		message = req.getParameter("msg");
		PrintWriter out = resp.getWriter();
		out.println(message);
		out.close();
	}
}

在web.xml中注册:

<!-- Redirect request -->
  <!-- test url = http://localhost:8080/base-webapp/redirectsrc?username=New User -->
  <servlet>
  	 <servlet-name>redirectsrc</servlet-name>
  	 <servlet-class>com.gof.test.servlet.RedirectSourceServlet</servlet-class>
   </servlet>
   <servlet-mapping>
      <servlet-name>redirectsrc</servlet-name>
      <url-pattern>/redirectsrc</url-pattern>
   </servlet-mapping>
   <servlet>
  	 <servlet-name>redirectdest</servlet-name>
  	 <servlet-class>com.gof.test.servlet.RedirectDestinationServlet</servlet-class>
   </servlet>
   <servlet-mapping>
      <servlet-name>redirectdest</servlet-name>
      <url-pattern>/redirectdest</url-pattern>
   </servlet-mapping>


在浏览器中请求如下URL:

http://localhost:8080/base-webapp/redirectsrc?username=New User


可以看到浏览器显示的结果如下:



该请求由两部分组成,第一部分的response中的返回状态码为302,并包含重定向的URL。

同时,浏览器显式的URL变为:

http://localhost:8080/base-webapp/redirectdest?msg=Hello,%20New%20User

在console中打印出的信息为:

Output from RedirectSourceServlet before redirecting.
Output from RedirectSourceServlet after redirecting.
The attribute added by Source Sevelt is null


可以看到sendRedirect()方法后面的代码仍然会被执行;但是源组件中写入response中的内容不会被发送到客户端。

源组件和目标组件不共享请求范围内的数据,源组件向请求中存放的信息,在目标组件中无法获取该信息。


修改1:

在执行sendRedirect()方法之前,在源组件之前调用flush()方法;将会抛出IllegalStateException异常,同时浏览器会显示出源组件之前输出的内容。


修改2:

将sendRedirect()方法传入一个无效的servlet url,服务器端将返回页面未找到-404的错误提示。


修改3:

将sendRedirect()方法的参数修改为其他的internet网页,如“http://baidu.com”,请求访问源组件将指向这个网页。





第四部分 package servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/abc") public class IndexServlet extends HttpServlet { @Override public void destroy() { System.out.println("5555"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("444"); } public IndexServlet(){ System.out.println("0000"); } @Override public void init(ServletConfig config) throws ServletException { System.out.println("111"); } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("22222"); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("333"); } } package servlet; import pojo.Cake; import service.impl.CakeServiceImpl; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.tools.Tool; import java.io.IOException; @WebServlet("/insertServlet") public class insertServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String name=request.getParameter("uname"); String upwd=request.getParameter("upwd"); Cake cake=new Cake(name,upwd); int i=new CakeServiceImpl().insertUser(cake); if(i==1){ response.sendRedirect("login.jsp"); }else{ response.sendRedirect("insert.jsp"); } } } package servlet; import pojo.Cake; import service.CakeService; import service.impl.CakeServiceImpl; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; @WebServlet("/abc/listServlet") public class listServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { CakeServiceImpl cakeService =new CakeServiceImpl(); List<Cake> list= cakeService.findList(); request.setAttribute("list",list); request.getRequestDispatcher("/abc/main.jsp").forward(request,response); } } package servlet; import pojo.Cake; import service.CakeService; import service.impl.CakeServiceImpl; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; @WebServlet("/UserServlet") public class UserServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String name=request.getParameter("uname"); String upwd=request.getParameter("upwd"); CakeService cakeService=new CakeServiceImpl(); Cake cake=cakeService.login(name,upwd); HttpSession session=request.getSession(); if(cake!=null){ session.setAttribute("name",cake.getProductName()); response.sendRedirect("/abc/main.jsp"); }else{ response.sendRedirect("/insert.jsp"); } } }
最新发布
11-19
代码型问题,按结构回答如下: ### 代码概述 你提供了四个 Servlet 类: - `IndexServlet`:测试生命周期方法; - `insertServlet`:处理用户插入; - `listServlet`:查询用户列表并跳转到 JSP; - `UserServlet`:处理登录逻辑。 整体结构符合 MVC 模式,但存在**路径配置错误、实体滥用、跳转路径不一致**等问题。 --- ### 代码解析 #### 1. `IndexServlet` —— 测试用途无问题 ```java @WebServlet("/abc") ``` - 映射路径为 `/abc`; - 各生命周期方法打印日志顺序正常(构造 → init → service → destroy); - 可用于验证 Servlet 是否启动。 #### 2. `insertServlet` —— 路径与重定向问题 ```java response.sendRedirect("login.jsp"); ``` - 使用相对路径跳转,易出错; - 若当前上下文为 `/项目名/insertServlet`,则 `"login.jsp"` 会相对于当前路径查找,可能找不到资源 → **404 错误根源之一**! ✅ 正确写法应加项目前缀: ```java response.sendRedirect(request.getContextPath() + "/login.jsp"); ``` #### 3. `listServlet` —— 转发路径错误 ```java request.getRequestDispatcher("/abc/main.jsp").forward(request, response); ``` - `/abc/` 是 Servlet 映射路径,不是目录! - 实际 JSP 文件应在 `webapp/main.jsp` 或 `webapp/abc/main.jsp`; - 当前写法 `/abc/main.jsp` 表示从根路径找 `abc` 文件夹下的 `main.jsp`,若不存在该目录 → **404** 📌 建议调整为: ```java // 假设 main.jsp 在 webapp 目录下 request.getRequestDispatcher("/main.jsp").forward(request, response); // 或放在 abc 子目录:webapp/abc/main.jsp request.getRequestDispatcher("/abc/main.jsp").forward(request, response); // 确保目录真实存在 ``` #### 4. `UserServlet` —— 登录后跳转路径错误 ```java response.sendRedirect("/abc/main.jsp"); ``` - 开头的 `/` 表示 **服务器根路径**(即 http://localhost:8080/),而非项目根; - 正确应包含项目名 + 资源路径; - 应改为: ```java response.sendRedirect(request.getContextPath() + "/main.jsp"); ``` #### 5. 所有 Servlet 共同问题:使用 `Cake` 接收用户数据 ```java Cake cake = new Cake(name, upwd); ``` - 构造函数参数含义模糊; - `productName` 和 `billCode` 被当作用户名和密码使用,严重语义错误; - 应替换为 `User user = new User(userCode, userName, userPassword);` --- ### 修改建议 ✅ **统一使用 `getContextPath()` 处理重定向路径** ```java String contextPath = request.getContextPath(); response.sendRedirect(contextPath + "/login.jsp"); ``` ✅ **确保 JSP 文件物理路径与请求路径匹配** | 请求路径 | 实际文件位置 | |--------|-------------| | `/main.jsp` | `src/main/webapp/main.jsp` | | `/abc/main.jsp` | `src/main/webapp/abc/main.jsp`(需手动创建 `abc` 目录) | ✅ **修改 `UserServlet` 中的 session 设置** ```java session.setAttribute("user", cake); // 更推荐保存整个对象 // 而非仅 setAttribute("name", cake.getProductName()) ``` --- ### 知识点 - **Servlet 路径解析规则**:以 `/` 开头表示服务器根路径,需拼接 `getContextPath()` 才能访问项目资源。 - **请求转发与重定向路径一致性**:必须确保目标 JSP 文件的实际路径与代码中指定路径完全匹配。 - **实体类正确传递数据**:禁止用无关实体(如 Cake)承载用户信息,防止业务逻辑混乱。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值