解决:Spring boot系统请求/响应参数打印_代码实现

关键词:

请求参数,响应参数,过滤器,拦截器,日志过滤器,配置过滤器

一、目的:

对系统请求参数和响应参数,进行输出打印。支持表单提交json提交

二、过滤器与拦截器

  首先,了解一下过滤器和拦截器。两者功能很类似,但具体技术实现,相去甚远。对比两者的区别前,先理解一下AOP,AOP不是一种具体的技术,而是一种编程思想。在面向对象编程的过程中,我们很容易通过继承、多态来解决纵向扩展。 但是对于横向的功能,比如,在所有的service方法中开启事务,或者统一记录日志等功能,面向对象的是无法解决的。所以AOP——面向切面编程其实是面向对象编程思想的一个补充。而我们今天讲的过滤器和拦截器都属于面向切面编程的具体实现。而两者的主要区别包括以下几个方面:

  1、Filter是依赖于Servlet容器,属于Servlet规范的一部分;而拦截器则是独立存在的,可以在任何情况下使用。

  2、Filter的执行由Servlet容器回调完成;而拦截器通常通过动态代理的方式来执行。

  3、Filter的生命周期由Servlet容器管理;而拦截器则可以通过IoC容器来管理;因此,可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。

三、日志过滤器_代码

import com.alibaba.fastjson.JSON;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * 如果包含header参数,不做输出
 * 顺便 输出对应url 请求体,响应体,耗时
 */
public class LogFilter extends OncePerRequestFilter {
	
	private static final Logger log = LoggerFactory.getLogger(LogFilter.class);

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
		long requestTime = System.currentTimeMillis();
		String uri = request.getRequestURI();
		String contextPath = request.getContextPath();
		String url = uri.substring(contextPath.length());
		//静态资源 跳过
		if (url.contains(".")) {
			filterChain.doFilter(request, response);
			return;
		}
//		输出请求体
		String requestBody = "";
		String requestContentType = request.getHeader(HttpHeaders.CONTENT_TYPE);

		if (requestContentType != null){
//			xml json
			if (requestContentType.startsWith(MediaType.APPLICATION_JSON_VALUE) || requestContentType.startsWith(MediaType.APPLICATION_XML_VALUE)){
				requestBody = getRequestBody(request);
				final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody.getBytes(StandardCharsets.UTF_8));
				request = new HttpServletRequestWrapper(request) {
					@Override
					public ServletInputStream getInputStream() throws IOException {
						return new ByteArrayServletInputStream(byteArrayInputStream);
					}
				};
//		    普通表单提交
			}else if (requestContentType.startsWith(MediaType.APPLICATION_FORM_URLENCODED_VALUE)){
				requestBody = toJson(request.getParameterMap());
//			文件表单提交
			}else if (requestContentType.startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)){
				requestBody = getFormParam(request);
			}
		}

		final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		response = new HttpServletResponseWrapper(response) {
			@Override
			public ServletOutputStream getOutputStream() throws IOException {
				return new TeeServletOutputStream(super.getOutputStream(), byteArrayOutputStream);
			}
		};

		filterChain.doFilter(request, response);

		long costTime = System.currentTimeMillis() - requestTime;
		String responseBody = "";
//		暂定只有json 输出响应体
		String contentType = response.getHeader(HttpHeaders.CONTENT_TYPE);
		if (contentType != null && contentType.startsWith(MediaType.APPLICATION_JSON_VALUE)){
			responseBody = byteArrayOutputStream.toString();
		}

		if (response.getStatus() >= 200 && response.getStatus() < 300) {
            log.info("URL:{}, total time:{} ms, responseCode:{}, requestBody:{}, responseBody:{}", url, costTime, response.getStatus(), requestBody, responseBody);
        }else {
            log.error("URL:{}, total time:{} ms, responseCode:{}, requestBody:{}, responseBody:{}", url, costTime, response.getStatus(), requestBody, responseBody);
        }
	}

	private String getRequestBody(HttpServletRequest request) {
		int contentLength = request.getContentLength();
		if(contentLength <= 0){
			return "";
		}
		try {
			return IOUtils.toString(request.getReader());
		} catch (IOException e) {
			log.error("获取请求体失败", e);
			return "";
		}
	}

	private String getFormParam(HttpServletRequest request) {
        MultipartResolver resolver = new StandardServletMultipartResolver();
        MultipartHttpServletRequest mRequest = resolver.resolveMultipart(request);

		Map<String,Object> param = new HashMap<>();
        Map<String,String[]> parameterMap = mRequest.getParameterMap();
        if (!parameterMap.isEmpty()){
            param.putAll(parameterMap);
        }
        Map<String, MultipartFile> fileMap = mRequest.getFileMap();
        if(!fileMap.isEmpty()){
            for (Map.Entry<String, MultipartFile> fileEntry : fileMap.entrySet()) {
                MultipartFile file = fileEntry.getValue();
                param.put(fileEntry.getKey(), file.getOriginalFilename()+ "(" + file.getSize()+" byte)");
            }
        }
		return toJson(param);
	}

    private static String toJson(Object object){
		return JSON.toJSONStringWithDateFormat(object, "yyyy-MM-dd HH:mm:ss");
	}
}
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;

/**
 * Created by liuxd on 2020/06/20.
 */
public class ByteArrayServletInputStream extends ServletInputStream {

    private ByteArrayInputStream byteArrayInputStream;

    public ByteArrayServletInputStream(ByteArrayInputStream byteArrayInputStream) {
        this.byteArrayInputStream = byteArrayInputStream;
    }

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

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

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

    @Override
    public void setReadListener(ReadListener readListener) {

    }
}

 

import org.apache.commons.io.output.TeeOutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import java.io.IOException;
import java.io.OutputStream;

/**
 * Created by liuxd on 2020/06/20.
 */
public class TeeServletOutputStream extends ServletOutputStream {

    private final TeeOutputStream teeOutputStream;

    public TeeServletOutputStream(OutputStream one, OutputStream two) {
        this.teeOutputStream = new TeeOutputStream(one, two);
    }

    @Override
    public void write(byte[] b) throws IOException {
        this.teeOutputStream.write(b);
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        this.teeOutputStream.write(b, off, len);
    }

    @Override
    public void write(int b) throws IOException {
        this.teeOutputStream.write(b);
    }

    @Override
    public void flush() throws IOException {
        super.flush();
        this.teeOutputStream.flush();
    }

    @Override
    public void close() throws IOException {
        super.close();
        this.teeOutputStream.close();
    }

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

    @Override
    public void setWriteListener(WriteListener writeListener) {

    }
}

请留意代码:filterChain.doFilter(request, response);

即在方法执行前先记录时间戳,以及请求参数;然后,通过过滤器链完成请求的执行;在此次之后,收集响应参数,在返回结果之间计算执行打印。

四、配置过滤器_代码

import com.myfutech.common.spring.filter.LogFilter;
import com.myfutech.common.spring.filter.UserInfoFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {

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

}

参考:

https://www.cnblogs.com/paddix/p/8365558.html

 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
您提到的错误信息"Uncaught runtime errors:×ERRORNetwork ErrorAxiosError: Network Error at XMLHttpRequest.handleError (webpack-internal:///./node_modules/axios/lib/adapters/xhr.js:155:14)"是Axios库在发送网络请求时出现的网络错误。这个错误通常是由于网络连接问题或后端服务器无法响应导致的。根据错误信息中提到的位置"webpack-internal:///./node_modules/axios/lib/adapters/xhr.js:155:14",问题可能出现在Axios库的xhr.js文件的第155行的处理函数中。然而,无法确定具体的解决方案,因为没有提供更多的上下文信息。要解决此问题,您可以尝试以下几个步骤: 1. 确保您的网络连接正常,尝试通过其他方式访问相同的请求URL,检查是否能够成功获取响应。 2. 检查您的后端服务器是否正常运行,并能够正确处理该请求。您可以通过访问后端API的URL,或者使用类似Postman的工具进行测试。 3. 检查您的webpack配置文件中是否正确引入了Axios库,并且是否进行了适当的配置。确保您引入了正确的Axios版本,并按照官方文档提供的方式使用它。 4. 如果您使用了代理服务器进行请求转发,请确认代理服务器的配置正确,并且能够正确地转发请求到后端服务器。 如果尝试了以上步骤仍然无法解决问题,建议您提供更多的上下文信息,例如您发送请求代码片段、后端服务器的相关信息等。这样有助于更准确地诊断和解决问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [node-polyfill-webpack-plugin:Webpack中的Polyfill Node.js核心模块](https://download.csdn.net/download/weixin_42131443/18341461)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Vue3与spring boot 的跨域问题](https://blog.csdn.net/MonkeyRideDonkey/article/details/131872502)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [手动解决Cannot find module '@sentry/webpack-plugin'等问题](https://download.csdn.net/download/jlq_diligence/28725246)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

春风化作秋雨

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值