Dify AI 聊天接口后端代理

实现基于 Spring Boot 的 Dify AI 聊天接口代理,支持流式响应,并排查接口调用异常问题

一、Dify 代理接口实现原理

1. 核心功能

通过后端代理转发前端聊天请求至 Dify AI 平台(https://api.dify.ai/v1/chat-messages),实现用户鉴权、参数透传、流式响应返回,核心流程如下:

  1. 鉴权层:从请求头获取accessToken,调用UserFeignClient校验用户身份,未登录则返回标准化超时响应;
  2. 参数解析层:解析前端传入的query(聊天问题)、conversationId(会话 ID)等参数,校验必填项;
  3. 请求构建层:封装 Dify 平台要求的请求参数(含inputsresponse_mode=streaminguser等),添加 Dify API Token 认证头;
  4. 流式调用层:基于 Spring WebFlux 的WebClient调用 Dify 流式接口,接收 SSE(Server-Sent Events)流式响应;
  5. 响应返回层:将 Dify 返回的流式数据直接透传给前端,异常时封装标准化错误信息(遵循 SSE 格式)。

2. 技术选型

  • 核心框架:Spring Boot + Spring WebFlux(支持异步流式响应);
  • HTTP 客户端:WebClient(替代传统 HttpClient,适配流式响应);
  • 服务注册 / 发现:Nacos(服务间调用及路由转发);
  • 接口规范:SSE(text/event-stream)流式响应,兼容前端实时接收聊天回复。
package cn.com.xxx.mainservice.rest; import cn.com.xxx.common.feign.user.UserFeignClient; import cn.com.xxa.common.model.database.User; import cn.com.xxx.common.util.BaseUtils; import cn.com.xxxa.common.util.StaticValue; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.HashMap; import java.util.Map; @RestController public class ChatRest { @Qualifier("cn.com.xxx.common.feign.user.UserFeignClient") @Autowired private UserFeignClient userFeignClient; private final String DIFY_API_URL = "http://192.xxx.x.193/v1/chat-messages"; private final String DIFY_API_TOKEN = "app-kam4xxxxxxx50klwoJH"; // 替换为实际token /** * 构建WebClient(Spring WebFlux原生工具) */ private final WebClient webClient = WebClient.builder() .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(1024 * 1024)) // 调整缓冲区大小 .build(); /** * Dify聊天Rest接口(流式响应) * 返回类型为Flux<String>,直接返回SSE流式数据 */ @RequestMapping( value = "/rest/v1/chat/difyProxy", method = RequestMethod.POST, produces = MediaType.TEXT_EVENT_STREAM_VALUE // 声明SSE响应类型 ) public Flux<String> difyChatProxy(@RequestBody String jsonBody, HttpServletRequest request) { // ========== 解析前端聊天参数 & 鉴权 ========== String accessToken = request.getHeader(StaticValue.HEADER_ACCESSTOKEN); User loginUser = userFeignClient.getUserByAccessToken(accessToken); // 鉴权失败:返回流式错误信息 if (loginUser == null) { String errorData = String.format("data: %s\n\n", JSON.toJSONString(BaseUtils.loginTimeoutReturn())); return Flux.just(errorData); } String loginUserName = loginUser.getName(); JSONObject parmJson = JSON.parseObject(jsonBody); String query = parmJson.getString("query"); String conversationId = parmJson.getString("conversationId"); // 查询为空:返回流式错误信息 if (BaseUtils.isBlankStr(query)) { String errorData = String.format("data: %s\n\n", JSON.toJSONString(BaseUtils.httpErrReturn())); return Flux.just(errorData); } // ========== 构建Dify请求参数 ========== Map<String, Object> difyParam = new HashMap<>(); difyParam.put("inputs", new HashMap<>()); // 固定空inputs difyParam.put("query", query); // 前端传入的问题 difyParam.put("response_mode", "streaming"); // 流式响应模式 difyParam.put("user", loginUserName); // 用户名 difyParam.put("auto_generate_name", true); // 固定参数 // 可选参数:conversationId非空则添加 if (!BaseUtils.isBlankStr(conversationId)) { difyParam.put("conversation_id", conversationId); } // ========== 调用Dify流式接口并返回 ========== return webClient.post() .uri(DIFY_API_URL) // 设置Dify认证头 .header(HttpHeaders.AUTHORIZATION, "Bearer " + DIFY_API_TOKEN) .contentType(MediaType.APPLICATION_JSON) .bodyValue(JSON.toJSONString(difyParam)) .retrieve() // 处理非2xx响应 .onStatus(status -> !status.is2xxSuccessful(), clientResponse -> { return clientResponse.bodyToMono(String.class) .flatMap(errorMsg -> { String errorData = String.format("data: %s\n\n", JSON.toJSONString(Map.of( "error", "Dify接口调用失败:" + clientResponse.statusCode() + ",详情:" + errorMsg ))); return Mono.error(new RuntimeException(errorData)); }); }) // 解析流式响应为字符串 .bodyToFlux(String.class) // 日志打印 .doOnNext(line -> { if (line.startsWith("data: ")) { System.out.println("Dify流式数据:" + line); } }) // 异常处理:返回标准化错误信息 .onErrorResume(e -> { String errorData = String.format("data: %s\n\n", JSON.toJSONString(Map.of( "error", "服务端代理异常:" + e.getMessage() ))); return Flux.just(errorData); }) // 响应完成日志 .doFinally(signalType -> { System.out.println("Dify流式响应接收完毕,信号类型:" + signalType); }) // 设置响应头(关键:声明SSE格式) .contextWrite(ctx -> { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.TEXT_EVENT_STREAM); headers.set("Cache-Control", "no-cache"); headers.set("Connection", "keep-alive"); return ctx.put("responseHeaders", headers); }); } }

一开始把功能写在了微服务各个层(rest、controller、service、dto、feign等),但简单业务(没有涉及数据库操作)没必要写得那么复杂,具体业务具体分析吧。

二、问题排查记录

问题 1:编译报错 - 程序包 org.springframework.web.reactive.function.client 不存在

现象

编译ChatRest.java时提示程序包org.springframework.web.reactive.function.client不存在,无法识别WebClient类。

根因

项目依赖配置异常:

  1. 重复引入spring-webflux依赖且未指定版本;
  2. spring-boot-starter-webflux依赖冗余(已包含spring-webflux核心包,无需单独引入)。
解决方案
  1. 移除重复的spring-webflux依赖,仅保留spring-boot-starter-webflux核心依赖;
  2. 刷新 Maven 依赖并重新编译项目,确保WebClient相关包正常引入。

问题 2:运行报错 - 找不到主类cn.com.xxx.mainservice.MainServiceMainApplication

现象

启动项目时提示 “找不到或无法加载主类”,无法正常启动服务。

根因
  1. 主类包名与文件目录结构未严格匹配(Java 编译规范要求);
  2. 编译产物缺失(target/classes下无主类.class文件);
  3. IDE 缓存 / 编译缓存未清理,导致加载旧配置。
解决方案
  1. 核对主类包名(cn.com.geohwa.mainservice)与文件路径(src/main/java/cn/com/geohwa/mainservice/)一致性;
  2. 执行mvn clean compile清理并重新编译,生成完整的.class文件;
  3. 清理 IDE 缓存(IDEA:Invalidate Caches / Restart),重新导入项目。

问题 3:接口调用报错 - 404 Not Found

现象

前端 / ApiPost 请求/api/v1/chat/difyProxy返回 404,提示 “请求的资源不存在”。

根因
  1. 接口路径不匹配:代码中接口注解为/rest/v1/chat/difyProxy,但请求路径为/api/v1/chat/difyProxy(缺失rest前缀);
  2. 网关路由配置缺失:初始仅配置/api/v1/user/**路由,未添加/api/v1/chat/**路由规则;
临时解决方案

补充 Gateway 路由配置,通过RewritePath/api/v1/chat/**重写为/rest/v1/chat/**,转发至main-service服务。

问题 4:路由转发异常 - 请求被转发至同事的服务实例

现象

网关路由配置生效后,请求仍未正常响应,排查发现请求被转发至同网段(192.xxx.x.202:8848)另一同事的main-service实例。

根因

Nacos 注册中心中,多实例使用相同服务名(main-service)且部署在同一 Nacos 节点(192.xxx.x.202:8848),网关负载均衡随机转发至非目标实例。

解决方案
  1. 修改本地服务的 Nacos 服务节点为201

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1004450.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

意义行为原生论:悟空来路与关山——全领域非专业vs全领域负责制

意义行为原生论:悟空来路与关山——全领域非专业vs全领域负责制 引言:智能时代的认知困境与负责制危机 在专业分工日益精细的今天,我们面临一个深刻的悖论:专业知识不断深化,解决复杂系统性问题的能力却在下降;责…

Lenia完整指南:探索连续细胞自动机的数学生命世界

Lenia完整指南&#xff1a;探索连续细胞自动机的数学生命世界 【免费下载链接】Lenia Lenia - Mathematical Life Forms 项目地址: https://gitcode.com/gh_mirrors/le/Lenia Lenia&#xff08;莱尼亚&#xff09;是一个革命性的连续细胞自动机系统&#xff0c;它打破了…

基于微信小程序的健身运动社区的设计与实现(源码+论文+部署+安装)

感兴趣的可以先收藏起来&#xff0c;还有在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;我会一一回复&#xff0c;希望可以帮到大家。一、系统背景在 “健康中国 2030” 战略推进与移动互联网深度渗透的背景下&#xff0c;国民健身需求持续增…

day5 Java基础7

Java基础7 逻辑运算与 两个变量都为true,结果为true 或 两个变量有一个为true,结果为true 非 真则为假,假则为真 短路运算位运算(效率极高) &,|,^,~,>>,<<,>>> a = 0011 1100 b =…

为何机器人开始学叠衣服?六维力传感器赋予的能力正推动落地场景变革

前两天翻看行业动态&#xff0c;一个有趣的变化引起了我的注意&#xff1a;各大机器人厂商的宣传重点&#xff0c;正悄悄从那些让人眼花缭乱的舞蹈表演&#xff0c;转向了叠衣服、收拾桌子这类看似平淡的家务活。这个转变背后&#xff0c;我认为不仅仅是营销策略的调整&#xf…

《Nature Communications》最新研究:高效率差频产生器件赋能未来光通信与波长转换

前沿摘要近日&#xff0c;国际顶尖学术期刊《Nature Communications》在线发表了浙江大学光电科学与工程学院团队的最新研究成果(https://doi.org/10.1038/s41467-025-65953-z)。研究提出并实现了一种基于自适应极化薄膜周期性极化铌酸锂波导的高效率差频产生器件&#xff0c;在…

数字孪生软件开发公司

寻找一家满意的数字孪生&#xff08;Digital Twin&#xff09;开发公司&#xff0c;是实现复杂系统数字化、优化运营和预测分析的关键。数字孪生项目往往涉及跨领域的技术集成&#xff0c;包括物联网&#xff08;IoT&#xff09;、大数据、云计算、三维建模和高级分析&#xff…

MHT-FE520 光纤组合导航系统深度解析:多源融合导航的协议适配与工程实践

在潜航器、特种车辆、中型无人机等高端载体导航场景中&#xff0c;单一传感器易受环境干扰&#xff0c;难以持续保障导航精度与连续性。苏州邈航 MHT-FE520 光纤组合导航系统&#xff0c;以中精度光纤惯性测量为核心&#xff0c;通过多源设备兼容、标准化协议设计及强环境适应性…

终极指南:快速掌握eventpp事件处理库的8种集成方法

终极指南&#xff1a;快速掌握eventpp事件处理库的8种集成方法 【免费下载链接】eventpp eventpp - 一个为C提供的事件分派器和回调列表库。 项目地址: https://gitcode.com/gh_mirrors/ev/eventpp eventpp是一个功能强大的C事件分派器和回调列表库&#xff0c;作为纯头…

Webgl开发数字孪生项目的流程

Webgl开发数字孪生项目的流程是一个复杂且高度专业的跨学科过程&#xff0c;它结合了三维图形技术、数据集成、物联网&#xff08;IoT&#xff09;和云计算。以下是详细的六个主要阶段及其关键步骤。阶段一&#xff1a;需求定义与架构设计这个阶段是项目的基础&#xff0c;决定…

SideFX Labs游戏开发工具包:从新手到专家的终极指南

SideFX Labs游戏开发工具包&#xff1a;从新手到专家的终极指南 【免费下载链接】GameDevelopmentToolset A series of Houdini shelf tools that are geared towards game developers! 项目地址: https://gitcode.com/gh_mirrors/ga/GameDevelopmentToolset 还在为游戏…

区块链 Web3 项目开发公司

寻找一家满意的区块链 Web3 项目开发公司&#xff0c;是决定项目成败的关键一步。Web3 开发涉及去中心化、智能合约安全、代币经济模型等高度专业化的领域&#xff0c;与传统 Web2 开发有显著区别。 以下是找到满意开发公司的完整流程和关键考量因素&#xff1a; 第一步&…

【每日一题】PCIe答疑 - 接大量 GPU 时主板不认设备或无法启动和MMIO的可能关系?

今天早上的文章《PCIe协议经常谈到的Memory-Mapped I/O究竟是啥&#xff1f;》发了以后&#xff0c;有工程师留言问&#xff1a; 接大量 GPU 时主板不认设备或无法启动&#xff0c;是不是 MMIO 不足&#xff1f;除了换主板还有办法吗&#xff1f; 先说结论&#xff1a;YES&am…

java基础-Java Queue 接口

Queue 是 Java 集合框架中的一个重要接口&#xff0c;位于 java.util 包中&#xff0c;它表示一个先进先出&#xff08;FIFO&#xff09;的队列数据结构。Queue 接口继承了 Collection 接口&#xff0c;并定义了一组专门用于队列操作的方法。Queue 接口的主要特点先进先出(FIFO…

基于微信小程序的民宿预订系统的设计与实现(源码+论文+部署+安装)

感兴趣的可以先收藏起来&#xff0c;还有在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;我会一一回复&#xff0c;希望可以帮到大家。 一、系统背景 行业发展趋势 旅游业蓬勃发展推动民宿行业快速扩张&#xff0c;民宿凭借个性化体验、贴…

品牌营销的“防AI雷区”:MyDetector如何让你的文案和图片双保险

品牌营销的“防AI雷区”&#xff1a;MyDetector 如何让你的文案和图片双保险&#xff08;完整版 1680 字&#xff09; AI 时代&#xff0c;品牌最怕的不是写不出来&#xff0c;而是“写得太像 AI” 在如今的营销圈&#xff0c;AI 已经成了标配&#xff1a; ChatGPT 30 秒出一篇…

基于Java+ vue健身房管理系统(源码+数据库+文档)

目录 基于springboot vue健身房管理系统 一、前言 二、系统功能演示 详细视频演示 三、技术选型 四、其他项目参考 五、代码参考 六、测试参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 基于springboot vue健身房管理系统 一、前言 博主介绍&#xff…

对比传统分库分表:Sharding-JDBC效率提升300%的秘密

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 请生成一份详细的性能对比报告&#xff0c;比较&#xff1a;1. 原生JDBC连接多个数据源&#xff1b;2. 手动分库分表方案&#xff1b;3. Sharding-JDBC方案。要求包含&#xff1a;1…

如何快速掌握正点原子串口调试助手:嵌入式开发的终极指南

如何快速掌握正点原子串口调试助手&#xff1a;嵌入式开发的终极指南 【免费下载链接】正点原子串口调试助手XCOMV2.6下载 正点原子串口调试助手 XCOM V2.6 下载 项目地址: https://gitcode.com/open-source-toolkit/35260 正点原子串口调试助手 XCOM V2.6 是一款专为嵌…

富有的哈佛人 —— 储蓄:财富积累的第一块基石

富有的哈佛人 —— 储蓄&#xff1a;财富积累的第一块基石在哈佛大学的经济学课堂上&#xff0c;有一条被称为 "财富密码" 的核心原则&#xff1a;储蓄不是收入减去支出的剩余&#xff0c;而是支出必须低于收入减去储蓄的结果。这条看似简单的 "哈佛教条"&…