ApiConnectActivity
分类: 通用步骤
命名空间: CMS.Plugin.FlowManagement.Domain.FlowBusiness.Activitys
基类: BusinessActivity
模块: FlowManagement.Domain
概述
ApiConnectActivity(请求API)是用于在流程中直接发送 HTTP 请求的节点。与 ApiCallActivity 不同,它不依赖系统互联中预配置的 API,而是直接配置 HTTP 请求的所有细节,包括 URL、请求方法、请求头、Query 参数、Body 参数等。这使得它更加灵活,适用于快速集成外部接口或测试 API 连接。
ApiConnectActivity 支持 GET、POST、PUT、DELETE 四种 HTTP 方法,支持从流程上下文动态获取参数值,并能够解析 JSON 响应数据,将指定字段提取到流程上下文中。
业务场景
适用场景
- 快速集成: 无需在系统互联中配置,直接在流程中调用外部 API
- API 测试: 测试外部 API 的连通性和响应
- 动态 URL: 根据流程数据动态构建请求 URL
- RESTful API: 调用标准的 RESTful API 接口
- 临时集成: 临时性的 API 调用,不需要长期维护
在系统中的作用
ApiConnectActivity 在 LMES 流程系统中扮演着灵活的 HTTP 客户端角色:
- 直接发送 HTTP 请求到外部系统
- 支持多种 HTTP 方法(GET、POST、PUT、DELETE)
- 动态构建请求参数
- 解析 JSON 响应数据
- 处理请求异常
与其他节点的协作
- BusinessActivity: 在 API 请求前准备参数,在请求后处理响应
- ApiCallActivity: 功能类似,但 ApiCallActivity 使用预配置的 API
- 其他节点: 可以串联多个 API 请求,实现复杂的集成场景
配置说明
基本配置
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| HttpMethod | FlowHttpMethod | 是 | - | 请求方法(GET/POST/PUT/DELETE) |
| Url | string | 是 | "http://" | 请求地址 |
| RequestTimeOut | int | 是 | 30 | 请求超时时间(秒) |
| Query | HttpParameterModels | 否 | 空集合 | Query 参数 |
| Body | HttpParameterModels | 否 | 空集合 | Body 参数 |
| BodyContext | string | 否 | - | Body 流程上下文标识 |
| Headers | HttpParameterModels | 否 | 空集合 | 请求头参数 |
| Response | HttpParameterModels | 否 | 空集合 | 返回响应参数配置 |
| ExceptionContext | string | 否 | - | 请求异常流程上下文标识 |
配置项详解
HttpMethod
说明: HTTP 请求方法,决定使用哪种 HTTP 动词发送请求。
取值范围:
HttpGet: GET 请求,用于查询数据HttpPost: POST 请求,用于创建数据HttpPut: PUT 请求,用于更新数据HttpDelete: DELETE 请求,用于删除数据
注意事项:
- GET 和 DELETE 请求不支持 Body 参数
- POST 和 PUT 请求的 Body 会以 JSON 格式发送
Url
说明: 请求的目标 URL 地址。支持在 URL 中使用占位符 {参数名},会自动替换为 Query 参数的值。
取值范围: 有效的 HTTP/HTTPS URL
注意事项:
- 可以从流程上下文动态获取:
Flow.DataItems["NodeName_Url"](NodeName 为节点的 Name 属性值) - 支持 URL 模板:
http://api.example.com/users/{userId} - Query 参数会自动拼接到 URL 后面
RequestTimeOut
说明: 请求超时时间,单位为秒。超过此时间未收到响应会抛出超时异常。
取值范围: 大于 0 的整数
注意事项:
- 默认 30 秒,根据实际 API 响应时间调整
- 超时会抛出异常,需要在流程中处理
Query
说明: Query 参数集合,会拼接到 URL 后面作为查询字符串。
参数模型:
- ParameterName: 参数名称
- ParameterValue: 参数值(支持从流程上下文获取)
- Description: 参数描述
注意事项:
- 如果参数名在 URL 中作为占位符存在,会替换占位符而不是拼接到查询字符串
- 参数值会自动进行 URL 编码
Body
说明: Body 参数集合,会以 JSON 格式作为请求体发送(仅 POST 和 PUT 请求)。
参数模型:
- ParameterName: 参数名称
- ParameterValue: 参数值(支持从流程上下文获取)
- Description: 参数描述
注意事项:
- 仅在 POST 和 PUT 请求中有效
- 参数会被序列化为 JSON 对象
- Content-Type 自动设置为 application/json
BodyContext
说明: Body 流程上下文标识。如果设置此属性,会优先从流程上下文获取数据作为请求体,而不是使用 Body 参数配置。
取值范围: 流程上下文中的键名
注意事项:
- 优先级高于 Body 参数配置
- 如果值是字符串,直接作为请求体
- 如果值是对象,会序列化为 JSON
Headers
说明: 请求头参数集合,用于设置 HTTP 请求头。
参数模型:
- ParameterName: 请求头名称(如 Content-Type、Accept)
- ParameterValue: 请求头值
- Description: 参数描述
注意事项:
- 常用请求头:Content-Type、Accept、User-Agent 等
- Authorization 可以通过流程上下文
{节点名}_Authorization(节点名为节点的 Name 属性值)设置
Response
说明: 返回响应参数配置,定义如何从 API 响应中提取数据。
参数模型:
- ParameterName: 响应数据中的字段路径(支持点号分隔)
- WriteParameterName: 写入流程上下文的键名
- Description: 参数描述
- DataType: 数据类型
注意事项:
- 仅支持 JSON 格式的响应
- 支持提取嵌套字段(如 "data.user.name")
- 如果响应是数组,会自动取第一个元素
ExceptionContext
说明: 请求异常流程上下文标识。当请求发生异常时,异常消息会写入此键。
取值范围: 流程上下文中的键名
注意事项:
- 如果不设置,默认使用
{节点名}_Exception(节点名为节点的 Name 属性值) - 可以在后续节点中检查此键判断是否发生异常
流程上下文
输入参数
| 参数名 | 类型 | 说明 |
|---|---|---|
| {节点名}_Url | string | 动态设置请求 URL |
| {节点名}_Authorization | string | 动态设置授权信息 |
| Query 参数值 | any | Query 配置中引用的流程上下文值 |
| Body 参数值 | any | Body 配置中引用的流程上下文值 |
| BodyContext | any | 完整的请求体数据 |
输出参数
| 参数名 | 类型 | 说明 |
|---|---|---|
| {节点名}_StatusCode | int | HTTP 响应状态码 |
| {节点名}_Response | string | 完整的响应内容 |
| {节点名}_Exception | string | 请求异常消息(如果发生异常) |
| {节点名}_Url | string | 实际请求的 URL(包含 Query 参数) |
| 自定义参数 | any | Response 配置中定义的提取字段 |
数据流转说明
-
请求前:
- 从流程上下文读取参数值
- 构建完整的请求 URL
- 准备请求头和请求体
-
发送请求:
- 使用 HttpClient 发送请求
- 等待响应(受超时时间限制)
-
响应处理:
- 将状态码写入流程上下文
- 将完整响应写入流程上下文
- 解析 JSON 并提取字段
- 将提取的字段写入流程上下文
-
异常处理:
- 捕获异常并写入异常上下文
- 记录异常日志
业务逻辑说明
处理流程
ApiConnectActivity 的执行流程如下:
-
初始化:
- 清空异常、状态码、响应等输出参数
- 获取 URL(优先从流程上下文获取)
- 获取 Authorization(从流程上下文获取)
-
构建请求参数:
- 将 Headers、Query、Body 配置转换为字典
- 从流程上下文获取参数值
- 处理 URL 占位符替换
- 构建查询字符串
-
准备请求体:
- 如果设置了 BodyContext,从流程上下文获取
- 否则使用 Body 参数配置
- 序列化为 JSON 字符串
-
创建 HTTP 客户端:
- 使用 IHttpClientFactory 创建客户端
- 设置超时时间
- 添加请求头
- 添加 Authorization 头(如果有)
-
发送请求:
- 根据 HttpMethod 选择请求方法
- GET/DELETE: 直接发送
- POST/PUT: 设置 Content-Type 为 application/json,发送请求体
-
处理响应:
- 读取响应内容
- 记录日志(成功或失败)
- 将状态码和响应写入流程上下文
- 解析 JSON 并提取字段
-
异常处理:
- 捕获所有异常
- 记录异常日志
- 将异常消息写入流程上下文
流程图
[初始化参数]
↓
[构建 URL 和参数]
↓
[创建 HttpClient]
↓
[设置请求头]
↓
[发送 HTTP 请求]
↓
[等待响应]
↓
[解析 JSON]
↓
[提取字段]
↓
[写入流程上下文]
URL 占位符替换
ApiConnectActivity 支持在 URL 中使用占位符:
配置示例:
- URL:
http://api.example.com/users/{userId}/orders/{orderId} - Query 参数:
- userId = 1001
- orderId = 2001
处理结果:
- URL:
http://api.example.com/users/1001/orders/2001
如果 Query 参数不在 URL 占位符中,会拼接到查询字符串:
- Query 参数: status = "active"
- 最终 URL:
http://api.example.com/users/1001/orders/2001?status=active
依赖服务
| 服务接口 | 用途 | 说明 |
|---|---|---|
| IHttpClientFactory | HTTP 客户端工厂 | 创建 HttpClient 实例 |
| Flow.ServiceProvider | 服务提供者 | 获取依赖注入的服务 |
| Flow.Logger | 日志记录器 | 记录请求和响应日志 |
异常处理
ApiConnectActivity 会捕获所有异常,不会中断流程:
- 异常消息写入 ExceptionContext 或
{节点名}_Exception(节点名为节点的 Name 属性值) - 记录完整的异常日志
- 可以在后续节点中检查异常键判断是否成功
日志记录
ApiConnectActivity 记录以下关键日志:
- 请求信息:
请求API步骤请求信息 {HttpMethod} Url={url}, Query={query}, Headers={headers}, Body={body} - 响应信息:
请求API步骤响应 StatusCode={statusCode}, Response={response} - 异常信息: 完整的异常堆栈信息
使用示例
基本示例:GET 请求
{
"Type": "ApiConnectActivity",
"Name": "查询用户信息",
"Alias": "GetUser",
"HttpMethod": "HttpGet",
"Url": "https://api.example.com/users/{userId}",
"RequestTimeOut": 30,
"Query": [
{
"ParameterName": "userId",
"ParameterValue": "{UserId_Value}"
}
],
"Response": [
{
"ParameterName": "data.name",
"WriteParameterName": "UserName_Value",
"DataType": "String"
},
{
"ParameterName": "data.email",
"WriteParameterName": "UserEmail_Value",
"DataType": "String"
}
]
}
高级示例:POST 请求
{
"Type": "ApiConnectActivity",
"Name": "创建订单",
"Alias": "CreateOrder",
"HttpMethod": "HttpPost",
"Url": "https://api.example.com/orders",
"RequestTimeOut": 60,
"Headers": [
{
"ParameterName": "Content-Type",
"ParameterValue": "application/json"
},
{
"ParameterName": "X-API-Key",
"ParameterValue": "{ApiKey_Value}"
}
],
"Body": [
{
"ParameterName": "customerId",
"ParameterValue": "{CustomerId_Value}"
},
{
"ParameterName": "productId",
"ParameterValue": "{ProductId_Value}"
},
{
"ParameterName": "quantity",
"ParameterValue": "{Quantity_Value}"
}
],
"Response": [
{
"ParameterName": "data.orderId",
"WriteParameterName": "OrderId_Value",
"DataType": "String"
},
{
"ParameterName": "data.status",
"WriteParameterName": "OrderStatus_Value",
"DataType": "String"
}
]
}
使用 BodyContext 示例
{
"Type": "BusinessActivity",
"Name": "准备订单数据",
"Alias": "PrepareOrderData"
}
// 在准备节点中构建完整的请求体
public override async Task ProcessAsync(ProcessflowEventArgs args)
{
var orderData = new
{
customerId = Flow.DataItems["CustomerId_Value"],
products = new[]
{
new { productId = "P001", quantity = 2 },
new { productId = "P002", quantity = 1 }
},
shippingAddress = new
{
street = "123 Main St",
city = "Beijing",
country = "China"
}
};
Flow.DataItems["OrderRequestBody"] = orderData;
await base.ProcessAsync(args);
}
{
"Type": "ApiConnectActivity",
"Name": "提交订单",
"Alias": "SubmitOrder",
"HttpMethod": "HttpPost",
"Url": "https://api.example.com/orders",
"BodyContext": "OrderRequestBody",
"Response": [
{
"ParameterName": "orderId",
"WriteParameterName": "OrderId_Value",
"DataType": "String"
}
]
}
完整流程示例
{
"Name": "用户注册流程",
"Activities": [
{
"Type": "BusinessActivity",
"Name": "验证输入",
"Alias": "ValidateInput"
},
{
"Type": "ApiConnectActivity",
"Name": "检查用户名是否存在",
"Alias": "CheckUsername",
"HttpMethod": "HttpGet",
"Url": "https://api.example.com/users/check",
"Query": [
{
"ParameterName": "username",
"ParameterValue": "{Username_Value}"
}
],
"Response": [
{
"ParameterName": "exists",
"WriteParameterName": "UsernameExists_Value",
"DataType": "Bool"
}
]
},
{
"Type": "BusinessActivity",
"Name": "判断用户名",
"Alias": "CheckUsernameResult"
},
{
"Type": "ApiConnectActivity",
"Name": "创建用户",
"Alias": "CreateUser",
"HttpMethod": "HttpPost",
"Url": "https://api.example.com/users",
"Body": [
{
"ParameterName": "username",
"ParameterValue": "{Username_Value}"
},
{
"ParameterName": "email",
"ParameterValue": "{Email_Value}"
},
{
"ParameterName": "password",
"ParameterValue": "{Password_Value}"
}
],
"Response": [
{
"ParameterName": "userId",
"WriteParameterName": "UserId_Value",
"DataType": "Int"
},
{
"ParameterName": "token",
"WriteParameterName": "AccessToken_Value",
"DataType": "String"
}
]
},
{
"Type": "ApiConnectActivity",
"Name": "发送欢迎邮件",
"Alias": "SendWelcomeEmail",
"HttpMethod": "HttpPost",
"Url": "https://api.example.com/emails/send",
"Body": [
{
"ParameterName": "to",
"ParameterValue": "{Email_Value}"
},
{
"ParameterName": "template",
"ParameterValue": "welcome"
},
{
"ParameterName": "userId",
"ParameterValue": "{UserId_Value}"
}
]
},
{
"Type": "BusinessActivity",
"Name": "完成注册",
"Alias": "CompleteRegistration"
}
]
}
处理响应示例
// 在后续节点中处理 API 响应
[Serializable]
[Design("处理API响应", "检查API调用结果", Sort = 1)]
[Category("自定义")]
public class HandleApiResponseActivity : BusinessActivity
{
public override async Task ProcessAsync(ProcessflowEventArgs args)
{
// 检查是否有异常
var exception = Flow.DataItems["CreateUser_Exception"]?.ToString();
if (!string.IsNullOrEmpty(exception))
{
Flow.Logger.LogErrorMessage($"API调用异常: {exception}", Name);
throw new BusinessException(Name, "用户创建失败");
}
// 检查状态码
var statusCode = Flow.DataItems["CreateUser_StatusCode"];
if (statusCode == null || (int)statusCode != 200)
{
var response = Flow.DataItems["CreateUser_Response"]?.ToString();
Flow.Logger.LogErrorMessage($"API返回错误: StatusCode={statusCode}, Response={response}", Name);
throw new BusinessException(Name, "用户创建失败");
}
// 获取响应数据
var userId = Flow.DataItems["UserId_Value"];
var token = Flow.DataItems["AccessToken_Value"]?.ToString();
Flow.Logger.LogMessage($"用户创建成功: UserId={userId}, Token={token}", Name);
await base.ProcessAsync(args);
}
}
扩展开发指南
继承层次
Activity (SYC.Flow.Kernel)
└── BusinessActivity
└── ApiConnectActivity
可重写方法
| 方法名 | 用途 | 何时重写 |
|---|---|---|
| ProcessAsync | 核心业务逻辑 | 需要自定义 HTTP 请求逻辑 |
自定义 API 连接节点示例
[Serializable]
[Design("增强API连接", "带重试和缓存的API连接节点", Sort = 1)]
[Category("自定义")]
public class EnhancedApiConnectActivity : ApiConnectActivity
{
[Design("启用缓存", "是否启用响应缓存", Sort = 20)]
[Category("请求信息")]
[DataMember]
public bool EnableCache { get; set; } = false;
[Design("缓存时间", "缓存有效期(秒)", Sort = 21)]
[Category("请求信息")]
[DataMember]
public int CacheSeconds { get; set; } = 300;
[Design("重试次数", "请求失败时的重试次数", Sort = 22)]
[Category("请求信息")]
[DataMember]
public int RetryCount { get; set; } = 3;
public override async Task ProcessAsync(ProcessflowEventArgs args)
{
// 检查缓存
if (EnableCache)
{
var cacheKey = $"ApiCache_{Name}_{Url}";
var cachedResponse = Flow.DataItems[cacheKey];
if (cachedResponse != null)
{
var cacheTime = (DateTime)Flow.DataItems[$"{cacheKey}_Time"];
if ((DateTime.Now - cacheTime).TotalSeconds < CacheSeconds)
{
Flow.Logger.LogMessage("使用缓存响应", Name);
Flow.DataItems[$"{Name}_Response"] = cachedResponse;
Flow.DataItems[$"{Name}_StatusCode"] = 200;
return;
}
}
}
// 重试逻辑
int attempt = 0;
Exception lastException = null;
while (attempt < RetryCount)
{
attempt++;
try
{
Flow.Logger.LogMessage($"API请求尝试 {attempt}/{RetryCount}", Name);
await base.ProcessAsync(args);
// 检查状态码
var statusCode = Flow.DataItems[$"{Name}_StatusCode"];
if (statusCode != null && (int)statusCode >= 200 && (int)statusCode < 300)
{
// 成功,保存到缓存
if (EnableCache)
{
var cacheKey = $"ApiCache_{Name}_{Url}";
Flow.DataItems[cacheKey] = Flow.DataItems[$"{Name}_Response"];
Flow.DataItems[$"{cacheKey}_Time"] = DateTime.Now;
}
return;
}
Flow.Logger.LogWarningMessage($"API返回错误状态码: {statusCode}", Name);
}
catch (Exception ex)
{
Flow.Logger.LogExceptionMessage(ex, Name);
lastException = ex;
}
if (attempt < RetryCount)
{
await Task.Delay(attempt * 1000);
}
}
throw new BusinessException(Name, $"API请求失败,已重试 {RetryCount} 次", lastException);
}
}
注意事项
- 超时设置: 根据 API 响应时间合理设置超时,避免流程长时间阻塞
- 异常处理: API 请求失败不会中断流程,需要检查异常键或状态码
- JSON 格式: Response 参数提取仅支持 JSON 格式的响应
- 请求体: POST 和 PUT 请求的 Body 会以 JSON 格式发送
- URL 编码: Query 参数会自动进行 URL 编码
- 占位符: URL 占位符必须在 Query 参数中定义
- BodyContext 优先级: 如果设置了 BodyContext,Body 参数配置会被忽略
- 开发建议:
- 在后续节点中检查状态码和异常,确保请求成功
- 使用 Response 配置提取所需字段,避免手动解析 JSON
- 合理设置超时时间,避免长时间等待
- 敏感信息(如 API Key)从流程上下文动态获取
- 记录详细的请求和响应日志,便于问题排查
- 对于重要的 API 调用,考虑添加重试机制
相关节点
- ApiCallActivity: 调用系统互联中配置的 API
- MethodCallActivity: 调用 C# 方法
- ScriptCallActivity: 调用脚本
常见问题
Q1: ApiConnectActivity 和 ApiCallActivity 有什么区别?
A:
- ApiConnectActivity: 直接在流程中配置所有请求细节,更灵活,适合临时集成
- ApiCallActivity: 使用系统互联中预配置的 API,更规范,适合长期维护的集成
Q2: 如何在 URL 中使用动态参数?
A: 使用占位符 {参数名},并在 Query 参数中定义:
{
"Url": "https://api.example.com/users/{userId}",
"Query": [
{
"ParameterName": "userId",
"ParameterValue": "{UserId_Value}"
}
]
}
Q3: 如何发送复杂的 JSON 请求体?
A: 使用 BodyContext:
// 在前置节点中构建对象
Flow.DataItems["RequestBody"] = new
{
user = new { name = "张三", age = 30 },
items = new[] { "item1", "item2" }
};
{
"BodyContext": "RequestBody"
}
Q4: 如何处理非 JSON 格式的响应?
A: 不配置 Response 参数,直接从 {节点名}_Response(节点名为节点的 Name 属性值)获取原始响应字符串,然后手动解析。
Q5: 如何添加 Bearer Token?
A: 通过流程上下文设置:
Flow.DataItems["ApiNode_Authorization"] = $"Bearer {token}";
Q6: 请求失败会中断流程吗?
A: 不会。异常会被捕获并写入异常上下文,需要在后续节点中检查。
Q7: 如何实现请求重试?
A: 创建自定义节点继承 ApiConnectActivity,在 ProcessAsync 中实现重试逻辑(参考扩展开发指南中的示例)。
更新历史
| 日期 | 版本 | 说明 |
|---|---|---|
| 2025-11-28 | 1.0 | 初始版本 |
本文档最后更新时间: 2025-11-28