加打印语句,将请求参数打印出来。后面想想,以后可能还会遇到这样的情形,若是每次遇到,我都去对应的方式中加日志打印,就酿成重复事情。而且日志打印跟我们的营业自己没有任何关系。

纪录日志网上主要有三种方式:

  1. aop
  2. filter
  3. interceptor

我选择了filter。为什么选择它,由于我以为它相对于界说切点,然后切点前后处置来说,加倍利便;相对于 interceptor, 我加倍熟悉这种方式。

 

界说Filter

界说一个 LogFilter  。 内里临 HttpServletRequest  举行阻挡,凭据对应的 content-type 剖析请求参数。主要代码如下

/**
 * 功效形貌: 打印请求参数
 * @author lkb
 * @date 2020/5/6
 * @param
 * @return
 */
@Slf4j
public class LogFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

        //日志
        doLog(request, response);

        // 将request 传到下一个Filter
        filterChain.doFilter(request, response);
    }


    private void doLog(HttpServletRequest request,  HttpServletResponse response){
        // 输出请求体
        log.info("request. uri = {}, method = {}, requestParam = {}", request.getRequestURI(), request.getMethod(), getRequestParam(request));
        //todo 返回效果也可以举行处置
    }


    private String getRequestParam(HttpServletRequest request){
        String requestParam = "";

        String requestContentType = request.getHeader(HttpHeaders.CONTENT_TYPE);
        try {
            if(StringUtils.isNotEmpty(requestContentType)){
                if (requestContentType.startsWith(MediaType.APPLICATION_JSON_VALUE)
                        || requestContentType.startsWith(MediaType.APPLICATION_XML_VALUE)) {
                    // xml json
                    requestParam = getRequestBody(request);
                }
                else if (requestContentType.startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) {
                    // 文件表单提交
                    requestParam = getFormParam(request);
                }else if(requestContentType.startsWith(MediaType.APPLICATION_FORM_URLENCODED_VALUE)){
                    // 通俗表单提交
                    requestParam = toJson(request.getParameterMap());
                }
            }else{
                // 默认通俗表单提交
                requestParam = toJson(request.getParameterMap());
            }
        }catch (Exception e){
            log.error("getRequestParam error");
            log.error(e.getMessage(),e);
        }
        return requestParam;
    }
    
    ...
}

 

然后,注册这个filter

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean logFilter() {
        final FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        final LogFilter logFilter = new LogFilter();
        filterRegistrationBean.setFilter(logFilter);
        return filterRegistrationBean;
    }

}

 

上面两步之后,重启项目,可以看到请求过来后会打印出请求的uri、method、param 。

 

InputStream 只能读取一次

原本以为这样就万事大吉了。然则事实并不是云云。加上上面代码后会发现,再controller 加上的 @requestBody 没有效果,取不到任何数据,并抛出异常,告诉我们请求已经被读取过。 为什么呢?

 

缘故原由很简单。由于在 doLog 中获取请求参数的时刻,我们已经将请求的 inputStream 给读取了。读取inputStream 时有一个offset,它示意你从那里最先读取输入流。由于我们读取了一遍 inputStream,以是offset已经在流的最末端了。我们再去读取,就会发现没有东西可以读了。若是想重复读取 inputStream 就需要每次读取后重置 offset 的值。

 

固然为了利便,我并没有去重新inputStream 中的reset 方式。而是选择,在读取请求后,将请求缓存起来。

 

首先,BufferedServletInputStream 继续自 ServletInputStream。

public class BufferedServletInputStream extends ServletInputStream {

    private ByteArrayInputStream inputStream;

    public BufferedServletInputStream(byte[] buffer) {
        this.inputStream = new ByteArrayInputStream( buffer );
    }

    @Override
    public int available(){
        return inputStream.available();
    }

    @Override
    public int read(){
        return inputStream.read();
    }

    @Override
    public int readLine(byte[] b, int off, int len){
        return inputStream.read( b, off, len );
    }

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

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

    @Override
    public void setReadListener(ReadListener readListener) {

    }
}

 

然后,BufferedServletRequestWrapper 继续 HttpServletRequestWrapper。

@Slf4j
public class BufferedServletRequestWrapper extends HttpServletRequestWrapper {

    private byte[] buffer;

    public BufferedServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        InputStream is = request.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte buff[] = new byte[1024];
        int read;
        while ((read = is.read(buff)) > 0) {
            baos.write(buff, 0, read);
        }
        this.buffer = baos.toByteArray();
    }

    @Override
    public ServletInputStream getInputStream() {
        return new BufferedServletInputStream(this.buffer);
    }
}

内里使用一个 byte[] buffer 数组将请求缓存起来。

 

最后,在 LogFilter 中 doLog 前,对请求举行包装。

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
    throws ServletException, IOException {
    // 备份HttpServletRequest
    request = new BufferedServletRequestWrapper(request);

    //日志
    doLog(request, response);

    // 将request 传到下一个Filter
    filterChain.doFilter(request, response);
}

经由上诉处置,我们就可以愉快地用日志纪录请求参数了。

 

总结

最后总结一下:

1. 纪录请求参数日志的方式最好接纳切面的头脑

2. inputStream 默认只能读取一次,多次读取要重新处置inputStream

3. @requestBody 的原理可以领会一下

 

 

 

,

进入sunbet官网手机版登陆

欢迎进入sunbet官网手机版登陆!Sunbet 申博提供申博开户(sunbet开户)、SunbetAPP下载、Sunbet客户端下载、Sunbet代理合作等业务。

Allbet Gaming声明:该文看法仅代表作者自己,与阳光在线无关。转载请注明:新乡文明网:【实践】切面打印请求参数
发布评论

分享到:

郑州景点:担心女大未嫁!拳王泰森出「3亿元」征女婿 网惊:女儿「汉草」超越泰森
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。