实施Spring拦截器

时间:2018-11-25 15:53:16

标签: java spring spring-boot spring-data

我想实现Spring Interceptor以便打印每个接收到的并发送API XML请求。我尝试了以下测试代码:

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

}

记录组件:

import React, { Component, Fragment } from "react";
import Form from "../components/Form";

import product from "./product.json";

class App extends Component {

    constructor() {
        super();  

        this.state = {
            data: {}
        };

    }

    onSubmit = (model) => {

        console.log("Outer", model);

        this.setState({
            data: model
        });

        console.log("Form: ", this.state);
    }
    render() {

        const fields = product.fields;

        return (
            <Fragment>
                <div>Header</div>
                <Form
                    model={fields}
                    onSubmit={(model) => {this.onSubmit(model);}}
                />
                <div>Footer</div>
            </Fragment>
        );
    }
}

export default App;

但是在调试模式下,没有任何内容打印到控制台中。知道我错了吗?可能该组件未注册,或者我缺少一些重要的配置?

2 个答案:

答案 0 :(得分:3)

根据您的代码,您在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;
}

更多信息为here

该拦截器将满足去向的请求。

对于收入请求,您必须从HandlerInterceptorAdapter继承拦截器:

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

,然后通过以下方式向WebMvcConfigurer注册,例如:

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

更多信息为here

在两种情况下,都不需要从拦截器中制作bean(可以删除注释@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

答案 1 :(得分:2)

将AOP面向方面的编程应用于日志记录,您可以执行以下操作:

  1. 创建一个方面以进行拦截,然后再进入Controller。
  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;
    }
}