我需要读取我的其中一个
GenericFilterBean
中的请求负载。
因为我不能两次调用 getreader 我使用了 ContentCachingRequestWrapper
就像-
HttpServletRequest httpRequest = (HttpServletRequest) request;
ContentCachingRequestWrapper cachedRequest = new ContentCachingRequestWrapper(httpRequest);
获取请求体-
String payload = cachedRequest.getReader().lines().collect(Collectors.joining());
并链接请求-
chain.doFilter(cachedRequest, response);
但我的控制器仍然抛出-
.w.s.m.s.DefaultHandlerExceptionResolver : Resolved exception caused by handler execution: org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public org.springframework.http.ResponseEntity<?> com.siemens.plm.it.sf.api.controllers.OrderController.createCCOrder(com.siemens.plm.it.de.ms.provisioning.model.sap.SapQuoteCreationArgument,javax.servlet.http.HttpServletRequest) throws java.lang.Exception
关于如何链接请求以便我也可以读取控制器中的有效负载有什么想法吗?
谢谢你。
我遇到了同样的问题。您需要在 requestWrapper 上调用 getInputStream 才能让它被缓存。这就是你的过滤器的样子:
@Component
public class CachingRequestBodyFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper((HttpServletRequest) servletRequest);
//this line is necessary to cache InputStream
wrappedRequest.getInputStream();
//String requestBody = IOUtils.toString(wrappedRequest.getInputStream(), StandardCharsets.UTF_8); with this line you can map requestBody to String
chain.doFilter(wrappedRequest, servletResponse);
}
}
控制器:
@PostMapping(ENDPOINT)
void endpoint(HttpServletRequest request) {
ContentCachingRequestWrapper requestWrapper = (ContentCachingRequestWrapper) request;
String requestBody = new String(requestWrapper.getContentAsByteArray());
//process requestBody
}
一般来说,您可以采用 Krzysztof 的方法,即所有网站/教程中显示的方法。
但我想继续直接在 WebResource/RestController 中接收
@RequestBody
。为此,这种方法不起作用,因为 Spring 在内部不会调用 getContentAsByteArray()
。
所以,这是另一种方法:
对于
@RequestBody
,Spring 使用来自InputStream 的方法read()
。所以我们需要重写那个。为此,我们必须创建新的包装器。以 Kt 为单位的样本:
class RequestBodyContentWrapper(private val wrapped: ContentCachingRequestWrapper) : ContentCachingRequestWrapper(wrapped) {
private val bodyProcessorIS: RequestBodyInputStreamWrapper by lazy {
RequestBodyInputStreamWrapper(super.getInputStream())
}
override fun getInputStream(): ServletInputStream {
return bodyProcessorIS
}
// use the method supported by ContentCachingRequestWrapper to read the bytes
fun prepareInputStream() = bodyProcessorIS.resetReading(wrapped.contentAsByteArray)
}
class RequestBodyInputStreamWrapper(private val wrapped: ServletInputStream) : ServletInputStream() {
private lateinit var bais: ByteArrayInputStream
// convert the bytes read from the cache to InputStream
// which later can be used in the read()
fun resetReading(bytes: ByteArray) {
this.bais = ByteArrayInputStream(bytes)
}
override fun read() = bais.read()
override fun isFinished() = wrapped.isFinished
override fun isReady(): Boolean = wrapped.isReady
override fun setReadListener(readListener: ReadListener?) = wrapped.setReadListener(readListener)
}
然后,在过滤器中:
override fun doFilterInternal(originalRequest: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
val contentRequest = ContentCachingRequestWrapper(originalRequest)
val hmac = ...
// ... internal logic
val readableRequest = RequestBodyContentWrapper(contentRequest)
readableRequest.prepareInputStream()
filterChain.doFilter(readableRequest, response)
}
这是必需的,因为 Spring 内部类不会使用
getContentAsByteArray
,而是使用 inputStream.read()
。令人失望的是 Spring 的 ContentCachingRequestWrapper 无法处理该问题(稍后不允许使用 read()
)。
一开始可能不是那么简单,但对我来说很有效。花一些时间来理解它。
我更喜欢这种方式,以保持 RestController 的清洁。过滤器通常是唯一的(也就是说,这个附加逻辑位于单个位置);与我们经常有的 RestController 相反。