I want to implement Spring Interceptor in order to print every received and send API XML request. I tried this test code:
@SpringBootApplication
@EntityScan(".....")
public class Application extends SpringBootServletInitializer implements WebMvcConfigurer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
// check if restTeamplate doesn't already have other interceptors
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new RestTemplateHeaderModifierInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RestTemplateHeaderModifierInterceptor());
}
};
}
}
Component for logging:
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import io.micrometer.core.instrument.util.IOUtils;
@Component
public class RestTemplateHeaderModifierInterceptor implements ClientHttpRequestInterceptor, HandlerInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(HomeController.class);
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("[ ");
for (byte b : body) {
sb.append(String.format("0x%02X ", b));
}
sb.append("]");
LOGGER.debug("!!!!!!!!!!!!!!! Input " + sb.toString());
System.out.println("!!!!!!!!!!!!!!!");
ClientHttpResponse response = execution.execute(request, body);
InputStream inputStream = response.getBody();
String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
LOGGER.debug("!!!!!!!!!!!!!!! result " + result);
System.out.println("!!!!!!!!!!!!!!!");
return response;
}
}
But nothing is printed into the console in DEBUG mode. Any idea where I'm wrong? Probably this component is not registered or I'm missing some important configuration?
According to your code, you registered an empty list of interceptors in your RestTemplate. Try to change your code as follows:
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
// check if restTeamplate doesn't already have other interceptors
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new RestTemplateHeaderModifierInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
More info is here.
That interceptor will serve outgoing requests.
For income requests, you have to inherit your interceptor from HandlerInterceptorAdapter
:
public class MyIncomeRequestInterceptor extends HandlerInterceptorAdapter {
//...
}
and then register it with WebMvcConfigurer
in the following way, for example:
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyIncomeRequestInterceptor());
}
};
}
More info is here.
In both cases, it's not necessary to make beans from your interceptors (you can remove annotation @Component
).
UPDATE
A working example:
@Slf4j
@RestController
@ControllerAdvice
@SpringBootApplication
public class Application implements WebMvcConfigurer, ResponseBodyAdvice<Object> {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping("/hello")
public ResponseEntity<?> hello() {
return ResponseEntity.ok(Map.of("message", "hello"));
}
@EventListener
public void onReady(final ApplicationReadyEvent e) {
Map result = restTemplate().getForObject("http://localhost:8080/hello", Map.class);
if (result != null) {
log.info("[i] Request result: '{}'", result.get("message"));
}
}
@Bean
public RestTemplate restTemplate() {
ClientHttpRequestFactory factory = new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory());
RestTemplate restTemplate = new RestTemplate(factory);
var interceptors = restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) interceptors = new ArrayList<>();
interceptors.add(new OutgoingInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
@Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(new IncomingInterceptor());
}
@Override
public boolean supports(final MethodParameter returnType, final Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(final Object body, final MethodParameter returnType, final MediaType selectedContentType, final Class<? extends HttpMessageConverter<?>> selectedConverterType, final ServerHttpRequest request, final ServerHttpResponse response) {
log.info("[i] ResponseBodyAdvice: response body {}", body);
return body;
}
class OutgoingInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] bytes, final ClientHttpRequestExecution execution) throws IOException {
log.info("[i] Outgoing interceptor: requested URL is '{}'", request.getURI());
ClientHttpResponse response = execution.execute(request, bytes);
String body = StreamUtils.copyToString(response.getBody(), Charset.defaultCharset());
log.info("[i] Outgoing interceptor: response body is '{}'", body);
return response;
}
}
class IncomingInterceptor implements HandlerInterceptor {
@Override
public void postHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final ModelAndView mw) throws Exception {
log.info("[i] Incoming interceptor: requested URL is '{}'", request.getRequestURL().toString());
}
}
}
To log the response body of every metod of controllers IMO it's better to use ResponseBodyAdvice
implementation with @ControllerAdvice
annotation (see above in the code).
Result:
2019-01-16 14:05:07.260 : [i] Outgoing interceptor: requested URL is 'http://localhost:8080/hello'
2019-01-16 14:05:07.366 : [i] ResponseBodyAdvice: response body {message=hello}
2019-01-16 14:05:07.383 : [i] Incoming interceptor: requested URL is 'http://localhost:8080/hello'
2019-01-16 14:05:07.387 : [i] Outgoing interceptor: response body is '{"message":"hello"}'
2019-01-16 14:05:07.402 : [i] Request result: 'hello'
Repo: sb-web-interceptors-demo
Applying AOP Aspect Oriented Programming for logging, you can do this:
Aspect for logging:
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Enumeration;
@Aspect
@Component
public class LoggingAspect {
private static final String CONTROLLER_EXPRESION = "within(@org.springframework.stereotype.Controller *) && execution(* *.*(..))";
private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);
/**
* Before -> Any resource annotated with @Controller annotation and all method
* and function taking HttpServletRequest as first parameter.
*
* @param joinPoint
* @param request
*/
@Before(CONTROLLER_EXPRESION)
public void logBefore(JoinPoint joinPoint, HttpServletRequest request) {
log.debug("Entering in Method : {}", joinPoint.getSignature().getName());
log.debug("Class Name : {}", joinPoint.getSignature().getDeclaringTypeName());
log.debug("Arguments : {}", Arrays.toString(joinPoint.getArgs()));
log.debug("Target class : {}", joinPoint.getTarget().getClass().getName());
if (null != request) {
log.debug("Start Header Section of request ");
log.debug("Method Type : {}", request.getMethod());
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement().toString();
String headerValue = request.getHeader(headerName);
log.debug("Header Name: {} Header Value : {}", headerName, headerValue);
}
log.debug("Request Path info : {}", request.getServletPath());
log.debug("End Header Section of request ");
}
}
/**
* After -> All method within resource annotated with @Controller annotation.
*
* @param joinPoint
* @param result
*/
@AfterReturning(pointcut = CONTROLLER_EXPRESION, returning = "result")
public void logAfter(JoinPoint joinPoint, Object result) {
String returnValue = this.getValue(result);
log.debug("Method Return value : {}", returnValue);
}
/**
* After -> Any method within resource annotated with @Controller annotation and throws an exception ...Log it
* @param joinPoint
* @param exception
*/
@AfterThrowing(pointcut = CONTROLLER_EXPRESION, throwing = "exception")
public void logAfterThrowing(JoinPoint joinPoint, Throwable exception) {
log.error("An exception has been thrown in {} {}", joinPoint.getSignature().getName(), " ()");
log.error("Cause : {}", exception.getCause());
}
/**
* Around -> Any method within resource annotated with @Controller annotation.
* @param joinPoint
* @return
* @throws Throwable
*/
@Around(CONTROLLER_EXPRESION)
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
try {
String className = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
Object result = joinPoint.proceed();
long elapsedTime = System.currentTimeMillis() - start;
log.debug("Method {}.{} () execution time : {} ms", className, methodName, elapsedTime);
return result;
} catch (IllegalArgumentException e) {
log.error("Illegal argument {} in {}()", Arrays.toString(joinPoint.getArgs()), joinPoint.getSignature().getName());
throw e;
}
}
private String getValue(Object result) {
String returnValue = null;
if (null != result) {
if (result.toString().endsWith("@" + Integer.toHexString(result.hashCode()))) {
returnValue = ReflectionToStringBuilder.toString(result);
} else {
returnValue = result.toString();
}
}
return returnValue;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With