Реализуйте Spring Interceptor - PullRequest
0 голосов
/ 25 ноября 2018

Я хочу реализовать Spring Interceptor, чтобы печатать каждый полученный и отправлять API-запрос XML.Я попробовал этот тестовый код:

@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());
        }
    };
} 
}

Компонент для ведения журнала:

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;
    }

}

Но в режиме отладки ничего не выводится на консоль.Есть идеи, где я не прав?Возможно, этот компонент не зарегистрирован, или мне не хватает какой-то важной конфигурации?

Ответы [ 2 ]

0 голосов
/ 18 января 2019

Применяя AOP Aspect-Oriented Programming для ведения журнала, вы можете сделать это:

  1. Создать Aspect для перехвата перед вводом в контроллер.
  2. Создать выражение сокращения точки для до и после

Формат записи:

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;
    }
}
0 голосов
/ 26 ноября 2018

Согласно вашему коду, вы зарегистрировали пустой список перехватчиков в RestTemplate.Попробуйте изменить свой код следующим образом:

@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;
}

Более подробная информация здесь .

Этот перехватчик будет обслуживать исходящие запросы.

Для запросов доход вы должны унаследовать ваш перехватчик от HandlerInterceptorAdapter:

public class MyIncomeRequestInterceptor extends HandlerInterceptorAdapter {
    //...
}

, а затем зарегистрировать его с помощью WebMvcConfigurer следующим образом, например:

@Bean
public WebMvcConfigurer webMvcConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new MyIncomeRequestInterceptor());
        }
    };
}

Более подробная информация здесь .

В обоих случаях нет необходимости делать бобы из ваших перехватчиков (вы можете удалить аннотацию @Component).

ОБНОВЛЕНИЕ

Рабочий пример:

@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());
        }
    }
}

Для записи тела ответов каждого метода контроллеров IMO лучше использовать реализацию ResponseBodyAdvice с@ControllerAdvice аннотация (см. Выше в коде).

Результат:

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'

Репо: sb-web-interceptors-demo

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...