工作流已经能提取任务、输出 JSON,下一步通常就是调用接口:把结果交给其他服务,再根据响应决定后续流程。
实际配置时,几个小问题就可能让流程卡住:请求体被重复编码,接口返回 HTML 却被当作 JSON 解析,或者 503 响应在进入条件判断之前就触发了节点失败。
本文用一个无鉴权的回显接口,完整走通 JSON 请求体构造 → HTTP 调用 → 状态码判断 → 响应解析 → 统一输出。示例数据和代码都在正文中,可独立完成练习。
1. 环境和本次目标
| 项目 | 本文使用的环境 |
|---|---|
| Dify | 社区版 1.17.1,自部署 |
| 部署方式 | Docker Compose |
| 操作系统 | macOS / Windows,以下配置在浏览器中完成 |
| 应用类型 | Workflow,由用户输入启动 |
| 演示接口 | https://httpbin.org/anything |
| 是否需要模型和 API Key | 本篇不使用 LLM,演示接口无需鉴权 |
资料核对日期:2026-10-21。本文涉及的状态码、重试和异常处理行为按 Dify 1.17.1 说明,其他版本应结合实际节点行为验证。
httpbin 是 HTTP 请求与响应演示服务,/anything 会回显请求内容。我们只发送下文的虚构数据,核对 POST 方法和服务端解析到的 JSON 对象;回显成功不代表已经创建任务或完成业务写入 。httpbin 官方接口定义
2. 先看工作流结构
创建 Workflow,命名为"JSON 接口调用演示",按下面的结构编排:
text
用户输入 → 准备请求体 → HTTP 回显 → 状态码判断
├─ 2xx → 校验回显 ─┐
└─ 其他 → 状态提示 ─┤
↓
结果汇合
↓
输出
其中"准备请求体"和"校验回显"使用 Code 节点,"状态提示"使用 Template,"结果汇合"使用 Variable Aggregator。
本篇不再调用模型生成数据,便于先把接口链路单独调通。以后接回结构化提取流程时,可以直接使用已经校验过的对象。
3. 输入示例 JSON,构造请求体
3.1 用户输入字段
在用户输入节点(部分界面称 Start / 开始)中添加:
| 字段 | 设置 |
|---|---|
| 变量名称 | payload_json |
| 展示名称 | 待发送 JSON |
| 类型 | Paragraph / 段落 |
| 必填 | 是 |
试运行时使用以下完整示例:
json
{
"summary": "团队确认更新账号申请说明,并整理常见问题清单。",
"action_items": [
{
"task": "更新账号申请说明,补充入口与字段",
"owner": "王宁",
"deadline": "2026-10-23",
"evidence": "王宁负责在 2026-10-23 前更新账号申请说明。"
},
{
"task": "整理账号申请常见问题清单",
"owner": "",
"deadline": "",
"evidence": "团队决定整理常见问题清单,但负责人和完成时间未明确。"
}
],
"pending_notes": ["常见问题清单的负责人和完成时间待确认。"]
}
3.2 Code 节点:准备请求体
新增 Python Code 节点,命名为"准备请求体"。
- 输入
payload_json:绑定"用户输入 → payload_json"。 - 输出
request_body:String。 - 删除不使用的默认输入、输出字段。
python
import json
def main(payload_json: str) -> dict:
if not isinstance(payload_json, str) or not payload_json.strip():
raise ValueError("请提供 JSON 对象文本")
try:
payload = json.loads(payload_json)
except json.JSONDecodeError:
raise ValueError("输入不是合法 JSON,请检查引号、逗号和括号") from None
if not isinstance(payload, dict):
raise ValueError("请求内容必须是 JSON 对象")
if not isinstance(payload.get("summary"), str):
raise ValueError("summary 必须是字符串")
if not isinstance(payload.get("action_items"), list):
raise ValueError("action_items 必须是数组")
if not isinstance(payload.get("pending_notes"), list):
raise ValueError("pending_notes 必须是数组")
return {
"request_body": json.dumps(payload, ensure_ascii=False, allow_nan=False)
}
json.loads 把输入文本解析为对象,json.dumps 再把对象序列化成可发送的 JSON 文本。中文、引号、换行等转义由序列化器处理,不需要手工拼接。
这段代码只检查本篇演示所需的基本结构。如果前面已经有业务字段校验,应继续保留,不要用这里的基础检查替代完整校验。
**接入已有对象时:**将 Code 的输入改为 data,绑定上游已校验的对象,序列化时使用 json.dumps(data, ensure_ascii=False, allow_nan=False) 即可,不需要再次 json.loads(data)。
4. HTTP 请求节点怎么填
添加 HTTP Request,命名为"HTTP 回显",连接"准备请求体"。配置如下:
| 配置项 | 本文取值 |
|---|---|
| Method | POST |
| URL | https://httpbin.org/anything |
| Authorization | No Auth |
| Header | Content-Type: application/json |
| Body 类型 | Raw Text / raw |
| Body 内容 | 仅插入"准备请求体 → request_body"变量 |
| SSL Verification | 保持开启 |
| Retry | 关闭 |
| Error Handling | None / 不处理异常 |
HTTP 节点可配置 URL、请求头、请求体和连接、读取、写入超时。官方 HTTP 节点说明
4.1 请求体只插入一次,不再加引号
打开 Raw 编辑区,通过变量选择器插入 request_body。整个请求体就是这个变量,外面不要再包一对引号,也不要加一个额外的 payload 对象。
Raw 模式会把变量替换后的文本作为请求内容发送;手动填写的 Content-Type: application/json 指明内容类型。官方请求体实现
例如,应该发送的是对象:
json
{"summary":"示例","action_items":[],"pending_notes":[]}
如果再次对整段文本编码,发送的就可能变成一个 JSON 字符串:
json
"{\"summary\":\"示例\",\"action_items\":[],\"pending_notes\":[]}"
这两种内容都可能符合 JSON 语法,但类型不同。后面的回显检查会识别这种错误。
4.2 为什么本文要求关闭 Retry
这是本篇最需要注意的版本细节。
Dify 1.17.1 的 HTTP 节点默认配置开启重试。该版本运行时在启用重试或异常处理策略时,会把非 2xx 响应按失败处理,未必进入下游的普通状态码判断。1.17.1 默认配置、官方运行时状态处理
本篇为了直接判断真实响应,采用固定模式:
Retry 关闭 + Error Handling 为 None → 收到的非 2xx 状态可沿普通路径交给条件节点判断。
网络断开、域名解析失败、连接或读取超时,则仍会导致节点失败;它们不等于"收到一个非 2xx 响应"。
5. 先判断状态,再解析正文
HTTP 节点的常用输出包括:
| 变量 | 类型 | 用途 |
|---|---|---|
| status_code | Number | 判断 HTTP 状态 |
| body | String | 响应正文,确认内容格式后再解析 |
| headers | Object | 响应头 |
即便响应内容是 JSON,body 在这里仍是字符串,不能直接把它当成已经解析的业务对象。
在 HTTP 后添加 If/Else,命名为"状态码判断",用 AND 组合两个条件:
- "HTTP 回显 → status_code"大于等于数值
200。 - "HTTP 回显 → status_code"小于数值
300。
IF 进入成功分支,ELSE 进入状态提示分支。错误响应可能是空正文或 HTML,所以不要在分支之前无条件对 body 执行 JSON 解析。
6. 成功分支:解析并核对回显
IF 分支添加 Python Code,命名为"校验回显"。
输入绑定:
| 输入名称 | 来源 |
|---|---|
| status_code | HTTP 回显 → status_code |
| body | HTTP 回显 → body |
| request_body | 准备请求体 → request_body |
只声明两个输出:report: String、echoed: Object。
python
import json
def main(status_code: int, body: str, request_body: str) -> dict:
if not 200 <= status_code < 300:
raise ValueError("本节点只处理 2xx 成功分支")
try:
response = json.loads(body)
expected = json.loads(request_body)
except (json.JSONDecodeError, TypeError):
raise ValueError("响应正文或请求体不是可解析的 JSON") from None
if not isinstance(response, dict) or response.get("method") != "POST":
raise ValueError("未收到预期的 POST 回显响应")
echoed = response.get("json")
if not isinstance(echoed, dict):
raise ValueError("回显 json 不是对象,请检查请求体是否被重复编码")
options = {"ensure_ascii": False, "sort_keys": True, "allow_nan": False}
if json.dumps(echoed, **options) != json.dumps(expected, **options):
raise ValueError("回显对象与发送的对象不一致")
report = "\n".join([
"# 接口回显核对",
"",
f"- HTTP 状态码:{status_code}",
"- 请求方法:POST",
"- 回显对象与请求对象:一致",
"- 本次仅验证数据传输与回显,没有创建业务任务。",
"",
"## 回显数据",
"",
"```json",
json.dumps(echoed, ensure_ascii=False, indent=2, allow_nan=False),
"```",
])
return {"report": report, "echoed": echoed}
httpbin 的回显对象中,method 表示收到的请求方法,json 表示服务端解析请求正文后的值。代码会检查方法、对象类型,以及回显对象与请求对象是否一致。httpbin 端点实现
核对成功时,report 包含可读说明,echoed 保留对象供后续使用。这里的成功条件专门针对本篇回显接口;换成真实业务 API 后,应按该 API 的响应结构判断。
7. 非 2xx 分支:给出明确状态提示
ELSE 分支添加 Template,命名为"状态提示"。新增本地输入变量 status_code,绑定"HTTP 回显 → status_code",模板填写:
jinja2
# 接口调用状态
收到 HTTP {{ status_code }},本次未执行成功回显校验。
请查看 HTTP 节点的响应正文与响应头,并按接口文档判断错误原因。
当前结果不能用来确认业务操作已经成功。
不要把所有错误都写成"服务不可用",也不要仅凭一个错误状态就断言真实业务一定没有写入。不同接口的错误含义和事务行为,应以其文档为准。
汇合并输出
添加 Variable Aggregator,命名为"结果汇合",保持 Aggregation Group 关闭,选择两个字符串候选:
- 校验回显 → report。
- 状态提示 → output。
最后连接一个 Output,新增 report,绑定"结果汇合 → output"。互斥分支每次只运行其中一条,聚合器提供统一输出。官方变量聚合器说明
8. 验证成功、状态错误和超时
8.1 正常回显
使用 /anything 和正文示例完整运行,预期检查:
- 请求方法为 POST。
- Content-Type 是 application/json。
- 状态判断进入 2xx 分支。
- 回显 json 是对象,且与发送对象一致。
- 报告明确表述"传输与回显通过",没有声称创建业务任务。
8.2 模拟 503 响应
暂时把 URL 改为:
text
https://httpbin.org/status/503
保持 Retry 关闭、Error Handling 为 None。若接口按定义返回 503,预期走 ELSE,显示真实状态码,不进入成功正文解析。完成后将 URL 改回 /anything。httpbin 状态码接口定义
8.3 区分超时和 HTTP 错误
连接、读取、写入超时分别对应不同阶段,可在节点高级设置中配置。练习时可按界面单位先设置连接 5 秒、读取 20 秒、写入 20 秒;这些是演示取值,应根据实际接口调整。
若需观察读取超时,可在测试环境尝试 httpbin 的 /delay/5,并把读取超时临时改为 1 秒。公共服务、缓存和代理会影响表现,应以节点实际错误为准,不能把它当作精确计时基准。
本篇模式下,超时会导致流程失败,而不是得到一个可判断的真实响应状态。不要人为填 200、0 或其他数字,把它包装成正常 HTTP 结果。
8.4 如果想让网络错误也显示友好提示
可以另行启用 HTTP 的异常分支,将它连接到失败提示节点。但要同时调整原来的预期:在 1.17.1 中,启用异常处理后,非 2xx 响应也会进入失败路径,不能继续期待普通 ELSE 分支拿到它们。
因此要先选定处理模式,再配置连线:
| 模式 | 主要用途 |
|---|---|
| 本文模式:重试关闭、异常处理 None | 直接拿真实状态码,判断收到的响应 |
| 异常分支模式 | 将非 2xx 与网络类失败统一接入失败路径 |
不要给失败路径配置一个虚假的成功状态作为默认值。
9. 接入真实 API 前,要改哪些地方
| 项目 | 需要按真实接口调整的内容 |
|---|---|
| URL 和方法 | 接口地址,以及 GET、POST 等实际约定 |
| 请求体 | 字段名称、必填项、类型和业务校验 |
| 认证 | 将密钥放入 Secret 类型环境变量,按文档配置认证 |
| 响应解析 | 修改成功分支,不再固定读取 httpbin 的 method、json |
| 成功判断 | 除 HTTP 状态外,核对业务状态、业务 ID 等实际约定 |
| 重试 | 对可能产生写入的请求,先确认幂等机制和重复提交处理 |
真实密钥不应被放入普通用户输入、示例 JSON 或公开回显请求中。配置认证时,可通过变量选择器引用 Secret 环境变量;本例仍保持 No Auth。Dify HTTP 认证与变量说明
请求超时只表示调用方未及时拿到结果,不等于服务端一定没有执行。对于真实创建类接口,是否可以重试,应依据服务端幂等和结果查询机制决定。
10. 常见问题排查表
| 现象 | 优先检查 |
|---|---|
| 接口说 JSON 格式不对 | 请求体是否只插入 request_body,是否重复加引号 |
| 回显 json 是字符串 | 是否对已序列化的请求体再次 JSON 编码 |
| 响应解析提示正文不是 JSON | 是否先判断了状态,实际 body 是否为 HTML 或空文本 |
| 503 没有进入 ELSE | Retry 是否关闭,Error Handling 是否为 None |
| 浏览器能访问,Dify 请求失败 | Dify 容器的网络、代理、DNS 和证书链是否可用 |
| Code 输出字段报错 | 声明的名称和类型是否与返回字典一致 |
| 响应状态成功,但回显核对失败 | 对象是否被包装、截断或修改,URL 是否仍为演示接口 |
| 聚合器变量不匹配 | 两个候选是否都是字符串,是否误开聚合分组 |
先在节点调试中定位输入与输出,再执行完整试运行;发布当前配置后,还要从 Web App 页面重新验证。
11. 验证范围与参考资料
本文的请求构造、响应解析代码使用本地示例与模拟响应检查。完整 Dify 工作流未在本文环境中实机运行;公网回显请求检查因本地执行环境的域名解析限制未完成,因此没有把公开服务可达性写成实测结论。
完整复现时,仍需在自己的 Dify 环境确认网络连通、节点运行、两条正常分支及超时行为。公开演示服务的实时可用性不属于本文代码保证范围。
参考资料:
后续可以把这条链路接到真实任务接口:先校验结构化数据,再按接口要求构造请求,最后根据业务响应确认结果。每一步都有明确输入和输出,才便于定位问题和维护。