跳到主要内容
版本:Next

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 请求,实现复杂的集成场景

配置说明

基本配置

属性名类型必填默认值说明
HttpMethodFlowHttpMethod-请求方法(GET/POST/PUT/DELETE)
Urlstring"http://"请求地址
RequestTimeOutint30请求超时时间(秒)
QueryHttpParameterModels空集合Query 参数
BodyHttpParameterModels空集合Body 参数
BodyContextstring-Body 流程上下文标识
HeadersHttpParameterModels空集合请求头参数
ResponseHttpParameterModels空集合返回响应参数配置
ExceptionContextstring-请求异常流程上下文标识

配置项详解

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 属性值)
  • 可以在后续节点中检查此键判断是否发生异常

流程上下文

输入参数

参数名类型说明
{节点名}_Urlstring动态设置请求 URL
{节点名}_Authorizationstring动态设置授权信息
Query 参数值anyQuery 配置中引用的流程上下文值
Body 参数值anyBody 配置中引用的流程上下文值
BodyContextany完整的请求体数据

输出参数

参数名类型说明
{节点名}_StatusCodeintHTTP 响应状态码
{节点名}_Responsestring完整的响应内容
{节点名}_Exceptionstring请求异常消息(如果发生异常)
{节点名}_Urlstring实际请求的 URL(包含 Query 参数)
自定义参数anyResponse 配置中定义的提取字段

数据流转说明

  1. 请求前:

    • 从流程上下文读取参数值
    • 构建完整的请求 URL
    • 准备请求头和请求体
  2. 发送请求:

    • 使用 HttpClient 发送请求
    • 等待响应(受超时时间限制)
  3. 响应处理:

    • 将状态码写入流程上下文
    • 将完整响应写入流程上下文
    • 解析 JSON 并提取字段
    • 将提取的字段写入流程上下文
  4. 异常处理:

    • 捕获异常并写入异常上下文
    • 记录异常日志

业务逻辑说明

处理流程

ApiConnectActivity 的执行流程如下:

  1. 初始化:

    • 清空异常、状态码、响应等输出参数
    • 获取 URL(优先从流程上下文获取)
    • 获取 Authorization(从流程上下文获取)
  2. 构建请求参数:

    • 将 Headers、Query、Body 配置转换为字典
    • 从流程上下文获取参数值
    • 处理 URL 占位符替换
    • 构建查询字符串
  3. 准备请求体:

    • 如果设置了 BodyContext,从流程上下文获取
    • 否则使用 Body 参数配置
    • 序列化为 JSON 字符串
  4. 创建 HTTP 客户端:

    • 使用 IHttpClientFactory 创建客户端
    • 设置超时时间
    • 添加请求头
    • 添加 Authorization 头(如果有)
  5. 发送请求:

    • 根据 HttpMethod 选择请求方法
    • GET/DELETE: 直接发送
    • POST/PUT: 设置 Content-Type 为 application/json,发送请求体
  6. 处理响应:

    • 读取响应内容
    • 记录日志(成功或失败)
    • 将状态码和响应写入流程上下文
    • 解析 JSON 并提取字段
  7. 异常处理:

    • 捕获所有异常
    • 记录异常日志
    • 将异常消息写入流程上下文

流程图

[初始化参数]

[构建 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

依赖服务

服务接口用途说明
IHttpClientFactoryHTTP 客户端工厂创建 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 调用,考虑添加重试机制

相关节点

常见问题

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-281.0初始版本

本文档最后更新时间: 2025-11-28