响应信息 中 url 部分替换

场景:由阿里云服务器迁移到华为云服务器,或者数据库中数据存在的url为

http://127.0.0.1:8080/miniodb/432e3d13a2ba41d986ac45ce9525b5f6.pdf

但当返回给前端时需要替换中间部分为

http://127.0.0.2:8079/miniodb/432e3d13a2ba41d986ac45ce9525b5f6.pdf

为保证数据库数据保留不变只是返回给前端数据时替换到,实现简单工具;


/**
 * 添加此注解项目启用url替换
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({UrlReplaceResponseBodyAdvice.class})
public @interface EnableUrlReplace {

    /**
     * 前缀
     */
    String[] prefixValue() default {};

    /**
     * 替换目标值
     */
    String targetValue() default "";

    /**
     * 是否 统一替换为 http
     */
    boolean httpProtocol() default false;
}
/**
 * 添加此注解不会替换对应url
 * 
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface ExcludeUrlReplace {}


/**
 * 消息拦截
 * 
 */
@Slf4j
class HttpValueFilter implements ValueFilter {

    private final UrlReplaceConfig urlReplaceConfig;

    public HttpValueFilter(UrlReplaceConfig urlReplaceConfig) {
        this.urlReplaceConfig = urlReplaceConfig;
    }

    /**
     * 是否支持转化
     */
    public boolean support(String json) {
        return urlReplaceConfig.support(json);
    }

    @Override
    public Object process(Object object, String name, Object value) {
        // 为 null 不处理
        if (Objects.isNull(value)) {
            return null;
        }

        // 如果是 string 直接处理
        if (value instanceof String) {
            return replace((String)value);
        }

        // 如果是集合
        if (value instanceof Collection) {
            final List<Object> list = new LinkedList<>();
            for (final Object next : (Collection<?>)value) {
                if (next instanceof String) {
                    list.add(replace((String)next));
                } else {
                    list.add(next);
                }
            }
            return list;
        }

        // 如果是数组
        if (value.getClass().isArray()) {
            final List<Object> list = new LinkedList<>();
            for (Object next : (Object[])value) {
                if (next instanceof String) {
                    list.add(replace((String)next));
                } else {
                    list.add(next);
                }
            }
            return list;
        }

        return value;
    }

    /**
     * 替换url
     * 
     * @param str 传入 str
     * @return 替换后url
     */
    private String replace(String str) {
        // 信息不存在 或 不是 url 不处理
        if (StringUtils.isBlank(str)) {
            return str;
        }
        // 不知道要替换成什么 不处理
        String targetValue = urlReplaceConfig.getTargetValue();
        if (StringUtils.isBlank(targetValue)) {
            return str;
        }

        final URL url;
        try {
            url = new URL(str);
        } catch (MalformedURLException e) {
            return str;
        }
        String[] prefixValue = urlReplaceConfig.getPrefixValue();
        if (Arrays.stream(prefixValue).noneMatch(item -> StringUtils.startsWithIgnoreCase(url.getAuthority(), item))) {
            // url 不存在需要替换的
            return str;
        }
        // 是否替换使用http
        String protocol = urlReplaceConfig.isHttpProtocol() ? "http" : url.getProtocol();
        return String.format("%s://%s%s", protocol, targetValue, url.getPath());
    }
}


/**
 * url 替换配置
 * 
 */
@Getter
@Setter
public class UrlReplaceConfig {

    /**
     * 前缀占用变量
     */
    public static final String PREFIX_VALUE = "enable.url.replace.prefixValue";

    /**
     * 目标占用变量
     */
    public static final String TARGET_VALUE = "enable.url.replace.targetValue";
    /**
     * 是否 统一替换为 http 占用变量
     */

    public static final String HTTP_PROTOCOL = "enable.url.replace.httpProtocol";
    /**
     * 前缀
     */
    private String[] prefixValue;

    /**
     * 替换目标值
     */
    private String targetValue;

    /**
     * 是否 统一替换为 http
     */
    private boolean httpProtocol;

    /**
     * 赋值配置属性
     * <p>
     * 优先从配置文件中获取配置,配置文件中没有取注解上自带的
     * 
     * @param applicationContext 应用上下文
     * @param annotation 启动注解
     */

    public void build(ApplicationContext applicationContext, EnableUrlReplace annotation) {
        Environment env = applicationContext.getEnvironment();

        this.prefixValue = getPrefixValue(env, annotation.prefixValue());
        this.targetValue = StringUtils.defaultIfEmpty(env.getProperty(TARGET_VALUE), annotation.targetValue());
        this.httpProtocol = env.getProperty(HTTP_PROTOCOL, Boolean.class, annotation.httpProtocol());
    }

    /**
     * 获取 前缀
     * 
     * @param env 环境变量
     * @param annotationValue 注解携带内容
     * @return String[]
     */
    private String[] getPrefixValue(Environment env, String[] annotationValue) {
        String propertyValue = env.getProperty(UrlReplaceConfig.PREFIX_VALUE);
        if (Objects.nonNull(propertyValue) && propertyValue.split(",").length != 0) {
            return propertyValue.split(",");
        } else {
            return annotationValue.length != 0 ? annotationValue : new String[0];
        }
    }

    /**
     * 是否支持转化
     */
    public boolean support(String json) {
        if (Objects.isNull(this.prefixValue)) {
            return false;
        }
        return Arrays.stream(this.prefixValue).anyMatch(item -> StringUtils.contains(json, item));
    }
}

/**
 * 响应信息替换url
 * 
 */
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@RestControllerAdvice
public class UrlReplaceResponseBodyAdvice implements ResponseBodyAdvice<Object>, ApplicationContextAware {
    private HttpValueFilter valueFilter;
    @Resource
    private ObjectMapper objectMapper;

    @Override
    public boolean supports(@NonNull MethodParameter returnType,
        @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
        // ResponseEntity 不处理
        if (returnType.getParameterType().isAssignableFrom(ResponseEntity.class)) {
            return false;
        }

        // 流不处理 不处理
        if (returnType.getParameterType().isAssignableFrom(InputStream.class)) {
            return false;
        }

        // 响应对象加了 ExcludeUrlReplace 不处理
        if (Objects.nonNull(AnnotationUtils.findAnnotation(returnType.getParameterType(), ExcludeUrlReplace.class))) {
            return false;
        }

        // Controller 加了 ExcludeUrlReplace 不处理
        if (Objects.nonNull(AnnotationUtils.findAnnotation(returnType.getDeclaringClass(), ExcludeUrlReplace.class))) {
            return false;
        }

        // Controller#method 加了 ExcludeUrlReplace 不处理
        if (Objects.nonNull(returnType.getMethod())
            && Objects.nonNull(AnnotationUtils.findAnnotation(returnType.getMethod(), ExcludeUrlReplace.class))) {
            return false;
        }

        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, @NonNull MethodParameter returnType,
        @NonNull MediaType selectedContentType, @NonNull Class<? extends HttpMessageConverter<?>> selectedConverterType,
        @NonNull ServerHttpRequest request, @NonNull ServerHttpResponse response) {

        // 如果返回的不是 json 那么不处理
        if (!Objects.equals(MediaType.APPLICATION_JSON, selectedContentType)) {
            return body;
        }

        try {
            // 使用 ObjectMapper 序列化一下,保证返回的格式不会错乱
            final String json = objectMapper.writeValueAsString(body);
            if (!valueFilter.support(json)) {
                return body;
            }

            // 此时,原有的对象已经丢失,全部变成了 JSONObject or JSONArray
            return JSON.parse(JSON.toJSONString(JSON.parse(json), valueFilter));
        } catch (Exception e) {
            log.warn("替换 AliOSS Bucket 失败", e);
        }

        return body;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // 获取 应用上下文 中 使用EnableAliOSSBucketResponseBodyAdvice 的 bean
        Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(EnableUrlReplace.class);
        // 若存在应当只有一个 位于 启动类上
        Set<Map.Entry<String, Object>> entitySet = serviceBeanMap.entrySet();
        if (entitySet.size() == 1) {
            Class<?> aClass = entitySet.iterator().next().getValue().getClass();
            EnableUrlReplace annotation = AnnotationUtils.findAnnotation(aClass, EnableUrlReplace.class);
            UrlReplaceConfig urlReplaceConfig = new UrlReplaceConfig();
            urlReplaceConfig.build(applicationContext, Objects.requireNonNull(annotation));
            valueFilter = new HttpValueFilter(urlReplaceConfig);
        }
    }

}

使用方法:

在项目启动类上加上 @EnableUrlReplace 注解

@EnableUrlReplace
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

在对应配置文件增加配置

enable:
  url:
    replace:
      prefixValue: "127.0.0.1:8080"
      targetValue: "127.0.0.2:8079"

会将prefixValue中存在的替换为 targetValue,prefixValue如果是多个用逗号分割,如果没有这个配置不会替换对应url,如果存在方法或者某个接口不需要替换可以加上@ExcludeUrlReplace排除掉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值