我必须实现的逻辑是将所有请求记录在一起提交给DB的主体。
所以我决定使用afterCompletion
的{{1}}方法。
有两个参数传递给此方法HandlerInterceptor
和HttpServletRequest
。
问题是:如何从提供的对象中获取HttpServletResponse
和RequestBody
?
据我所知,我们可以使用ResponseBody
和@RequestBody
。我可以在@ResponseBody
上重复使用它们吗?
答案 0 :(得分:8)
据我所知,RequestBody
和ResponseBody
只能读取一次。所以你不应该在Interceptor
中阅读它们。
这是一些explanation。
答案 1 :(得分:4)
您可以扩展RequestBodyAdviceAdapter
并实现方法afterBodyRead
:
@ControllerAdvice
public MyRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
// write body -your input dto- to logs or database or whatever
return body;
}
}
RequestBodyAdvice在HandlerInterceptor
之前预先设置请求链。正是在http请求输入流转换为您的对象之后。
答案 2 :(得分:3)
这是一个相当古老的线索,但是为了寻找从Interceptor获取RequestBody
和ResponseBody
的方法的人。以下是我的工作方式。
RequestBody
,可以简单地使用IOUtils:
String requestBody= IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
ResponseBody
,我不得不使用HttpServletResponseCopier:
ServletLoggingFilter.HttpServletResponseCopier copier = (ServletLoggingFilter.HttpServletResponseCopier) response;
String responseBody = new String(copier.getCopy());
答案 3 :(得分:2)
这是来自HandlerInterceptor javadoc的javadoc的相关声明。
完成请求处理后回调,即渲染视图后回调。将在处理程序执行的任何结果上调用,从而允许适当的资源清理。
您无法访问请求正文(作为InputStream),因为已经读取了请求。如果需要访问请求参数,可以通过调用 - request.getParameter(“parameterName”);
使用请求对象来访问您无法访问响应正文,因为已经呈现了响应。这意味着响应已经提交给输出流。
答案 4 :(得分:2)
You may use ResponseBodyAdvice
available since Spring 4.1, with which you could intercept the response body before the body is written to the client.
Here is an example: https://sdqali.in/blog/2016/06/08/filtering-responses-in-spring-mvc/
答案 5 :(得分:1)
您可以使用以下方法从HandlerInterceptorAdapter方法中的HTTPServlet请求中获取请求正文:
request.getInputStream();
如果您希望它与扫描仪一起使用,您可以按如下方式分配扫描仪:
Scanner scanner = new Scanner(request.getInputStream(), "UTF-8");
然后,您可以使用某些操作或扫描仪技巧从扫描仪中使用所需的数据。 或者您也可以使用Apache Commons IO将其分配给字符串:
String requestStr = IOUtils.toString(request.getInputStream());
答案 6 :(得分:-1)
正如其他人所说,您不能多次读取请求输入流或响应输出流,但是可以使用过滤器将原始的请求和响应对象替换为包装的对象。 这样,您可以实现包装程序并缓冲有效负载,从而可以按需要多次使用它。
您需要的是: 创建一个过滤器:
@Component
public class RequestFilter implements Filter {
@Autowired
LogApiInterceptor logApiInterceptor;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
...
// LOG REQUEST
ResettableStreamHttpServletRequest wrappedRequest = null;
ResettableStreamHttpServletResponse wrappedResponse = null;
try {
wrappedRequest = new ResettableStreamHttpServletRequest((HttpServletRequest) request);
wrappedResponse = new ResettableStreamHttpServletResponse((HttpServletResponse) response);
logApiInterceptor.writeRequestPayloadAudit(wrappedRequest);
} catch (Exception e) {
LOG.error("Fail to wrap request and response",e);
}
try {
chain.doFilter(wrappedRequest, wrappedResponse);
} finally {
MDC.clear();
}
...
}
}
注册您的拦截器:
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new LogApiInterceptor());
}
}
创建拦截器:
public class LogApiInterceptor extends HandlerInterceptorAdapter {
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
if ( response instanceof ResettableStreamHttpServletResponse ) {
((ResettableStreamHttpServletResponse)response).payloadFilePrefix = ((ResettableStreamHttpServletRequest)request).payloadFilePrefix;
((ResettableStreamHttpServletResponse)response).payloadTarget = ((ResettableStreamHttpServletRequest)request).payloadTarget;
writeResponsePayloadAudit((ResettableStreamHttpServletResponse) response);
}
}
public String getRawHeaders(HttpServletRequest request) {
StringBuffer rawHeaders = new StringBuffer();
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = (String) headerNames.nextElement();
String value = request.getHeader(key);
rawHeaders.append(key).append(":").append(value).append("\n");
}
return rawHeaders.toString();
}
public String getRawHeaders(HttpServletResponse response){
StringBuffer rawHeaders = new StringBuffer();
Enumeration headerNames = Collections.enumeration(response.getHeaderNames());
while (headerNames.hasMoreElements()) {
String key = (String) headerNames.nextElement();
String value = response.getHeader(key);
rawHeaders.append(key).append(":").append(value).append("\n");
}
return rawHeaders.toString();
}
private void writePayloadAudit(String payloadFile, String rawHeaders, String requestBody) throws IOException {
try (Writer writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(payloadFile), StandardCharsets.UTF_8))) {
writer.write(rawHeaders);
writer.write("\n");
writer.write(requestBody);
}
}
}
RessetableInputStream:
public class ResettableServletInputStream extends ServletInputStream {
public InputStream inputStream;
private ServletInputStream servletInputStream = new ServletInputStream(){
boolean isFinished = false;
boolean isReady = true;
ReadListener readListener = null;
public int read() throws IOException {
int i = inputStream.read();
isFinished = i == -1;
isReady = !isFinished;
return i;
}
@Override
public boolean isFinished() {
return isFinished;
}
@Override
public boolean isReady() {
return isReady;
}
@Override
public void setReadListener(ReadListener readListener) {
this.readListener = readListener;
}
};
public int available() throws IOException {
return inputStream.available();
}
public void close() throws IOException {
inputStream.close();
}
public void mark(int readLimit){
inputStream.mark(readLimit);
}
public boolean markSupported(){
return inputStream.markSupported();
}
public int read(byte[] b, int off, int len) throws IOException {
return inputStream.read(b,off,len);
}
public int readline(byte[] b, int off, int len) throws IOException {
if ( len <= 0 ){
return 0;
}
int count=0,c;
while ( (c = read()) != -1 ) {
b[off++] = (byte) c;
count++;
if ( c == '\n' || count == len ) {
break;
}
}
return count > 0 ? count : -1;
}
public void reset() throws IOException {
inputStream.reset();
}
public long skip(long n) throws IOException {
return inputStream.skip(n);
}
@Override
public int read() throws IOException {
return inputStream.read();
}
@Override
public void setReadListener(ReadListener readListener){
servletInputStream.setReadListener(readListener);
}
public boolean isReady() {
return servletInputStream.isReady();
}
public boolean isFinished() {
return servletInputStream.isFinished();
}
}
ResettableOutputStream
public class ResettableServletOutputStream extends ServletOutputStream {
@Autowired
LogApiInterceptor logApiInterceptor;
public OutputStream outputStream;
private ResettableStreamHttpServletResponse wrappedResponse;
private ServletOutputStream servletOutputStream = new ServletOutputStream(){
boolean isFinished = false;
boolean isReady = true;
WriteListener writeListener = null;
@Override
public void setWriteListener(WriteListener writeListener) {
this.writeListener = writeListener;
}
public boolean isReady(){
return isReady;
}
@Override
public void write(int w) throws IOException{
outputStream.write(w);
wrappedResponse.rawData.add(new Integer(w).byteValue());
}
};
public ResettableServletOutputStream(ResettableStreamHttpServletResponse wrappedResponse) throws IOException {
this.outputStream = wrappedResponse.response.getOutputStream();
this.wrappedResponse = wrappedResponse;
}
@Override
public void close() throws IOException {
LOG.info("** RESPONSE CLOSE **");
outputStream.close();
logApiInterceptor.writeResponsePayloadAudit(wrappedResponse);
}
@Override
public void setWriteListener(WriteListener writeListener) {
servletOutputStream.setWriteListener( writeListener );
}
@Override
public boolean isReady(){
return servletOutputStream.isReady();
}
@Override
public void write(int w) throws IOException {
servletOutputStream.write(w);
}
}
RequestWrapper
public class ResettableStreamHttpServletRequest extends HttpServletRequestWrapper {
private byte[] rawData = {};
private HttpServletRequest request;
private ResettableServletInputStream servletStream;
public String requestId;
public String payloadFilePrefix;
public String payloadTarget;
ResettableStreamHttpServletRequest(HttpServletRequest request) throws IOException {
super(request);
this.request = request;
this.servletStream = new ResettableServletInputStream();
}
void resetInputStream() throws IOException {
initRawData();
servletStream.inputStream = new ByteArrayInputStream(rawData);
}
private void initRawData() throws IOException {
if ( rawData.length == 0 ) {
byte[] b = IOUtils.toByteArray(this.request.getInputStream());
if ( b != null )
rawData = b;
}
servletStream.inputStream = new ByteArrayInputStream(rawData);
}
@Override
public ServletInputStream getInputStream() throws IOException {
initRawData();
return servletStream;
}
public BufferedReader getReader() throws IOException {
initRawData();
String encoding = getCharacterEncoding();
if ( encoding != null ) {
return new BufferedReader(new InputStreamReader(servletStream, encoding));
} else {
return new BufferedReader(new InputStreamReader(servletStream));
}
}
}
ResponseWrapper
public class ResettableStreamHttpServletResponse extends HttpServletResponseWrapper {
public String requestId;
public String payloadFilePrefix;
public String payloadTarget;
public List<Byte> rawData = new ArrayList<Byte>();
public HttpServletResponse response;
private ResettableServletOutputStream servletStream;
ResettableStreamHttpServletResponse(HttpServletResponse response) throws IOException {
super(response);
this.response = response;
this.servletStream = new ResettableServletOutputStream(this);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return servletStream;
}
public PrintWriter getWriter() throws IOException {
String encoding = getCharacterEncoding();
if ( encoding != null ) {
return new PrintWriter(new OutputStreamWriter(servletStream, encoding));
} else {
return new PrintWriter(new OutputStreamWriter(servletStream));
}
}
}