场景:由阿里云服务器迁移到华为云服务器,或者数据库中数据存在的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排除掉