Dos formas de solicitar parámetros y resultados de respuesta de la interfaz de registro de springboot: interceptor y aspecto (código específico)

Dos formas de solicitar parámetros y resultados de respuesta de la interfaz de registro de springboot: interceptor y aspecto (código específico)

Prefacio: si hay un problema en producción, queremos verificar el registro, los parámetros de solicitud de la interfaz de llamada del usuario y el resultado de retorno de la respuesta en un cierto período de tiempo, y usar el registro para adivinar lo que hizo el usuario en ese momento. El registro de los parámetros de solicitud y los resultados de respuesta de la interfaz es útil para solucionar los problemas de producción, pero también traerá problemas de rendimiento de la memoria al sistema. Así que tenemos que sopesar los pros y los contras para elegir El siguiente es el código específico para los dos métodos de registro.

1. Usar facetas (recomendado usar esto, simple)
@Component
@Aspect
@Slf4j
public class ApiLogAspect {
    
    

    @Pointcut("execution(* com.xl.finance.module..controller..*.*(..))")
    public void controller() {
    
    
    }

    @Around("controller()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
    
    
        long startTime = System.currentTimeMillis();
        Long userId = WebUtils.getId();
        String userName = WebUtils.getName();
        String requestUrl = WebUtils.getRequestUrl();
        requestUrl = new URL(requestUrl).getPath();
        String requestParam = getMethodArgs(point);
        Object result = point.proceed();
        long endTime = System.currentTimeMillis();
        String responseParam = handlerResult(result);
        log.info("requestUrl:{} userId:{},userName:{} requestParam:{},responseParam:{},rtt:{}ms", requestUrl, userId, userName, requestParam, responseParam, endTime - startTime);
        return result;
    }

    private String getMethodArgs(JoinPoint point) {
    
    
        Object[] args = point.getArgs();
        if (args == null || args.length == 0) {
    
    
            return "";
        }
        try {
    
    
            Map<String, Object> params = new HashMap<>();
            String[] parameterNames = ((MethodSignature) point.getSignature()).getParameterNames();
            for (int i = 0; i < parameterNames.length; i++) {
    
    
                Object arg = args[i];
                // 过滤不能转换成JSON的参数
                if ((arg instanceof ServletRequest) || (arg instanceof ServletResponse)) {
    
    
                    continue;
                } else if ((arg instanceof MultipartFile)) {
    
    
                    arg = arg.toString();
                }
                params.put(parameterNames[i], arg);
            }
            return JSONObject.toJSONString(params);
        } catch (Exception e) {
    
    
            log.error("接口出入参日志打印切面处理请求参数异常", e);
        }
        return Arrays.toString(args);
    }

    /**
     * 返回结果简单处理
     * 1)把返回结果转成String,方便输出。
     * 2)返回结果太长则截取(最多3072个字符),方便展示。
     *
     * @param result 原方法调用的返回结果
     * @return 处理后的
     */
    private String handlerResult(Object result) {
    
    
        if (result == null) {
    
    
            return null;
        }
        String resultStr;
        try {
    
    
            if (result instanceof String) {
    
    
                resultStr = (String) result;
            } else {
    
    
                resultStr = JSONObject.toJSONString(result);// 如果返回结果非String类型,转换成JSON格式的字符串
            }

            if (resultStr.length() > 3072) {
    
    
                resultStr = resultStr.substring(0, 3072);
            }
        } catch (Exception e) {
    
    
            resultStr = result.toString();
            log.error("接口出入参日志打印切面处理返回参数异常", e);
        }
        return resultStr;
    }
}
2. Usa interceptores

Originalmente pensó que de esta manera, siempre que el interceptor personalizado herede la clase HandlerInterceptorAdapter y reescriba preHandle() y afterCompletion(), estará bien. No esperaba encontrarme con muchos escollos.

Parámetros de la solicitud: request.getParameterMap() puede obtener los parámetros de la solicitud, pero si la interfaz usa @RequestBody, encontrará que no puede obtener el valor.

Resultado de la respuesta: esto es un poco doloroso. Revisé casi todos los métodos de respuesta, pero descubrí que no se podía obtener el resultado de la respuesta de la interfaz.

Pegue el código directamente debajo.

2.1 Implementar la clase Filter

El flujo es único, para evitar que los parámetros de solicitud del flujo se lean en el registro, lo que afecta la interfaz de la solicitud real

@Component
@WebFilter(filterName = "HttpServletRequestFilter", urlPatterns = "/")
@Order(10000)
public class HttpServletRequestFilter implements Filter {
    
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    
    
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    
        ServletRequest requestWrapper = null;
        if(servletRequest instanceof HttpServletRequest) {
    
    
            requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
        }
        //获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中
        // 在chain.doFiler方法中传递新的request对象
        if(null == requestWrapper) {
    
    
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
    
    
            filterChain.doFilter(requestWrapper, servletResponse);
        }
    }

    @Override
    public void destroy() {
    
    
        Filter.super.destroy();
    }
}
2.2 Heredar la clase RequestWrapper
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {
    
    

    private final String body;

    public RequestWrapper(HttpServletRequest request) {
    
    
        super(request);

        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        try (InputStream inputStream = request.getInputStream()) {
    
    
            if(inputStream != null){
    
    
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
    
    
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            }
        } catch (Exception e) {
    
    
            log.error("RequestWrapper read error :{}",e.getMessage());
        } finally {
    
    
            IoUtil.close(bufferedReader);
        }
        body = stringBuilder.toString();
    }

    /**
     * 将getInputStream重新,让它能重复获取到body里的内容,这样才不会影响后续的流程
     * @return
     * @throws IOException
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
    
    
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletInputStream = new ServletInputStream() {
    
    
            @Override
            public boolean isFinished() {
    
    
                return false;
            }

            @Override
            public boolean isReady() {
    
    
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
    
    
            }

            @Override
            public int read() throws IOException {
    
    
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;
    }

    /**
     * 重写获取 字符流的方式
     * @return
     */
    @Override
    public BufferedReader getReader() throws IOException {
    
    
        return new BufferedReader(new InputStreamReader(this.getInputStream(), StandardCharsets.UTF_8));
    }


    /**
     * 获取body
     * @return
     */
    public String getBody() {
    
    
        return this.body;
    }
}
2.3 Implementar la clase ResponseBodyAdvice

Después de implementar ResponseBodyAdvice, puede obtener el resultado de la respuesta de la interfaz para imprimir el registro, pero para procesar el registro de manera uniforme en el interceptor personalizado, aquí el resultado de la respuesta se almacena en un atributo de respuesta.

@ControllerAdvice
public class InterceptResponse implements ResponseBodyAdvice<Object> {
    
    

    private static final Integer MAX_LENGTH = 1000;

    @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) {
    
    
        String result ;
        if(body instanceof JsonResult){
    
    
            JsonResult jsonResult = (JsonResult) body;
            result = JSON.toJSONString(jsonResult);
        }else{
    
    
            result = JSON.toJSONString(body);
        }
        Integer length = result.length() > MAX_LENGTH ? MAX_LENGTH :  result.length();
        result = result.substring(0,length);
        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession httpSession = httpServletRequest.getSession(true);
        //放到缓存里,以便于可以在HandlerInterceptor拦截里取出并打印出返回结果
        httpSession.setAttribute("body", result);
        return body;
    }
}
2.4 El interceptor recién definido hereda HandlerInterceptorAdapter
@Component
@Slf4j
public class LogMonitorInterceptor extends HandlerInterceptorAdapter {
    
    

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        String requestParams = StringUtils.EMPTY;
        if (request instanceof RequestWrapper) {
    
    
            requestParams = ((RequestWrapper) request).getBody();
        }
        if(StrUtil.isEmpty(requestParams)){
    
    
            requestParams = JSON.toJSONString(request.getParameterMap());
        }
        log.info("request:  uri:{} , type:{} , ip:{}, operatorId:{}, operatorName:{}, params:{}",
                request.getRequestURI(),request.getMethod(),request.getRemoteAddr(),
                WebUtils.getId(),WebUtils.getUsername(), requestParams );
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
    
        HttpSession httpSession = request.getSession();
        String result = (String) httpSession.getAttribute("body");

        log.info("response:  url:{} , type:{} , ip:{}, operatorId:{}, operatorName:{}, result:{}",
                request.getRequestURI(),request.getMethod(),request.getRemoteAddr(),
                WebUtils.getId(),WebUtils.getUsername(), result );
    }
}
2.5 Implementar WebMvcConfigurer
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    
    @Autowired
    private LogMonitorInterceptor logMonitorInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    

        // 接口操作日志拦截器
        registry.addInterceptor(logMonitorInterceptor);
    }
}

Supongo que te gusta

Origin blog.csdn.net/qq798867485/article/details/129861674
Recomendado
Clasificación