热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpringBoot请求复制

为什么要做请求复制?业务场景:微信公众号限制申请个数,现在一个公众号要是dev、int、uat、prod环境公用的,所有当有微信公众号回调场景时,只能在让微信公众号回调到p

  为什么要做请求复制?

  业务场景:微信公众号限制申请个数,现在一个公众号要是 dev、int、uat、prod 环境公用的,所有当有微信公众号回调场景时,只能在让微信公众号回调到 prod 环境,再由prod 环境把请求复制后转到其他环境。

 


1 获取运行环境

1.1 测试代码

  编写一个类,从Spring 上下文中,读取ActiveProfiles

@Component
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicatiOnContext= null;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void destroy() throws Exception {
applicationContext
= null;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextHolder.applicationContext
= applicationContext;
}
/**
* 获取当前环境
*/
public static String getActiveProfile() {
return applicationContext.getEnvironment().getActiveProfiles()[0];
}
}

  启动类

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

 


1.2 设置Active Profiles

  要完成上面的业务需求,我们首先要知道当前的运行环境。提前是我们要设置 Active Profiles 参数,这样才能在 SpringContextHolder 类中的 getActiveProfile() 方法来获取到。


1.2.1 Idea 设置 Active Profiles 方式

 

 

   启动 RunApplication 时,运行日志如下:

2022-05-20 12:19:33.261 INFO 7204 --- [ main] com.**.RunApplication : The following profiles are active: dev

 


1.2.2 JVM启动命令行方式

  在使用JVM命令启动时候,可以添加如下命令来设置Active Profiles:-Dspring.profiles.active=dev

  如果还是在Idea启动是测试,可以在 Environment 设置下里添加JVM命令。

 

 

    启动 RunApplication 时,运行日志如下:

2022-05-20 12:19:33.261 INFO 7204 --- [ main] com.**.RunApplication : The following profiles are active: dev

 


1.2.3 测试用例设置 Active Profiles

  如果使用SpringBootTest方式测试我们的代码,需要设置Active Profiles,可以使用 @ActiveProfiles 注解进行设置。

@SpringBootTest
@ActiveProfiles(value
= "dev")
@RunWith(SpringRunner.
class)
public class SpringServiceTest {
@Test
public void test() {
String activeProfile
= SpringContextHolder.getActiveProfile();
System.out.println(
"activeProfile == " + activeProfile);
}
}

  测试用例运行日志如下:

2022-05-20 17:26:14.773 INFO 936 --- [ main] com.**.test.SpringServiceTest : The following profiles are active: dev
......
activeProfile
== dev

 


2 请求转发的拦截器

2.1 定义拦截器

  我们使用AOP的方式,定义一个Intercepter,需要实现 HandlerInterceptor  接口。用来复制请求的参数,并且转发请求到其他环境的地址去。

@Component
public class WeChatRequestInterceptor implements HandlerInterceptor {
public static ThreadLocal> threadLocal = new ThreadLocal>();
private final static String ACTIVE_PROFILE_PROD = "prod";
private final static String REQUEST_STR = "requestStr";
private final static String DEV_URL = "http:192.168.1.1:8080/dev/";
private final static String INT_URL = "http:192.168.1.1:8080/int/";
private final static String UAT_URL = "http:192.168.1.1:8080/uat/";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 只需要带线上环境生效
if (!ACTIVE_PROFILE_PROD.equals(SpringContextHolder.getActiveProfile())) {
return true;
}
String uri
= request.getRequestURI();
// GET请求
if(request.getMethod().equalsIgnoreCase("GET")){
String parameters
= getParameter(request);
String path
= DEV_URL + uri + "?" + parameters;
// TODO 使用http的方式,转发GET请求,建议单独封装一个Service方法
return true;
}
// POST请求
if(request.getMethod().equalsIgnoreCase("POST")){
// 获取inputStream,解析成字符串,保存到ThreadLocal
String requestStr = IoUtils.toString(request.getInputStream(), request.getCharacterEncoding());
// 这里的TheadLocal需要自己写包装类,我这里只是简单写法
// 因为 POST请求中的inputStream只要读取一次,在Request里就被清除了,所有要存到TheadLocal,让prod环境的请求可以正常处理。
Map localMap = threadLocal.get();
localMap.put(REQUEST_STR, requestStr);
try {
// 复制HttpServletRequest
HttpServletRequest requestWrapper = new ContentCachingRequestWrapper(request);
String path
= DEV_URL + uri;
// TODO 使用http的方式,转发POST请求, 建议单独封装一个Service方法
} catch (Exception e) {
log.error(
"CopyHttpServletRequestInterceptor Exception, ", e);
}
return true;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
/**
* 从HttpServletRequest获取所有Parameter
*/
public static String getParameter(HttpServletRequest request){
List
params = new ArrayList<>();
Enumeration parameterNames
= request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String key
= (String) parameterNames.nextElement();
String value
= request.getParameter(key);
params.add(key
+ "=" + value);
}
if (!CollectionUtils.isEmpty(params)){
return String.join("&", params);
}
return "";
}
}

 


2.2 配置拦截器

  我们需要一个 SpringMVC 配置类,配置起来这个拦截器,声明要拦截的请求地址。

  测试代码如下:

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Autowired
private WeChatRequestInterceptor weChatRequestInterceptor;
/**
* 自定义拦截器
*/
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(weChatRequestInterceptor).addPathPatterns(
"/wechat/callback");
}
}

 



推荐阅读
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 本文介绍了Java中Hashtable的clear()方法,该方法用于清除和移除指定Hashtable中的所有键。通过示例程序演示了clear()方法的使用。 ... [详细]
  • 开发笔记:spring boot项目打成war包部署到服务器的步骤与注意事项
    本文介绍了将spring boot项目打成war包并部署到服务器的步骤与注意事项。通过本文的学习,读者可以了解到如何将spring boot项目打包成war包,并成功地部署到服务器上。 ... [详细]
  • SpringBoot简单日志配置
     在生产环境中,只打印error级别的错误,在测试环境中,可以调成debugapplication.properties文件##默认使用logbacklogging.level.r ... [详细]
  • 2021最新总结网易/腾讯/CVTE/字节面经分享(附答案解析)
    本文分享作者在2021年面试网易、腾讯、CVTE和字节等大型互联网企业的经历和问题,包括稳定性设计、数据库优化、分布式锁的设计等内容。同时提供了大厂最新面试真题笔记,并附带答案解析。 ... [详细]
  • java中的try catch_Java中的trycatchfinally异常处理
    Java中的try-catch-finally异常处理一、异常处理异常(Exception):是在运行发生的不正常情况。原始异常处理:if(条件){处理办法1处理办法 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 从零学Java(10)之方法详解,喷打野你真的没我6!
    本文介绍了从零学Java系列中的第10篇文章,详解了Java中的方法。同时讨论了打野过程中喷打野的影响,以及金色打野刀对经济的增加和线上队友经济的影响。指出喷打野会导致线上经济的消减和影响队伍的团结。 ... [详细]
  • Go语言实现堆排序的详细教程
    本文主要介绍了Go语言实现堆排序的详细教程,包括大根堆的定义和完全二叉树的概念。通过图解和算法描述,详细介绍了堆排序的实现过程。堆排序是一种效率很高的排序算法,时间复杂度为O(nlgn)。阅读本文大约需要15分钟。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 微信公众号:内核小王子关注可了解更多关于数据库,JVM内核相关的知识;如果你有任何疑问也可以加我pigpdong[^1]jvm一行代码是怎么运行的首先,java代码会被编译成字 ... [详细]
author-avatar
mobiledu2502876223
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有