dify定制化前端用iframe嵌入导致部分接口返回值为空,怎么办?

227 阅读5分钟

前言:可看可不看,只是发现问题的过程,已经遇到问题的直接看下面解决办法。最近接手了公司一个需求,内容是将dify定制化前端项目嵌入到公司的内部服务网上面,然后就将项目克隆下来,进行一些简单改造,本地调试都没有问题了就准备部署嵌入了,嵌入后,发现一次对话结束后,无法发送下一次对话,分析以为是对话接口的流式返回有问题导致前端代码没有走到onCompleted这个方法里,导致一系列的问题,就在onMessageEnd里面对响应状态进行修改,再次发版一切看似正常但是使用下来发现每次对话都是新对话,没有上下文,分析发现是会话id没有传,再分析发现是有部分接口返回值为空,正常是返回一个会话列表,会话id是拿到返回值后处理的,然后对比请求头发现cookie没带上...大概就是这么个情况。

原因分析

其实是因为iframe嵌套了一个非同源的地址,浏览器的安全策略导致接口返回在设置响应头的cookie时被拦截了,下一次请求时就没法带上cookie;cookie里面存的是sessionId,而这个sessionId可以粗暴的理解为一次性的用户id,根据这个sessionId来区分用户,没有这个id就无法判断是哪个用户就找不到这个用户的会话列表,所以返回为空数组,但是不影响每次对话,因为每次对话根据的是会话id。

解决思路

这里再分析一下他这个定制化前端项目,他根目录下app里面有个api的文件夹,起初不明白是干嘛用的,定位问题的时候才明白,这相当于接口请求中间件,这个里面才是实际他请求dify后台的地方,你在service里面写的请求实际上是根据路径分配到api里,然后再api文件夹里有对应路径文件夹里的文件来处理对应的请求,通过引入的dify-client这个库;好了,简单说一下这个项目结构,上文的cookie和sessionId也是在这里处理的。 知晓了问题,就好解决了,要么我们换一种方式传sessionId,将生成sessionId的时机放在前端这里,通过参数传给后端,后端修改一下接收的参数的方式就行了;但是我们不想动他后端的代码,所以换个方式传参就不成立了,只能想其他方法了; 浏览器怎么才能不拦截呢,升级为https就可以了;我们在尝试了一系列在设置cookie时加入一些属性无果后,无奈只能搞个证书升级为https;

方案一:升级为https

将我们的项目地址升级为https后确实可以了,有返回值了,但因为证书的问题,这里就不说是什么问题了,又有其他非项目性的问题,导致我们放弃了这个方案;事情又回到了原点;

方案二:尝试用token,但是因为方案三更快速的解决了问题,就没有继续完成,这里提供一种思路

我们就看dify的非定制化前端,在运行的时候,并没有携带cookie啊,用的是token,就想是不是用token也可以达到同样的效果;然后就看他是哪个接口返回的token,我们拿过来也调用一下获取token然后放在请求头里能不能行,也尝试了,但是因为对项目还不是特别熟悉,尝试的时间有点长;前面说过由于接口和api里面路径一一对应的,增加一个接口,还得扩写一下dify-client这个依赖库,api文件夹也得增加一个路径,可能是扩写的请求方法有问题,大概率是路径没对,接口报404了;其实可以跳过api这个中间环节直接请求的,被他原来的代码代入了惯性思维,也是对代码的不熟悉和对dify的不熟悉;就在我正在解决接口没有正确的请求到dify后台的问题时,TL给了个新思路,就是方案三了

方案三:利用user这个参数来替代sessionId的功能

其实我们在看api里面的代码时可以发现他设置cookie是为了后续接口请求拿到这个sessionId然后放在user这个参数里面,所以这个user参数不一定非得是他自己设置的appid和uuid生成的那一串字符串,理论上可以是任何字符串,只要用户和用户之前能区分就行了,我怎么没想到呢,很气;理顺这些就好改了,修改一下api/utils/common.ts这个文件的代码,将他原本放在user的参数改成我们传进来的就行了,前面也说了,我们在页面的请求会进到这里转一下,所以你传个用户名+id替换一下就行了;至此完美解决;

结语:

其实可以更早点去我们部署好的dify项目里去看的看人家非定制化的项目里是怎么传值的,有现成的解决方案,我们没去研究,自己瞎鼓捣半天,最后还是受到启发才能解决;还有方案二可以看定制化方案文档里的api文档,可以直接调用,不通过api转一下也可以的;希望可以帮助同样被困扰的朋友!