使用拦截器获取请求信息和返回信息

这篇博客探讨了如何在Spring MVC中使用拦截器(HandlerInterceptor)和ResponseBodyAdvice来处理请求和响应。文中提到,由于拦截器无法直接获取请求体内容,因此选择了ResponseBodyAdvice作为解决方案,它能完整获取请求和响应信息,并展示了具体实现代码。主要功能包括:获取请求头信息,处理设备型号和登录日志,以及通过ActiveMQ发送相关信息。

一、要使用拦截器获取请求信息和返回信息,而不是使用切面。
1、使用拦截器一开始使用的是spring的HandlerIntercepter。但是使用这个无法获取到response和request请求体内容。因为两者通过流来读取,而只能读取一次。因为要Controller读取。所以获取不到。

代码如下:
1、Config

package com.sinosoft.app.platform.interceptor.config;

import com.sinosoft.app.platform.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
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 LoginLogInterceptorConfig implements WebMvcConfigurer {

    /**
     * 为了让拦截器添加到spring容器中
     * @return
     */
    @Bean
    public LoginInterceptor loginInterceptor(){
        return new LoginInterceptor();
    }
    //添加自定义拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
//        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/sys/auth/login/app");
        registry.addInterceptor(loginInterceptor()).addPathPatterns("/sys/auth/login/app1");
    }

}

2、Intercepter

package com.sinosoft.app.platform.interceptor;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sinosoft.app.base.activemq.util.ActivemqUtil;
import com.sinosoft.app.platform.interceptor.entity.CustTerminalInfo;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.jms.Queue;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//@Component
public class LoginInterceptor implements HandlerInterceptor {

   private static final org.slf4j.Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
//    //注入存放消息的队列
//    @Resource
//    private Queue queue;
//    //注入springboot封装的工具类
//    @Resource
//    private JmsMessagingTemplate jmsMessagingTemplate;

    @Override
    public void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        String type = request.getHeader("x-ss-from");
        //财富端或企业端登录
        if (!StringUtils.isEmpty(type)){
            //获取设备型号
            String deviceInfor = request.getHeader("deviceInfor");
            if (!StringUtils.isEmpty(deviceInfor)){
                CustTerminalInfo custTerminalInfo = JSONObject.parseObject(deviceInfor, CustTerminalInfo.class);
                custTerminalInfo.setType(type);
                //发送消息给business存入设备型号
                ActivemqUtil.sender("terminal_info_",JSON.toJSONString(custTerminalInfo));
//                jmsMessagingTemplate.convertAndSend(queue, custTerminalInfo);
            }
        }
    }

}


2、再次使用的是Spring的ResponseBodyAdvice。该方法可以完美的获取到想要的信息。需要注意的是获取到的params都是自动带着个  [] 。

代码:
 

package com.sinosoft.app.platform.interceptor;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sinosoft.app.base.activemq.util.ActivemqUtil;
import com.sinosoft.app.base.utils.Result;
import com.sinosoft.app.base.utils.TokenUser;
import com.sinosoft.app.base.utils.WebUtil;
import com.sinosoft.app.base.vo.ReturnInfoVo;
import com.sinosoft.app.platform.interceptor.LoginInterceptor;
import com.sinosoft.app.platform.interceptor.entity.CustTerminalInfo;
import com.sinosoft.app.platform.interceptor.entity.LoginLog;
import net.sf.json.JSONString;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Parameter;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author JYJ
 * @since  2021-6-18 16:09:59
 * @apiNote 落库设备信息和登录日志
 */
@ControllerAdvice
public class LoginResponseBodyAdvice implements ResponseBodyAdvice {

    private static final org.slf4j.Logger log = LoggerFactory.getLogger(LoginInterceptor.class);

    /**
     * 参数返回给前端之前进行,可以进行相关的处理例如,记录请求参数,响应参数,封装统一响应参数等操作
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request,
                                  ServerHttpResponse response) {
        try {
            if(request.getURI().toASCIIString().indexOf("/login/app")>0){
                //获取到返回内容
                String bodyStr = JSON.toJSONString(body);
                ReturnInfoVo result = JSONObject.parseObject(bodyStr, ReturnInfoVo.class);
                if (result.isSuccess()){
                    //获取到请求头
                    HttpHeaders headers = request.getHeaders();
                    String type = String.valueOf(headers.get("x-ss-from"));
                //财富端或企业端登录
                if (!StringUtils.isEmpty(type)){
                    type = type.replace("[","");
                    type = type.replace("]","");
                    //获取设备型号
                    String deviceInfor = String.valueOf(headers.get("deviceInfor"));
                    if (!StringUtils.isEmpty(deviceInfor)){
//                        String jsonString = JSON.toJSONString(deviceInfor);
                        deviceInfor = deviceInfor.replace("[","");
                        deviceInfor = deviceInfor.replace("]","");
                        CustTerminalInfo custTerminalInfo = JSONObject.parseObject(deviceInfor,CustTerminalInfo.class);
//                        CustTerminalInfo custTerminalInfo = custTerminalInfos.get(0);
                        custTerminalInfo.setType(type);
                        String userCode = String.valueOf(result.getResult());
                        custTerminalInfo.setCustCode(userCode);
                        //发送消息给business存入设备型号
                        ActivemqUtil.sender("user_terminal_info_",JSON.toJSONString(custTerminalInfo));
                        //发送登录日志
                        //只有企业端和财富端进行落库
                        if ("1".equals(type)||"2".equals(type)) {
                            LoginLog loginLog = new LoginLog();
                            loginLog.setUserType(type);
                            loginLog.setUserCode(userCode);
                            loginLog.setOperateDate(new Date());
                            loginLog.setTerminalInfo(deviceInfor);
//                            try {
//                                TokenUser user = WebUtil.getUser();
//                                log.info(JSON.toJSONString(user));
////                              loginLog.setUserName(user.getUserName());
//                            }catch (Exception e){
//                                log.info("NULLLLLLL");
//                            }
                            //发送登录日志
                            ActivemqUtil.sender("user_login_log_", JSON.toJSONString(loginLog));
                        }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


        return body;
    }


    /**
     * 如果要beforeBodyWrite方法生效,必须返回true
     * @param arg0
     * @param arg1
     * @return
     */
    @Override
    public boolean supports(MethodParameter arg0, Class arg1) {
        return true;
    }

    /**
     * 将inputStream里的数据读取出来并转换成字符串
     *
     * @param inputStream inputStream
     * @return String
     */
    private String inputStream2String(InputStream inputStream) {
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = null;

        try {

            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    log.error(e.getMessage());
                }
            }
        }
        return sb.toString();
    }
}

 

使用 Feign 拦截器时,若需获取请求路径的协议 IP 端口信息,可以通过 `feign.RequestTemplate` 对象来实现。在拦截器的 `apply` 方法中,`RequestTemplate` 提供了访问请求 URL 的能力,包括完整的协议、主机名端口号。 ### 获取请求路径的协议 IP 端口信息 Feign 拦截器中,可以通过 `RequestTemplate` 获取完整的请求 URL,然后解析出协议 IP 端口信息。例如: ```java import feign.RequestInterceptor; import feign.RequestTemplate; import org.springframework.stereotype.Component; import java.net.URI; @Component public class FeignClientInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { // 获取完整的请求URL String requestUrl = template.url(); // 解析URL URI uri = URI.create(requestUrl); String scheme = uri.getScheme(); // 获取协议 String host = uri.getHost(); // 获取主机名 int port = uri.getPort(); // 获取端口号 // 输出协议、主机名端口号 System.out.println("Protocol: " + scheme); System.out.println("Host: " + host); System.out.println("Port: " + port); } } ``` 通过上述方式,可以准确获取请求路径的协议 IP 端口信息。需要注意的是,`uri.getPort()` 返回的是整数类型的端口号,如果 URL 中未显式指定端口,则返回 `-1`。此时可以根据默认协议(如 HTTP 默认 80,HTTPS 默认 443)进行处理。 ### 结合动态代理 Feign 代理对象 若请求地址配置在系统参数中,Feign 代理对象在启动阶段可能无法获取到系统参数值,因为 DAO 实例尚未初始化[^1]。为了解决此问题,可以在运行阶段通过 JDK 动态代理生成一个空的代理对象,并在调用 `invoke` 方法时再生成可用的 Feign 代理类,从而实现动态获取请求路径信息的功能。 ### 示例:动态代理结合 Feign ```java import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class DynamicFeignProxy implements InvocationHandler { private final Object target; public DynamicFeignProxy(Object target) { this.target = target; } public static Object createProxy(Object target) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new DynamicFeignProxy(target) ); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 在运行阶段再生成可用的Feign代理类 return method.invoke(target, args); } } ``` 通过上述方式,确保在运行阶段获取到完整的请求路径信息,并动态生成 Feign 代理对象。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值