zuul 验证,重写返回报文,解析gzip压缩response,使用案例

业务是调用另一个平台API,用他们的接口能力实现一些功能。

真正请求前的filter,我把一些请求前的验证和日志入库放在了这里。

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.util.DateUtils;
import com.iMagine.iMagine_common.exception.MyException;
import com.iMagine.iMagine_common.result.CommonResult;
import com.iMagine.iMagine_common.utils.UUIDUtil;
import com.iMagine.iMagine_mapper.entity.User;
import com.iMagine.iMagine_pro.constant.ErrorEnum;
import com.iMagine.iMagine_pro.constant.SysConstant;
import com.iMagine.iMagine_pro.feign.body.AiServerResponseBody;
import com.iMagine.iMagine_pro.service.MjToAiService;
import com.iMagine.iMagine_pro.utils.TokenUtil;
import com.iMagine.iMagine_pro.utils.ZuulParameterUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.util.HTTPRequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StreamUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * Zuul过滤器,必须继承ZuulFilter父类。
 * 当前类型的对象必须交由Spring容器管理。使用@Component注解描述。
 * 继承父类后,必须实现父类中定义的4个抽象方法。
 * shouldFilter、 run、 filterType、 filterOrder
 */
@Component
@Slf4j
public class LoggerPostFilter extends ZuulFilter {

    @Autowired
    private MjToAiService mjToAiService;

    /**
     * 返回boolean类型。代表当前filter是否生效。
     * 默认值为false。
     * 返回true代表开启filter。
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * run方法就是过滤器的具体逻辑。
     * return 可以返回任意的对象,当前实现忽略。(spring-cloud-zuul官方解释)
     * 直接返回null即可。
     */
    @Override
    public Object run() {
        log.info("LoggerPostFilter任务处理开始...");
        // 获取zuul提供的上下文对象
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String requestUrl = request.getRequestURI();
        log.info("requestUrl:{}",requestUrl);
        //创建统一UUID
        String uuid = UUIDUtil.getUUID();
        ConcurrentHashMap<String,Object> params = new ConcurrentHashMap<>(4);
        HttpServletResponse response = context.getResponse();
        params.put("uuid",uuid);
        params.put("date",new Date());
        try {
            InputStream responseDataStream = context.getResponseDataStream();
            String contentEncoding = context.getZuulRequestHeaders().get("accept-encoding");
            if (responseDataStream != null && "gzip".equals(contentEncoding) && context.getResponseGZipped()) {
                responseDataStream  = new GZIPInputStream(context.getResponseDataStream());
            }else{
                responseDataStream = context.getResponseDataStream();
            }
            String body = StreamUtils.copyToString(responseDataStream, Charset.forName("UTF-8"));
            //根据code值替换提示内容
            JSONObject requestJson = JSON.parseObject(body);
            log.info("requestJson:{}",requestJson);
            // 处理一些返回的数据到params
            params.put("code",response.getStatus());
            params.put("returnMj",body);
            if(!ObjectUtils.isEmpty(response) && response.getStatus() == 200){
                //设置接口记录为执行中状态
                params.put("operationResult",SysConstant.STATUS_TWO);
                //收集一些埋点需要的参数
                handlerSomething(body,params);
            }else{
                //设置接口记录为失败状态
                params.put("operationResult",SysConstant.STATUS_ONE);
                //根据code替换返回前端的提示信息
                log.error("返回异常报文{}",JSON.toJSONString(requestJson));
                String code = "";
                if(ObjectUtils.isEmpty(requestJson)){
                    code = String.valueOf(response.getStatus());
                    requestJson = new JSONObject();
                    requestJson.put("code",code);
                }else{
                    code = requestJson.get("code").toString();
                }
                String reason = ErrorEnum.ErrorEnumType.getModelUploadType(code);
                if(StringUtils.isBlank(reason)) reason = ErrorEnum.ErrorEnumType.getModelUploadType("1000");
                requestJson.put("reason",reason);
            }
            //对请求的入参进行筛选记录
            log.info("开始调用handlerRequest...,{}",params);
            handlerRequest(context,params);
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json");
            // 将修改后的内容重新压缩为 GZIP 格式
            if (responseDataStream != null && "gzip".equals(contentEncoding) && context.getResponseGZipped()) {
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
                gzipOutputStream.write(JSON.toJSONString(CommonResult.success(requestJson)).getBytes("UTF-8"));
                gzipOutputStream.close();
                // 设置新的压缩后的响应数据流
                context.setResponseDataStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
            }else{
                // 设置新的压缩后的响应数据流
                context.setResponseBody(JSON.toJSONString(CommonResult.success(requestJson)));
            }
        } catch (Exception e) {
            log.error("请求异常!{}",e);
            // 异常数据记录
            User user = TokenUtil.getUserByToken();
            mjToAiService.handlerException(uuid,e,DateUtils.parseDate(params.get("date")+""),user,context.getRequest());
            throw new MyException("请求异常!");
        }
        log.info("LoggerPostFilter任务处理结束!");
        return null;
    }

    /**
     * 收集一些埋点需要的参数
     * @param body 接口返回的报文
     * @param params 需要存储的集合
     */
    private void handlerSomething( String body,ConcurrentHashMap<String,Object> params){
        String paramString = body.replaceAll(" ", "").replaceAll(System.getProperty("line.separator"), "");
        JSONObject responseBody = JSON.parseObject(paramString);
        if (!ObjectUtils.isEmpty(responseBody)){
            log.info("【返回参数】{}" , responseBody);
            //不确定接口返回的是jobid  还是  id
            //如果是正常返回,则覆盖uuid,届时用来对回调接口做关联
            if (ObjectUtils.isEmpty(responseBody.get("id"))){
                params.put("uuid",responseBody.get("jobid"));
            }else{
                params.put("uuid",responseBody.get("id"));
            }
        }
    }

    /**
     * 对请求的入参进行筛选记录
     * 统一记录生图埋点数据
     * @param context
     */
    private void handlerRequest(RequestContext context,ConcurrentHashMap<String,Object> params){
        //对MJ相关生图接口做埋点
        log.info("对MJ相关生图接口做埋点...");
        HttpServletRequest request = context.getRequest();
        String requestUrl = request.getRequestURI();
        Date dateHandler = DateUtils.parseDate(String.valueOf(params.get("date")));
        JSONObject requestMap = JSON.parseObject(JSON.toJSONString(ZuulParameterUtil.getRequestParams(context)));
        User user = TokenUtil.getUserByToken();
        try {
            log.info("接口url:{},参数列表:{}", requestUrl,requestMap);
            String clientIp = TokenUtil.getRequest().getRemoteHost();
            //1.请求悠船文生图接口
            //2.封装AiServerResponseBody
            AiServerResponseBody result = mjToAiService.handlerSpecial(
                    Integer.valueOf(params.get("code")+""),
                    params.get("returnMj") + "",
                    params.get("uuid")+"",
                    dateHandler,user);
            log.info("AiServerResponseBody 存入数据{}",result);
            //3.异步插入调用记录img_ai_record,日活记录img_dau_record,异步处理
            mjToAiService.addRecord(params.get("returnMj") + "",Integer.valueOf(params.get("code")+""),
                    JSON.toJSONString(requestMap),params.get("uuid")+"",dateHandler,request.getRequestURI(),
                    params.get("operationResult")+"",user,clientIp,1);
            log.info("diffusion uuid:{}, result:{}", params.get("uuid")+"", result);
        } catch (Exception e) {
            //5.处理异常
            log.error("handlerRequest异常:{}",e);
            mjToAiService.handlerException(params.get("uuid")+"",e,dateHandler,user,request);
        }
    }

    /**
     * 过滤器的类型。可选值有:
     * pre - 前置过滤
     * route - 路由后过滤
     * error - 异常过滤
     * post - 远程服务调用后过滤
     */
    @Override
    public String filterType() {
        return "post";
    }

    /**
     * 同种类的过滤器的执行顺序。
     * 按照返回值的自然升序执行。
     */
    @Override
    public int filterOrder() {
        return 2;
    }
}

请求回来拦截的filter,解析返回的报文,分析数据和业务处理,最终gzip重构返回报文。

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.util.DateUtils;
import com.iMagine.iMagine_common.exception.MyException;
import com.iMagine.iMagine_common.result.CommonResult;
import com.iMagine.iMagine_common.utils.UUIDUtil;
import com.iMagine.iMagine_mapper.entity.User;
import com.iMagine.iMagine_pro.constant.ErrorEnum;
import com.iMagine.iMagine_pro.constant.SysConstant;
import com.iMagine.iMagine_pro.feign.body.AiServerResponseBody;
import com.iMagine.iMagine_pro.service.MjToAiService;
import com.iMagine.iMagine_pro.utils.TokenUtil;
import com.iMagine.iMagine_pro.utils.ZuulParameterUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.util.HTTPRequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StreamUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
* Zuul过滤器,必须继承ZuulFilter父类。
* 当前类型的对象必须交由Spring容器管理。使用@Component注解描述。
* 继承父类后,必须实现父类中定义的4个抽象方法。
* shouldFilter、 run、 filterType、 filterOrder
*/
@Component
@Slf4j
public class LoggerPostFilter extends ZuulFilter {

@Autowired
private MjToAiService mjToAiService;

/**
* 返回boolean类型。代表当前filter是否生效。
* 默认值为false。
* 返回true代表开启filter。
*/
@Override
public boolean shouldFilter() {
return true;
}

/**
* run方法就是过滤器的具体逻辑。
* return 可以返回任意的对象,当前实现忽略。(spring-cloud-zuul官方解释)
* 直接返回null即可。
*/
@Override
public Object run() {
log.info("LoggerPostFilter任务处理开始...");
// 获取zuul提供的上下文对象
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
String requestUrl = request.getRequestURI();
log.info("requestUrl:{}",requestUrl);
//创建统一UUID
String uuid = UUIDUtil.getUUID();
ConcurrentHashMap<String,Object> params = new ConcurrentHashMap<>(4);
HttpServletResponse response = context.getResponse();
params.put("uuid",uuid);
params.put("date",new Date());
try {
InputStream responseDataStream = context.getResponseDataStream();
//这里的accept-encoding 看源码会自动转为小写
String contentEncoding = context.getZuulRequestHeaders().get("accept-encoding");
if (responseDataStream != null && "gzip".equals(contentEncoding) && context.getResponseGZipped()) {
responseDataStream = new GZIPInputStream(context.getResponseDataStream());
}else{
responseDataStream = context.getResponseDataStream();
}
String body = StreamUtils.copyToString(responseDataStream, Charset.forName("UTF-8"));
//根据code值替换提示内容
JSONObject requestJson = JSON.parseObject(body);
log.info("requestJson:{}",requestJson);
// 处理一些返回的数据到params
params.put("code",response.getStatus());
params.put("returnMj",body);
if(!ObjectUtils.isEmpty(response) && response.getStatus() == 200){
//设置接口记录为执行中状态
params.put("operationResult",SysConstant.STATUS_TWO);
//收集一些埋点需要的参数
handlerSomething(body,params);
}else{
//设置接口记录为失败状态
params.put("operationResult",SysConstant.STATUS_ONE);
//根据code替换返回前端的提示信息
log.error("返回异常报文{}",JSON.toJSONString(requestJson));
String code = "";
if(ObjectUtils.isEmpty(requestJson)){
code = String.valueOf(response.getStatus());
requestJson = new JSONObject();
requestJson.put("code",code);
}else{
code = requestJson.get("code").toString();
}
String reason = ErrorEnum.ErrorEnumType.getModelUploadType(code);
if(StringUtils.isBlank(reason)) reason = ErrorEnum.ErrorEnumType.getModelUploadType("1000");
requestJson.put("reason",reason);
}
//对请求的入参进行筛选记录
log.info("开始调用handlerRequest...,{}",params);
handlerRequest(context,params);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
// 将修改后的内容重新压缩为 GZIP 格式
if (responseDataStream != null && "gzip".equals(contentEncoding) && context.getResponseGZipped()) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
gzipOutputStream.write(JSON.toJSONString(CommonResult.success(requestJson)).getBytes("UTF-8"));
gzipOutputStream.close();
// 设置新的压缩后的响应数据流
context.setResponseDataStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
}else{
// 设置新的压缩后的响应数据流
context.setResponseBody(JSON.toJSONString(CommonResult.success(requestJson)));
}
} catch (Exception e) {
log.error("请求异常!{}",e);
// 异常数据记录
User user = TokenUtil.getUserByToken();
mjToAiService.handlerException(uuid,e,DateUtils.parseDate(params.get("date")+""),user,context.getRequest());
throw new MyException("请求异常!");
}
log.info("LoggerPostFilter任务处理结束!");
return null;
}

/**
* 收集一些埋点需要的参数
* @param body 接口返回的报文
* @param params 需要存储的集合
*/
private void handlerSomething( String body,ConcurrentHashMap<String,Object> params){
String paramString = body.replaceAll(" ", "").replaceAll(System.getProperty("line.separator"), "");
JSONObject responseBody = JSON.parseObject(paramString);
if (!ObjectUtils.isEmpty(responseBody)){
log.info("【返回参数】{}" , responseBody);
//不确定接口返回的是jobid 还是 id
//如果是正常返回,则覆盖uuid,届时用来对回调接口做关联
if (ObjectUtils.isEmpty(responseBody.get("id"))){
params.put("uuid",responseBody.get("jobid"));
}else{
params.put("uuid",responseBody.get("id"));
}
}
}

/**
* 对请求的入参进行筛选记录
* 统一记录生图埋点数据
* @param context
*/
private void handlerRequest(RequestContext context,ConcurrentHashMap<String,Object> params){
//对MJ相关生图接口做埋点
log.info("对MJ相关生图接口做埋点...");
HttpServletRequest request = context.getRequest();
String requestUrl = request.getRequestURI();
Date dateHandler = DateUtils.parseDate(String.valueOf(params.get("date")));
JSONObject requestMap = JSON.parseObject(JSON.toJSONString(ZuulParameterUtil.getRequestParams(context)));
User user = TokenUtil.getUserByToken();
try {
log.info("接口url:{},参数列表:{}", requestUrl,requestMap);
String clientIp = TokenUtil.getRequest().getRemoteHost();
//1.请求悠船文生图接口
//2.封装AiServerResponseBody
AiServerResponseBody result = mjToAiService.handlerSpecial(
Integer.valueOf(params.get("code")+""),
params.get("returnMj") + "",
params.get("uuid")+"",
dateHandler,user);
log.info("AiServerResponseBody 存入数据{}",result);
//3.异步插入调用记录img_ai_record,日活记录img_dau_record,异步处理
mjToAiService.addRecord(params.get("returnMj") + "",Integer.valueOf(params.get("code")+""),
JSON.toJSONString(requestMap),params.get("uuid")+"",dateHandler,request.getRequestURI(),
params.get("operationResult")+"",user,clientIp,1);
log.info("diffusion uuid:{}, result:{}", params.get("uuid")+"", result);
} catch (Exception e) {
//5.处理异常
log.error("handlerRequest异常:{}",e);
mjToAiService.handlerException(params.get("uuid")+"",e,dateHandler,user,request);
}
}

/**
* 过滤器的类型。可选值有:
* pre - 前置过滤
* route - 路由后过滤
* error - 异常过滤
* post - 远程服务调用后过滤
*/
@Override
public String filterType() {
return "post";
}

/**
* 同种类的过滤器的执行顺序。
* 按照返回值的自然升序执行。
*/
@Override
public int filterOrder() {

return 2;
}
}

  

 

posted @ 2024-11-12 18:04  90的生力军  阅读(2)  评论(0编辑  收藏  举报