第三方接口回调转发到不同环境-创新互联
背景:完成完整的业务,需要第三方异步回调系统的接口。从而更新业务状态。但是第三方系统经常只能配置一个回调接口。但是我们系统有4个环境。常常只能在一个环境测试,切换环境测试需要去第三方修改回调接口。
网站建设哪家好,找成都创新互联!专注于网页设计、网站建设、微信开发、成都微信小程序、集团企业网站建设等服务项目。为回馈新老客户创新互联还提供了西夏免费建站欢迎大家使用!目标:接口回调回来后,允许接口转发到其他环境
实现思路:由于回调接口中是没有参数能标识发到哪个环境,因此无法在nginx层面解决。就在具体服务上做:
接收请求,找到需要转发到哪些环境
封装请求,将回调请求转发过去(header带上标识:x-forward-from)
如果请求带有x-forward-from,则不转发
同时本服务也接收该请求并处理
这些逻辑都是和接口逻辑不相干的,因此考虑切面等方式处理,最后采用filter处理。逻辑在filter中处理
以下是遇到的相关问题:项目采取SpringMVC
Q:WebApplicationContext获取HttpClient的bean失败
call其他环境用封装的HttpClient的bean,它是一个带线程池的,filter通过在web.xml进行配置生效。如下:
NotifyMessageForwardFilter com.lenovo.ofp.payment.front.webapp.filter.NotifyMessageForwardFilter NotifyMessageForwardFilter /notify/*
这种方式,可以看作是new NotifyMessageForwardFilter()。无法通过@Auwired 注入bean,会报错。当然这个报错具体原因是因为filter调用时,bean对象还没初始化好:web.xml中各个标签初始化的顺序如下:contetxt-param ->listener ->filter ->servlet
用来加载你配置的文件信息配置你的监听服务过滤器配置你单独的一些操作容器初始化
加载顺序为:context-param ->listener ->filter ->servlet 加载的顺序不受在web.xml中配置的位置影响
springmvc.xml中定义component-scan;而springmvc.xml在servlet中加载;所以filter中找不到servlet才生成的bean
其次是WebApplicationContext获取的applicationContext,它id是:org.springframework.web.context.WebApplicationContext:/payment-front-webapp,通过BeanDefinitionNames()这个方法发现,没有HttpClient的bean,连Controller的bean都没有。因此才认为,至少还有一个其他的ApplicationContext
而servlet的applicationContext的id是:org.springframework.web.context.WebApplicationContext:/payment-front-webapp/dispatcher
发现了吧,ApplicationContext不是一个,所以呀,我们要用下面那个。
A:解决方法:通过上面的分析,我们也知道ApplicationContext除了WebApplicationUtils获取的那个外,还存在至少一个。所以用ApplicationContextAware来获取。毕竟这个接口是在服务启动完成后执行的内容。获取的Context更为完整。
@Component
public class ApplicationContextUtils implements ApplicationContextAware {
public static ApplicationContext APPLICATION_CONTEXT = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("init ApplicationContextUtils success");
ApplicationContextUtils.APPLICATION_CONTEXT = applicationContext;
}
}
Q1:org.apache.http.ProtocolException: Content-Length header already present
A:content-length是根据内容的长度动态计算的。因此在请求转发的时候,要删掉
Q2:requestBody无法被重复读取
请求转发到不同环境,这意味着我们需要封装http请求,需要读取body。而body读取一次后就会报错。
A:body无法被重复读取原因是body内容放在InputStream中,这个流是只能读一次的,根据HttpServletRequestWrapper包装一下就行了
public class HttpMultiReadServletRequestWrapper extends HttpServletRequestWrapper {
BufferedReader bufferedReader = null;
String requestBody = "";
public HttpMultiReadServletRequestWrapper(HttpServletRequest request) {
super(request);
// 用原始的request获取一次body,然后缓存起来
this.requestBody = HttpRequestUtils.getRequestBody(request);
}
@Override
public ServletInputStream getInputStream() throws IOException {
String characterEncoding = getCharacterEncoding();
characterEncoding = StringUtils.isBlank(characterEncoding) ? "UTF-8" : characterEncoding;
return new DelegatingServletInputStream(
new ByteArrayInputStream(requestBody.getBytes(characterEncoding)));
}
@Override
public BufferedReader getReader() throws IOException {
if (bufferedReader == null) {
// 需要搭配StringReader才能反复读。用inputStream只能读一次
bufferedReader = new BufferedReader(new StringReader(requestBody));
bufferedReader.mark(requestBody.length() + 1);
} else {
// 重置之后,bufferedReader就又可以读了
bufferedReader.reset();
}
return bufferedReader;
}
public String getRequestBody() {
return this.requestBody;
}
}
HttpRequestUtils的getRequestBody内容如下
public static String getRequestBody(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
try {
BufferedReader br = request.getReader();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
} catch (Exception e) {
log.error("Read notify request body", e);
}
return sb.toString();
}
Q3:@Autowired request是代理类,无法加载为了解决requestBody而生成RequestWrapper
A:其实就是一个实现逻辑出错。我们生成RequestWrapper后,应当在filterChain里面传入RequestWrapper而不是原来的request。其次是:RequestContextHolder,这个是在filter之前就设置了Request,所以有必要,需要更新里面的Request
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
网站标题:第三方接口回调转发到不同环境-创新互联
标题URL:http://scjbc.cn/article/depsdc.html