Servlet 的 getRequestURL() 不包含查询串;Spring MVC 返回字符串也不一定表示重定向。先分清“读取当前请求地址”和“生成下一次跳转响应”,就能避开这两类常见混淆。
获取当前 URL 时补上查询串
public static String requestUrlWithQuery(
javax.servlet.http.HttpServletRequest request) {
String url = request.getRequestURL().toString();
String query = request.getQueryString();
return query == null || query.isEmpty() ? url : url + "?" + query;
}
getQueryString() 返回原始查询串,不应把整个结果再次 URL 编码,否则 &、= 和已有转义会被改变。请求体不在这个字符串里,浏览器 fragment 也不会发给服务器。HttpServletRequest API
经过代理时,重建的协议与主机还取决于可信代理配置。不要把来自任意 Host 或转发头的结果当成可信回跳地址,也不要把带 token 等敏感参数的完整 URL 写进公开日志。
Spring MVC 的三种入口
下面使用 Spring Framework 5、javax.servlet 时代的包。控制器示例固定跳转到站内路径,不接收任意外部目标:
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class RedirectExamples {
@GetMapping("/redirect/string")
public String stringRedirect() {
return "redirect:/articles/";
}
@GetMapping("/redirect/view")
public ModelAndView viewRedirect() {
return new ModelAndView("redirect:/articles/");
}
@GetMapping("/redirect/response")
public void responseRedirect(HttpServletRequest request,
HttpServletResponse response)
throws IOException {
response.sendRedirect(request.getContextPath() + "/articles/");
}
}
String 和 ModelAndView 由 MVC 处理视图跳转;sendRedirect 则直接操作响应,方法不再另写正文或返回另一份视图。在 @RestController 或 @ResponseBody 下返回普通字符串,通常是响应正文,不是以上的视图名语义。Spring MVC 重定向
参数和方法语义也需要明确
新增参数应通过 URI 构建器或 RedirectAttributes 按参数值编码,不能把用户输入原样拼接进 Location。原始查询串与新参数编码是两种场景。
重定向是客户端重新发请求,不是服务端转发。原请求体不会因此自动复制到目标;涉及 POST 后跳转时,应明确所需状态码和方法语义。需要保留方法、请求体的场景不能仅凭“跳过去了”判断正确。