打印http接口的請求和響應(yīng)
一、簡述
基于spring提供的機(jī)制,有3種方法可以實(shí)現(xiàn)接口請求響應(yīng)日志的打印,分別是CommonsRequestLoggingFilter、HandlerInterceptor、RequestBodyAdviceAdapter、ResponseBodyAdvice。
二、修改日志級別打印請求參數(shù)
通過設(shè)置 web 的日志級別為 DEBUG,spring會自己打印請求參數(shù)。該方法打印的內(nèi)容覆蓋了后面介紹的所有方法中日志的內(nèi)容,如果不需要做定制打印,并且不介意打印的日志級別是DEBUG,那就足夠用了。
logging:
level:
root: INFO
web: DEBUG
三、使用 CommonsRequestLoggingFilter 打印請求參數(shù)
CommonsRequestLoggingFilter的使用比較簡單,只需要實(shí)現(xiàn)一個logFilter的bean即可。
只不過logFilter的日志級別是debug,需要在日志配置文件中,將CommonsRequestLoggingFilter類的日志級別設(shè)置為debug級別。
同時在生產(chǎn)環(huán)境的日志文件中打印debug日志不符合規(guī)范。
@Bean
public CommonsRequestLoggingFilter logFilter() {
CommonsRequestLoggingFilter loggingFilter = new CommonsRequestLoggingFilter();
loggingFilter.setIncludeQueryString(true);
loggingFilter.setIncludePayload(true);
loggingFilter.setMaxPayloadLength(2048);
return loggingFilter;
}
四、使用 HandlerInterceptor 打印請求參數(shù)
HandlerInterceptor 可以獲取到接口執(zhí)行過程中的 HttpServletRequest 和 HttpServletResponse 信息,因此能夠打印出接口請求響應(yīng)內(nèi)容。
@Component
public class LogInterceptorAdapter extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
ServletRequest servletRequest = new ContentCachingRequestWrapper(request);
Map<String, String[]> params = servletRequest.getParameterMap();
// 從 request 中讀取請求參數(shù)并打印
params.forEach((key, value) -> log.info("logInterceptor " + key + "=" + Arrays.toString(value)));
// 避免從 inputStream 中讀取body并打印
return true;
}
}
這種方式有個缺陷,對于 application/json 這種請求參數(shù)放在body中的方式,需要通過InputStream讀取內(nèi)容,而InputStream只能被讀取一次,
一旦在 HandlerInterceptor 中進(jìn)行了 InputStream 的讀取操作,后續(xù)的處理就讀取不到InputStream中的內(nèi)容,這是一個很嚴(yán)重的問題。
因此 HandlerInterceptor 不能用于打印請求中的body,可以改造一下該方法,只打印get請求參數(shù),post的請求參數(shù)用下面介紹的 RequestBodyAdviceAdapter 方法打印。
@Slf4j
@Component
public class LogInterceptorAdapter extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
if (DispatcherType.REQUEST.name().equals(request.getDispatcherType().name())
&& request.getMethod().equals(HttpMethod.GET.name())) {
ServletRequest servletRequest = new ContentCachingRequestWrapper(request);
Map<String, String[]> params = servletRequest.getParameterMap();
// 從 request 中讀取請求參數(shù)并打印
params.forEach((key, value) -> log.info("logInterceptor " + key + "=" + Arrays.toString(value)));
// 避免從 inputStream 中讀取body并打印
}
return true;
}
}
五、使用 RequestBodyAdviceAdapter 打印請求參數(shù)
RequestBodyAdviceAdapter 封裝了 afterBodyRead 方法,在這個方法中可以通過 Object body 參數(shù)獲取到body的內(nèi)容。
@ControllerAdvice
public class CustomRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {
@Autowired
HttpServletRequest httpServletRequest;
@Override
public boolean supports(MethodParameter methodParameter, Type type,
Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage,
MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
// 打印body內(nèi)容
return super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
}
六、使用 ResponseBodyAdvice 打印響應(yīng)內(nèi)容
ResponseBodyAdvice 和 RequestBodyAdviceAdapter 同屬于 ControllerAdvice。ResponseBodyAdvice 封裝了 beforeBodyWrite 方法,可以獲取到響應(yīng)報(bào)文。
@ControllerAdvice
public class CustomResponseBodyAdviceAdapter implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter,
Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object body,
MethodParameter methodParameter,
MediaType mediaType,
Class<? extends HttpMessageConverter<?>> aClass,
ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse) {
if (serverHttpRequest instanceof ServletServerHttpRequest &&
serverHttpResponse instanceof ServletServerHttpResponse) {
// 打印響應(yīng)body
}
return body;
}
}
七、使用 filter 打印請求和響應(yīng)
通過繼承spring的 OncePerRequestFilter實(shí)現(xiàn)自定義filter。在filter中讀取請求和響應(yīng)的body需要做一下特殊處理,因?yàn)榱髦荒鼙蛔x取一次,在filter中被讀取了,后續(xù)的處理就無法再次讀取流的內(nèi)容了。
spring提供了 ContentCachingRequestWrapper和 ContentCachingResponseWrapper兩個類來解決這個問題。文章來源:http://www.zghlxwxcb.cn/news/detail-787573.html
@Slf4j
@Component
public class AccessLogFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
ContentCachingRequestWrapper req = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper resp = new ContentCachingResponseWrapper(response);
try {
// Execution request chain
filterChain.doFilter(req, resp);
// Get body
byte[] requestBody = req.getContentAsByteArray();
byte[] responseBody = resp.getContentAsByteArray();
log.info("request body = {}", new String(requestBody, StandardCharsets.UTF_8));
log.info("response body = {}", new String(responseBody, StandardCharsets.UTF_8));
} finally {
// Finally remember to respond to the client with the cached data.
resp.copyBodyToResponse();
}
}
}
原文鏈接:https://blog.csdn.net/haiyan_qi/article/details/109960325文章來源地址http://www.zghlxwxcb.cn/news/detail-787573.html
到了這里,關(guān)于java打印http接口的請求和響應(yīng)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!