使用 HttpServletRequestWrapper 制作副本后缺少所需的请求正文
Posted
技术标签:
【中文标题】使用 HttpServletRequestWrapper 制作副本后缺少所需的请求正文【英文标题】:Required request body is missing after making a copy using HttpServletRequestWrapper 【发布时间】:2018-01-24 02:13:45 【问题描述】:在我的项目中,我有一组 api 调用,它们应该通过某些常用验证集进行过滤。在这种情况下,我必须在请求到达 REST 控制器之前拦截请求,读取请求正文,进行验证并将其传递给控制器(如果请求通过验证)。
由于HttpServletRequest
不能多次反序列化,我使用HttpServletRequestWrapper
来制作实际请求的副本。使用它制作的副本,我进行验证。
以下是拦截请求的配置类。
public class InterceptorConfig extends WebMvcConfigurerAdapter
@Autowired
CustomInterceptor customInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry)
registry.addInterceptor(customInterceptor).addPathPatterns("/signup/**");
这是我在 CustomInterceptor
类中的 preHandle
方法,它扩展了 HandlerInterceptorAdaptor
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
ServletRequest copiedRequest = new HttpRequestWrapper(request);
Map<String, Object> jsonMap = mapper.readValue(copiedRequest.getInputStream(), Map.class);
if(jsonMap.containsKey("userId"))
long userId = jsonMap.get("userId");
MyClass myObject= myAutowiredService.getMyObject(userId);
if(myObject == null)
response.setStatus(HttpStatus.SC_NOT_ACCEPTABLE);
return false;
// some more validations which end up returning false if they are met
return true;
这是我的HttpRequestWrapper
public class HttpRequestWrapper extends HttpServletRequestWrapper
private byte[] requestBody;
public HttpRequestWrapper(HttpServletRequest request) throws IOException
super(request);
try
requestBody = IOUtils.toByteArray(request.getInputStream());
catch (IOException ex)
requestBody = new byte[0];
@Override
public ServletInputStream getInputStream() throws IOException
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody);
return new ServletInputStream()
@Override
public boolean isFinished()
return byteArrayInputStream.available() == 0;
@Override
public boolean isReady()
return true;
@Override
public void setReadListener(ReadListener listener)
throw new RuntimeException("Not implemented");
public int read () throws IOException
return byteArrayInputStream.read();
;
现在一切就绪。现在,当我向任何具有/signup/**
模式的 url 发送请求时,所有验证都正常进行。但是,一旦请求到达控制器方法,就会弹出错误,指出请求正文不可用。
缺少必需的请求正文:公共 com.mypackage.myResponseObject com.mypackage.myController.myControllerMethod(com.mypackage.myDTO)
我正在努力寻找造成这种情况的原因以及解决问题的方法。我在 RequestWrapper 类中有什么做错了吗?还是缺少什么?
帮我解决这个问题。
谢谢!
【问题讨论】:
您不是在阅读副本,而是在阅读实际请求。如果您希望它工作,您需要在过滤器中执行此操作并传递包装的请求。目前该请求仍在消耗中。 【参考方案1】:问题似乎是您正在使用拦截器来读取HttpServletRequest
的InputStream
并将其包装在HttpRequestWrapper
中,但包装器永远不会返回。
我认为你应该使用Filter
public class CustomFilter extends OncePerRequestFilter
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException
ServletRequest copiedRequest = new HttpRequestWrapper(request);
Map<String, Object> jsonMap = mapper.readValue(copiedRequest.getInputStream(), Map.class);
if(jsonMap.containsKey("userId"))
long userId = jsonMap.get("userId");
MyClass myObject= myAutowiredService.getMyObject(userId);
if(myObject == null)
response.setStatus(HttpStatus.SC_NOT_ACCEPTABLE);
//return false;
// some more validations which end up returning false if they are met
filterChain.doFilter(copiedRequest, (ServletResponse) response);
您需要在web.xml
或WebApplicationInitializer
中使用此过滤器
【讨论】:
据我了解,我目前正在做的是制作实际请求的副本并使用该副本进行验证。最后一个返回 true 的 return 语句会将实际请求传递给控制器方法。 在您发布的答案中,wt 前进是复制的请求。不是吗?没有办法将复制的东西用于我们的验证和对控制器东西的实际请求。 据我所知,这是不可能的。因为您阅读了实际 Request 的 InputStream,而 Streams 是一种方式。 我按照你说的方式实现了。现在我面临的问题是将其配置为仅对某些请求启用。 url 过滤是现在的问题 嗨,我按照你在这里的指示做,但我的 RestController 中仍然有一个Required request body is missing:
@RequestBody以上是关于使用 HttpServletRequestWrapper 制作副本后缺少所需的请求正文的主要内容,如果未能解决你的问题,请参考以下文章
在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?
Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)