SpringBoot拿到所有Http请求的入参和出参

2,521 阅读2分钟

在web应用中,有各式各样的访问者,这些访问者请求的方式也可能完全出乎所料,对请求的监控是稳定性的一个基本动作,那基于Java构建的Web应用也需要做一些默认的处理。

对所有的Http请求进行监控,有两种方式:

  • 基于AOP:但是需要所有的入口有确定的格式**(有规范)**,开发者可能感受差一些
  • 基于拦截器:拦截器本身就是Web请求体系的一部分,拦截和业务功能可以解耦,业务专注与业务,做监控专注与监控。

如果说基于拦截器的话,可以怎么处理:

1、首先要配置拦截器

2、拦截器中要能捕获入参

3、打印日志,进行统计

给个例子

Demo

1、配置SpringBoot的框架,可以去start.spring.io/ 下载一个脚手架功能

2、添加一些基本的依赖,也是常用的库,项目中经常用到

<dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>28.2-jre</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.12.0</version>
    </dependency>

3、编写拦截器,及其处理


import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.fastjson.JSON;

import lombok.extern.slf4j.Slf4j;
import me.aihe.init.util.HttpUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

/**
 */
@Component
@Slf4j
public class HttpInterceptor extends HandlerInterceptorAdapter {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        try {
            String requestURI = request.getRequestURI();
            Map<String,Object> paramMap = HttpUtils.getParameterMap(request);

            log.info("HttpInterceptor {} {} {}",request.getMethod(),requestURI,JSON.toJSONString(paramMap));

            return true;
        }  catch (Exception e){
            log.error("HttpInterceptor preHandle error",e);
        }
        return true;
    }

}

4、获取Http请求入参和出参的工具类


import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;

import com.alibaba.fastjson.JSON;

import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

@Slf4j
public class HttpUtils {
    /**
     * 获取请求参数,把请求参数转成Mapd
     *
     * @param request request对象
     * @return 参数集合map
     */
    public static Map<String, Object> getParameterMap(HttpServletRequest request) {
        try {
            Map<String, String[]> parameterMap = request.getParameterMap();
            Map<String, Object> returnMap = Maps.newHashMap();
            // 默认参数
            for (Entry<String, String[]> entry : parameterMap.entrySet()) {
                String value = "";
                String key = entry.getKey();
                Object valueObj = entry.getValue();

                if (valueObj == null) {
                    value = "";
                } else if (!(valueObj instanceof String[])) {
                    value = valueObj.toString();
                } else {
                    String[] values = (String[])valueObj;
                    for (String temp : values) {
                        value = temp + ",";
                    }
                    value = value.substring(0, value.length() - 1);
                }
                returnMap.put(key, value);
                //post等raw内容 content-type:application/json

            }

            // PostBody参数
            try {
                ByteArrayOutputStream bos = getBOS(request.getInputStream());
                String ok = bos.toString();
                if (StringUtils.isNotEmpty(ok)){
                    returnMap.putAll(JSON.parseObject(ok));
                }
            } catch (Exception e) {
                log.error("getParameterMap stream error", e);
            }

            return returnMap;
        } catch (Exception e) {
            log.error("getParameterMap error", e);
        }
        return new HashMap<>();
    }

    private static ByteArrayOutputStream getBOS(InputStream in) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            BufferedInputStream br = new BufferedInputStream(in);
            for(int c=0;(c=br.read())!=-1;){
                bos.write(c);
            }
            br.close();
        } catch (Exception e) {
            log.error("getBOS error",e);
        }
        return bos;
    }

}

5、配置拦截器,使其生效


import me.aihe.init.interceptor.HttpInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig  implements WebMvcConfigurer {

    @Autowired
    HttpInterceptor httpInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //登录拦截器
        //registry
        //    .addInterceptor(new LoginInterceptor())
        //    .addPathPatterns("/**").excludePathPatterns(LoginInterceptor.EXCLUDE_PATH_PATTERNS);

        registry.addInterceptor(httpInterceptor)
            .addPathPatterns("/**");

    }
}

效果

image-20220524093245009

关于扩展

这里写的比较粗糙,只是把流程演示了一遍;还可以做的:

  • 日志格式的规范化,对日志进行采集监控
  • 还可以对入参进行校验,鉴权等

其实就是拦截器可以做的事情,这里都可以实现一些相关功能的拦截器进行处理,只是这个拦截器仅仅打印请求的入参和出参而已。