首先我们通过实现Filter接口来实现它的init和doFilter方法。
@Slf4j
@WebFilter(urlPatterns = "/*",filterName = "channelFilter")
public class ChannelFilter implements Filter {
private SysMachineService sysMachineService;
// 要过滤的地址
private static final String LOGIN_URI = "/api/";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
ServletContext sc = filterConfig.getServletContext();
ApplicationContext cxt = WebApplicationContextUtils.getWebApplicationContext(sc);
BeanHeader beanHeader = new BeanHeader();
beanHeader.setApplicationContext(cxt);
// 获取业务处理的service
this.sysMachineService = BeanHeader.getBean(SysMachineService.class);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
response.setContentType("text/html;charset=utf-8");
String requestPath = request.getRequestURI();
if(requestPath.contains(LOGIN_URI)){
try{
// 通过继承HttpServletRequestWrapper类重写他的方法获取请求参数
RequestWrapper requestWrapper = new RequestWrapper(request,sysMachineService);
String body = new String(requestWrapper.getBody());
log.info("RequestBody: {}", body);
这里就可以处理你获取到请求报文之后做的处理了
ResponseWrapper responseWrapper = new ResponseWrapper(response);
chain.doFilter(requestWrapper, responseWrapper);
// 获取接口响应报文
String resMsg = "";
ResponseWriter responseWriter = responseWrapper.getResponseWriter();
if (responseWriter != null) {
resMsg = responseWriter.getContent();
log.info("接口响应报文:" + resMsg);
}
这里我们可以处理接口返回结果之后的业务逻辑处理
}catch (Exception e){
log.error("异常:", e);
} finally {
}
}else{
chain.doFilter(request, response);
}
}
}
如果过滤器处理之后需要讲结果返回,那可以通过流方式就结果写出去。
private void sendJsonMessage(HttpServletResponse response, Object obj) {
try {
response.setContentType("application/json; charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print(JSONObject.toJSONString(obj, new SerializerFeature[]{SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat}));
writer.close();
response.flushBuffer();
} catch (Exception e){
log.error("输出异常:" + e.getMessage());
}
}
关键代码,我们通过继承HttpServletRequestWrapper类来实现我们在过滤器中读取请求接口时的请求报文;也可以在请求的报文中添加个别参数,比如用户的基础信息,或者其他一些参数,这些参数在调用接口时不需要调用方传参,但是在接口处理业务逻辑时又需要用到这些参数,就可以通过这种来实现。
public class RequestWrapper extends HttpServletRequestWrapper {
private byte[] body;
private SysMachineService sysMachineService;
public RequestWrapper(HttpServletRequest request, SysMachineService sysMachineService) {
super(request);
this.sysMachineService = sysMachineService;
try {
Map map = getParameterMap();
if (map.size() > 0) {
map = detectParameter(map);
body = JSON.toJSONString(map).getBytes();
} else {
// 获取请求接口时的请求报文,通过流读取参数数据,并保存
body = getByteByStream(request.getInputStream());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 重写获取流中参数方法
*/
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream is = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() throws IOException {
return is.read();
}
};
}
/**
* 取出流中的数据,做参数检验后放入body中
*/
public byte[] getByteByStream(InputStream is) throws Exception {
byte[] buffer = new byte[1024];
int len = 0;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//读出流中的数据
while ((len = is.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
bos.flush();
bos.close();
if (bos.size() > 0) {
//把数据转成Map形式,遍历进行检测
String str = bos.toString();
Map maps = detectParameter((Map) JSON.parse(str));
// 这里其实就是我们在请求报文中新增加了两个业务参数orgId和tenantId,它们是接口请求时请求报文中没有参数,我们通过过滤器将参数写入到报文中的
if(maps.containsKey("appKey")) {
String appKey = maps.get("appKey").toString();
SysMachine entity = this.sysMachineService.selectSysMachine(appKey);
maps.put("orgId", entity.getOrgId());
maps.put("tenantId", entity.getTenantId());
}
//保存检测后的数据
str = JSON.toJSONString(maps);
return str.getBytes();
}
return bos.toByteArray();
}
/**
* 获取请求报文
*/
public Map detectParameter(Map parameterMap) {
Map map = new HashMap();
//遍历参数MAP
for (Object key : parameterMap.keySet()) {
Object parameter = parameterMap.get(key);
//判断参数类型,集合和数组遍历拆解检测,检测完存入检测结果集合
if (parameter instanceof JSONObject) {
map.put(key, detectParameter((Map) parameter));
} else if (parameter instanceof JSONArray) {
JSONArray parameterArray = (JSONArray) parameter;
for (int i = 0; i < parameterArray.size(); i++) {
parameterArray.set(i, detectParameter(parameterArray.getJSONObject(i)));
}
map.put(key, parameterArray);
} else {
map.put(key, parameter);
}
}
return map;
}
public byte[] getBody() {
return this.body;
}
获取接口返回报文的我们也是通过继承HttpServletResponseWrapper和PrintWriter来重写它们的方法来实现。
public class ResponseWrapper extends HttpServletResponseWrapper {
private ResponseWriter responseWriter;
public ResponseWrapper(HttpServletResponse response) {
super(response);
}
@Override
public PrintWriter getWriter() throws IOException {
responseWriter = new ResponseWriter(super.getWriter());
return responseWriter;
}
public ResponseWriter getResponseWriter() {
return responseWriter;
}
}
public class ResponseWriter extends PrintWriter {
private StringBuilder buffer;
public ResponseWriter(Writer out) {
super(out);
buffer = new StringBuilder();
}
@Override
public void write(char[] buf, int off, int len) {
char[] dest = new char[len];
buffer.append(dest);
}
@Override
public void write(char[] buf) {
super.write(buf);
}
@Override
public void write(int c) {
super.write(c);
}
@Override
public void write(String s, int off, int len) {
super.write(s, off, len);
buffer.append(s);
}
@Override
public void write(String s) {
super.write(s);
}
public String getContent(){
return buffer.toString();
}
}
最后还有一个很重要的是在SpringBoot的启动类加上@ServletComponentScan注解,为啥我们要加这个注解了,我们来看看官网API的介绍:
最后贴上一个获bean的代码
public class BeanHeader implements ApplicationContextAware {
private static final Logger log = LoggerFactory.getLogger(BeanHeader.class);
private static ApplicationContext applicationContext;
public BeanHeader() {
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
BeanHeader.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static boolean checkapplicationContext() {
boolean flag = getApplicationContext() != null;
if (!flag) {
log.error("applicaitonContext未注入,实现ApplicationContextAware的类必须被spring管理");
}
return flag;
}
public static <T> T getBean(String name) {
return checkapplicationContext() ? (T) getApplicationContext().getBean(name) : null;
}
public static <T> T getBean(Class<T> clazz) {
return checkapplicationContext() ? getApplicationContext().getBean(clazz) : null;
}
public static <T> T getBean(String name, Class<T> clazz) {
return checkapplicationContext() ? getApplicationContext().getBean(name, clazz) : null;
}
然后我们做个测试
在我们请求的报文中是没有tenantId和orgId这两个参数的,但是打印的日志中我们实现了添加这两个参数;并且我们在过滤器中也成功获取到了接口请求参数,实现了我们的需求。
最近在工作中遇到这样一个需求,所以简单的写一下,有同学遇到同样的需求也可以做个参考。