如何通过HttpServletRequestWrapper修改parameterMap?Java搜索引擎应用求助
问题分析与解决方案
首先咱们先拆解你遇到的核心问题:
- Angular路由#导致参数丢失:你外部表单的
action是http://localhost:8050/Babel/#/search,浏览器会把#后面的内容当作页面片段,不会发送给服务器,所以后端根本接收不到word参数——这也是你被迫从referer里提取参数的根源。 - Request包装时机太晚:Spring的
@RequestParam是在进入research方法前就完成参数绑定的,你在方法内部包装HttpServletRequest,已经无法改变已经注入的word、page等变量值。 - Forward操作逻辑错误:当前请求已经在处理并准备生成响应了,此时执行
forward会触发重复响应的异常,完全不符合你的需求。
下面是针对性的解决步骤:
1. 修正外部表单的提交地址
把表单的action改成不带#的后端接口地址,让参数能正常发送到服务器:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Search</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <!-- 修改action为后端接口地址,去掉# --> <form action="http://localhost:8050/Babel/search" method="GET"> <input type="text" name="word" id="word"> <input type="submit" value="Search"> </form> </body> </html>
2. 用Filter提前包装Request(核心解决参数修改问题)
既然@RequestParam绑定在方法执行前,那咱们就在请求到达Controller之前,用Filter包装Request并修改参数:
第一步:创建参数修改Filter
import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.net.URLDecoder; import java.util.Map; import java.util.TreeMap; public class ExternalRequestFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; // 从referer提取参数的逻辑(根据你的需求调整) String referer = httpRequest.getHeader("referer"); Map<String, String[]> extraParams = new TreeMap<>(); if (referer != null && referer.contains("=")) { // 注意解码,避免参数乱码 String externalWord = URLDecoder.decode(referer.split("=")[1], "UTF-8"); // 仅当原请求没有word参数时才替换 if (httpRequest.getParameter("word") == null || httpRequest.getParameter("word").isEmpty()) { extraParams.put("word", new String[]{externalWord}); extraParams.put("page", new String[]{"1"}); extraParams.put("maxrow", new String[]{"5"}); // 包装请求 httpRequest = new PrettyFacesWrappedRequest(httpRequest, extraParams); } } // 把包装后的请求传递给后续流程 chain.doFilter(httpRequest, response); } @Override public void destroy() {} }
第二步:注册Filter
在web.xml中配置Filter,确保它在Spring的DispatcherServlet之前执行:
<filter> <filter-name>ExternalRequestFilter</filter-name> <filter-class>com.yourpackage.ExternalRequestFilter</filter-class> </filter> <filter-mapping> <filter-name>ExternalRequestFilter</filter-name> <url-pattern>/search</url-pattern> <!-- 匹配你的Controller接口路径 --> </filter-mapping>
3. 调整Controller逻辑
去掉方法内部无效的Request包装和Forward操作,同时修复AJAX验证的问题(外部表单提交不是AJAX请求,原逻辑会直接抛出异常):
@RequestMapping(produces=MediaType.APPLICATION_JSON_UTF8_VALUE, method = {RequestMethod.GET}) @ResponseBody public final ResponseEntity<?> research(@RequestParam(required = true, name ="word") String word, @RequestParam(required = false, defaultValue = "1", name = "page") int page, @RequestParam(required = false, defaultValue = "5", name = "maxrow") int maxRow, @RequestParam(required = false, defaultValue = "", name = "filter") String filter, HttpServletRequest request) throws ServletException, IOException{ ResponseEntity<?> response = null; System.out.println("word : " + word); try{ // 注释掉AJAX验证,或者改为可选逻辑——外部表单提交没有X-Requested-With头 // isAjaxHttpRequest(request); // isUserAuthenticated(); SearchResponse resultat = useCase.resultUseCase(word, page, maxRow, filter, null); HttpHeaders headers = new HttpHeaders(); headers.add("Access-Control-Allow-Origin", "*"); headers.add("Access-Control-Allow-Methods", "GET, POST"); headers.add("Content-Type", "application/json"); headers.add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); response = ResponseEntity.status(HttpStatus.OK).headers(headers).body(resultat.getSearchSources()); // 如果需要跳转到Angular的#/search页面,可替换上面的响应为: // String redirectUrl = "http://localhost:8050/Babel/#/search?word=" + URLEncoder.encode(word, "UTF-8"); // response = ResponseEntity.status(HttpStatus.FOUND).header("Location", redirectUrl).build(); }catch(HttpSearchErrorException e){ LOGGER.error("{HttpSearchErrorException}",e); response = constructResponseEntity(e.getHttpStatus(), e.getMessage()); }catch(SearchErrorException e){ LOGGER.error("{SearchErrorException}",e); response = constructResponseEntity(HttpStatus.SERVICE_UNAVAILABLE.value(), e.getMessage()); } System.out.println("Response : " + response.toString()); return response; }
关键补充说明
- 如果你的Angular应用需要保留
#路由模式,处理完后端请求后可以通过重定向把参数带到前端路由中(上面代码中的注释部分)。 - 建议给
PrettyFacesWrappedRequest的参数处理加上编码解码逻辑,避免中文参数乱码。
内容的提问来源于stack exchange,提问作者B.Othman
相关产品推荐
相关产品推荐

