Spring MVC集成POST API时出现400 Bad Request问题求助
问题分析与解决方案
400 Bad Request 通常源于请求格式不匹配或请求数据被过滤器篡改,结合你提到的 Apache WsFilter,重点排查以下几个方向:
1. WsFilter 提前读取请求体导致解析失败
Apache WsFilter 可能会提前读取 HTTP 请求体,而 Spring MVC 的 @RequestBody 依赖请求体的可重复读取能力。如果请求体被过滤器读取后未重置流,Spring MVC 就无法再解析表单数据,直接返回 400 错误。
解决办法:
自定义请求包装类缓存请求体,让过滤器和 Spring MVC 都能读取:
public class CachedBodyHttpServletRequestWrapper extends HttpServletRequestWrapper { private final byte[] cachedBody; public CachedBodyHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); this.cachedBody = StreamUtils.copyToByteArray(request.getInputStream()); } @Override public ServletInputStream getInputStream() throws IOException { return new CachedBodyServletInputStream(cachedBody); } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(cachedBody))); } private static class CachedBodyServletInputStream extends ServletInputStream { private final ByteArrayInputStream byteArrayInputStream; public CachedBodyServletInputStream(byte[] cachedBody) { this.byteArrayInputStream = new ByteArrayInputStream(cachedBody); } @Override public int read() throws IOException { return byteArrayInputStream.read(); } @Override public boolean isFinished() { return byteArrayInputStream.available() == 0; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener readListener) { throw new UnsupportedOperationException("Not supported"); } } }
修改 WsFilter 的 doFilter 方法,传递包装后的请求:
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; CachedBodyHttpServletRequestWrapper cachedRequest = new CachedBodyHttpServletRequestWrapper(httpRequest); chain.doFilter(cachedRequest, response); }
2. 替换 @RequestBody 为 @RequestParam 或 @ModelAttribute
既然独立 Spring Boot 项目中 @RequestParam 能正常工作,在集成项目中改用这种方式可以避免请求体解析的冲突:
单参数场景:
@RequestMapping(value = "/registration", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public void marketplaceRegistration(@RequestParam("token") String token, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException { LOGGER.info("\nIn registration method: TOKEN RECIEVED IS " + token); }
多参数场景:
先定义表单实体类:
public class RegistrationForm { private String token; // 其他参数的 getter/setter }
再用 @ModelAttribute 接收:
@RequestMapping(value = "/registration", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public void marketplaceRegistration(@ModelAttribute RegistrationForm form, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException { LOGGER.info("\nIn registration method: TOKEN RECIEVED IS " + form.getToken()); }
3. 调整 WsFilter 的执行顺序
确保 WsFilter 的执行顺序在 Spring MVC 的 DispatcherServlet 之后,避免它干扰请求解析流程。
在 web.xml 中调整配置顺序:
<!-- 先配置 Spring MVC 的 DispatcherServlet --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 再配置 WsFilter --> <filter> <filter-name>wsFilter</filter-name> <filter-class>org.apache.ws.commons.server.WsFilter</filter-class> </filter> <filter-mapping> <filter-name>wsFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
4. 检查请求头是否被篡改
WsFilter 可能会修改请求的 Content-Type 头,导致 Spring MVC 无法识别 x-www-form-urlencoded 格式。
调试方法:在 WsFilter 中添加日志打印请求头:
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; LOGGER.info("Request Content-Type: " + httpRequest.getContentType()); chain.doFilter(request, response); }
如果发现 Content-Type 被篡改,可在过滤器中手动重置:
((HttpServletRequest)request).setAttribute("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE);
内容的提问来源于stack exchange,提问作者agaonsindhe
相关产品推荐
相关产品推荐

