一、为什么低代码和RPA会走到一起
现在的软件开发,越来越讲究“快”。业务部门希望今天提需求,明天就能看到结果。传统的开发流程要写代码、测试、部署,一环扣一环,周期太长。低代码平台的出现,让很多人只需要拖动几个组件、配置一下参数,就能做出一个能用的应用。RPA(机器人流程自动化)呢,更是把“模拟人操作电脑”这件事做到了极致。你教它一遍点击、输入、读取,它就能每天重复做一千遍。
这两者结合起来,你会发现一个很舒服的场景:低代码负责快速搭建业务界面和流程,RPA负责把那些需要人工操作的、重复的劳动接管过来。尤其是当RPA需要和外部系统交换数据的时候,必然要调用API。API就像一条电话线,机器人拿起电话,向对方的系统问一句“嘿,给我最新的订单数据”,对方听到了,再把数据传回来。
但现实总是很骨感。电话线会断,对方可能没人接,也可能说了一半突然挂断。API调用时,各种意外都会发生。在这个过程中,如果处理不好,轻则流程中断,重则数据错乱。所以我们需要一套异常处理策略。
二、调用API时那些让人头疼的意外
想象一下,你让机器人每天晚上去查询所有客户的对账单,然后生成汇总表。结果有一天,对方公司的服务器正在升级,接口返回了一个“503 Service Unavailable”。如果你的机器人没有做任何处理,它就会直接报错,然后整个流程停在半路,第二天你上班发现,昨晚该生成的文件一个都没有。这就是没有异常处理的下场。
常见的API异常大概有这几类:
第一,超时。机器人把请求发出去了,但对方迟迟没有回应。可能网络拥堵,可能对方在处理一个特别大的任务。如果没有设置超时时间,机器人可能会一直傻傻地等下去。
第二,HTTP错误状态码。比如404说明接口地址变了,401说明凭证失效,500说明服务器内部炸了。这些错误各有各的原因,有的需要人工介入,有的只需要多试几次。
第三,数据格式问题。接口虽然返回了200,但返回的内容不是预期的格式。比如字段名变了,或者本该是数字的给成了字符串。这类问题最隐蔽,因为系统没有报错,但数据已经错了。
第四,业务逻辑错误。有些接口会返回一个业务码,比如“余额不足”、“库存不够”。这些不是技术异常,但也需要流程去处理。
面对这些意外,我们不能指望机器人运气好,每次都碰上正常情况。必须在代码里预判各种可能,并给出对应的方案。好在UiPath提供的灵活性让我可以像写传统程序一样处理异常,而不需要依赖那些黑盒组件。
三、UiPath里的异常处理基本功
3.1 什么?低代码也能写Try-Catch?
很多人一听到“低代码”,就以为只能拖拖拽拽,不能写代码。实际上,UiPath的“代码活动”允许你插入C#代码,这样既能享受低代码的便捷,又能在关键时刻用代码来兜底。就像自动挡汽车,大部分时间不用管档位,但遇到陡坡时,切换到手动模式会更稳妥。
在UiPath中,除了代码活动,你还可以用自带的“HTTP请求”活动。那个也很好用,但异常处理能力比较固定,比如只能设置超时,很难做复杂的重试和错误分类。所以,当你的需求稍微复杂一点,比如需要根据不同的错误码走不同的分支,代码活动会是一个更好的选择。这也是我们今天主要讨论代码的原因。
先看一个最简单的异常接住框架。技术栈:C#(UiPath代码活动)。
// 引入需要用到的命名空间
using System;
// 尝试执行有可能失败的操作
try
{
// 这里可以调用API、读写文件、执行SQL等
Console.WriteLine("正在调用API...");
throw new Exception("模拟一个网络异常"); // 故意抛出一个异常,方便演示
}
catch (Exception ex)
{
// 捕获异常后,把信息记录下来,而不是让流程直接崩溃
Console.WriteLine($"捕获到异常:{ex.Message}");
// 在UiPath中,可以在这里把状态写到日志文件或数据库
}
你看,其实非常简单。在UiPath里,你可以直接把这段代码粘到“代码活动”中。这样,即使调用的接口突然出错,机器人也能继续往后走,而不是停在那里。
但光接住还不够,我们要分门别类地处理。比如超时要重试,权限错误要通知管理员,数据格式错误要跳过这条记录。这就要用到多个catch块。
// 技术栈:C#(UiPath代码活动)
using System;
using System.Net.Http;
using System.Threading.Tasks;
try
{
using (var httpClient = new HttpClient())
{
// 设置超时为10秒
httpClient.Timeout = TimeSpan.FromSeconds(10);
// 发起GET请求
var response = httpClient.GetAsync("https://api.example.com/orders").Result;
// 如果状态码成功,就读取内容
if (response.IsSuccessStatusCode)
{
var json = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(json);
}
else
{
// 状态码不成功,抛出指定的异常
throw new HttpRequestException($"请求失败,状态码:{(int)response.StatusCode}");
}
}
}
catch (TaskCanceledException ex)
{
// 超时异常
Console.WriteLine("请求超时了,可能对方服务器太慢。");
}
catch (HttpRequestException ex)
{
// 网络或HTTP状态码异常
Console.WriteLine($"HTTP请求出错:{ex.Message}");
}
catch (Exception ex)
{
// 其他所有异常
Console.WriteLine($"未知错误:{ex.Message}");
}
在实际项目中,你还可以把这些异常信息通过UiPath.Core.Log写入日志活动,或者发送邮件给管理员。重点是:异常不能白白被吃掉,要有反馈、有记录。
3.2 学会重试,别让一次失败就全盘崩溃
很多API的异常其实都是临时的。比如网络波动、服务器瞬时高负载。这时候,我们只需要等一等,再试一次,很可能就成功了。所以重试机制是API调用中最重要的策略之一。
我们可以用循环来实现简单的重试。技术栈:C#(UiPath代码活动)。
using System;
using System.Net.Http;
using System.Threading;
// 最大重试次数
int maxRetry = 3;
// 当前重试次数
int currentTry = 0;
// 保存最终结果
string result = null;
while (currentTry < maxRetry)
{
currentTry++;
try
{
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromSeconds(15);
var response = client.GetAsync("https://api.example.com/data").Result;
if (response.IsSuccessStatusCode)
{
result = response.Content.ReadAsStringAsync().Result;
break; // 成功就跳出循环
}
// 如果返回的是5xx错误,就继续重试;如果是4xx错误,说明请求有问题,直接抛异常
if ((int)response.StatusCode >= 500)
{
Console.WriteLine($"第 {currentTry} 次请求返回服务器错误:{(int)response.StatusCode},稍后重试...");
}
else
{
throw new Exception($"请求失败,状态码:{(int)response.StatusCode}");
}
}
}
catch (TaskCanceledException)
{
Console.WriteLine($"第 {currentTry} 次请求超时了,稍后重试...");
}
catch (Exception ex) when (!(ex is TaskCanceledException))
{
// 如果不是超时异常,直接抛出,不再重试
throw;
}
// 等待一段时间再重试,这里等待2秒
Thread.Sleep(2000);
}
if (result != null)
{
Console.WriteLine("请求成功,拿到了数据:" + result);
}
else
{
Console.WriteLine("重试了多次仍然失败,需要人工介入。");
}
注意,这里的重试不能太频繁,不然会给对方服务器造成压力。更专业的做法是使用“指数退避”,比如第一次等2秒,第二次等4秒,第三次等8秒,像这样成倍增长。这样既给了对方恢复的时间,也避免把自己拖垮。
3.3 不要只看状态码,还要学会解析响应内容
有时候接口返回的是200,但内容里却写着“当前用户未登录”。所以,除了检查HTTP状态码,我们还要检查业务码。这就像打电话通了,但对方说“我不认识你”,你也要挂断并处理。
技术栈:C# + Newtonsoft.Json(UiPath中可以直接用)。
using System;
using Newtonsoft.Json.Linq;
// 模拟一个接口返回的JSON字符串
string responseJson = "{\"code\": 401, \"message\": \"登录已过期\", \"data\": null}";
// 解析成JObject
JObject obj = JObject.Parse(responseJson);
// 取出业务码
int businessCode = (int)obj["code"];
if (businessCode == 200)
{
// 业务成功,正常处理数据
Console.WriteLine("业务成功:" + obj["data"]);
}
else if (businessCode == 401)
{
// 登录过期,需要重新认证
Console.WriteLine("业务失败:登录已过期,请重新获取token。");
}
else if (businessCode == 500)
{
// 业务服务器内部错误
Console.WriteLine("业务失败:服务器内部错误。");
}
else
{
// 未知业务码,按一般错误处理
Console.WriteLine("业务失败:" + obj["message"]);
}
这样,我们就能在代码里从容地区分“技术异常”和“业务异常”,采取不同的处理方式。
四、一个完整的场景:从发起请求到优雅收场
假设我们要从外部系统拉取今天的销售订单,然后写入本地数据库。我们不能只写一个GET请求,还要考虑超时、重试、业务错误、日志记录。下面这个C#方法演示了如何把前面讲到的知识点组合起来。
技术栈:C#(UiPath代码活动,依赖Newtonsoft.Json)。
using System;
using System.Net.Http;
using System.Threading;
using Newtonsoft.Json.Linq;
// 这个方法用于获取订单数据,返回JSON字符串
public static string FetchOrders(string apiUrl, string token)
{
// 最大重试次数
const int MaxRetry = 3;
// 每次重试的基础等待时间(毫秒)
const int BaseDelay = 1000;
// 当前已尝试次数
int retryCount = 0;
// 最终结果
string jsonResult = null;
while (retryCount < MaxRetry)
{
retryCount++;
try
{
using (var client = new HttpClient())
{
// 设置超时时间
client.Timeout = TimeSpan.FromSeconds(20);
// 把token放到请求头中,用于身份验证
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
// 发起GET请求
var response = client.GetAsync(apiUrl).Result;
// 拿到响应内容
var content = response.Content.ReadAsStringAsync().Result;
// 如果HTTP状态码是成功
if (response.IsSuccessStatusCode)
{
// 再检查业务码
JObject json = JObject.Parse(content);
int code = (int)json["code"];
if (code == 200)
{
// 业务成功,保存数据
jsonResult = content;
// 跳出循环
break;
}
else if (code == 401)
{
// token失效,不需要重试,直接抛异常
throw new Exception("认证失败,请检查token。");
}
else
{
// 其他业务错误,比如数据不存在,也直接抛出
throw new Exception($"业务错误:{(string)json["message"]}");
}
}
else if ((int)response.StatusCode >= 500)
{
// 服务器错误,可以重试
Console.WriteLine($"第 {retryCount} 次请求,服务器返回HTTP {(int)response.StatusCode},将进行重试。");
}
else
{
// 客户端错误,比如404、400,重试没有意义,直接抛出
throw new Exception($"HTTP请求失败,状态码:{(int)response.StatusCode}");
}
}
}
catch (TaskCanceledException)
{
// 超时,走重试
Console.WriteLine($"第 {retryCount} 次请求超时,将进行重试。");
}
catch (HttpRequestException ex)
{
// 网络层异常,走重试
Console.WriteLine($"第 {retryCount} 次请求出现网络错误:{ex.Message}");
}
// 如果已经尝试了MaxRetry次,还是失败,就跳出循环
if (retryCount >= MaxRetry)
{
break;
}
// 指数退避:第1次等1秒,第2次等2秒,第3次等4秒
int delay = BaseDelay * (int)Math.Pow(2, retryCount - 1);
Console.WriteLine($"等待 {delay} 毫秒后进行下一次重试...");
Thread.Sleep(delay);
}
// 如果重试多次仍然没有结果,抛出一个可识别的异常
if (jsonResult == null)
{
throw new Exception($"在 {MaxRetry} 次重试之后,仍然无法获取订单数据。");
}
return jsonResult;
}
看,这个FetchOrders方法把超时、重试、业务码检查、日志输出都揉在了一起。你可以在UiPath的代码活动中直接粘贴调用,也可以把它封装成一个类库,让多个流程共用。这样做的好处是,以后接口地址变了,或者请求头需要加参数,只需要改一个地方,而不是每个流程都去翻一遍。这就是把成熟思路沉淀下来的价值。
五、这个组合适合用在哪些地方
最常见的场景有两个。第一个是跨系统数据同步。很多企业内部有好几个系统,比如ERP、CRM、财务系统。它们之间没有现成的接口,但都有API可以调用。RPA机器人可以定时去A系统抓数据,处理一下,再通过API写到B系统。在这个过程中,异常处理能保证某一次同步失败时,机器人不会把错误数据传过去,也不会静悄悄放弃。
第二个是无人值守的自动化流程。比如每天凌晨自动跑批,机器人需要调用银行接口获取交易流水。如果遇到银行系统维护,接口会返回503。有了重试机制,机器人可以等几分钟再试,而不是直接躺在那里等待早上人来救。
另外,在客服工单处理中,RPA也需要调用工单系统的API来创建、查询工单。如果遇到权限过期,异常处理可以及时发送告警邮件,让管理员更新凭证,而不是让客户的工单一直卡在队列里。
还有一个小众但很实用的场景:数据清洗。比如你从旧系统导出了一批客户数据,需要调用第三方地址校验API来修正地址。每条记录可能几百毫秒,如果中途出现一次超时,你不能让整个任务全部失败,而是应该记录下这条失败的记录,继续处理下一条,最后统一汇报。这种场景下,异常处理的意义不只是“让流程跑完”,更是“让流程知道有哪些没跑完”。
六、聊聊这种方式的优点和局限性
6.1 优点
第一,用低代码结合RPA做API调用,开发速度很快。你不必从头搭建一个完整的服务,只需要在UiPath中拖一个“代码活动”,把C#逻辑写进去即可。对于小团队或者业务人员来说,学习曲线比传统开发平缓得多。
第二,异常处理策略灵活。因为可以写C#代码,所以几乎任何异常处理方式都可以实现:重试、回退、告警、白名单、熔断……只要你想得到,代码都能做到。
第三,调试和观察方便。UiPath可以逐行运行流程,配合日志活动,你能很直观地看到哪一步出错,错误信息是什么。代码里的Console.WriteLine也可以直接输出到输出窗格,定位问题非常快。
6.2 局限性
当然,它并不是万能的。首先,代码活动虽然灵活,但本质上我们还是写代码,如果逻辑太复杂,代码的可维护性会变差。其次,UiPath的代码活动运行在RPA机器人进程中,如果机器人本身挂了,异常处理也就无从谈起。第三,API调用依赖于网络环境,如果企业网络本身不通,重试再多次也是白费。所以,低代码+RPA更适合做“端到端的轻量级集成”,而不是替代企业级API网关。
此外,性能上也要留心。RPA机器人本质上是模拟人在操作,它的并发能力有限。如果你需要每秒处理上千个请求,那还是应该用专业的服务端程序,而不是靠一堆机器人去跑。异常处理能解决“偶发失败”,但解决不了“架构不够”的问题。
七、动手之前一定要想清楚的事
第一,设置合理的超时时间和重试次数。不要为了追求成功率把重试次数设成无限,不然对方服务器会被你打死,你的流程也会被拖死。建议超时15秒,重试3次,每次间隔递增。
第二,别把所有异常都吞掉。有些人喜欢写个空的catch,异常逮住后什么都不做。这样会掩盖问题,后面出了问题都找不到原因。一定要记录日志,至少要有Console.WriteLine,在UiPath里最好用“日志消息”活动。
第三,注意敏感信息的保护。在代码中调用API时,可能需要把token或密码写进去。不要硬编码在代码里,建议使用UiPath的凭据资产或环境变量。否则你的代码一旦被别人看到,等于把钥匙交给了别人。
第四,做好降级方案。如果重试多次仍然失败,机器人应该做什么?是跳过这条数据继续下一单,还是停下来等人工处理?这需要和业务方提前商量好。好的异常处理不是“永远成功”,而是“失败时也知道该怎么走”。
第五,要监控重试次数和失败率。如果某个接口经常需要重试,那可能不是网络的原因,而是对方接口本身不稳定。这时候你应该和对方技术团队沟通,而不是继续无限重试。把重试次数、失败次数、平均响应时间这些指标记录下来,可以帮助你更早发现问题。
八、总结
低代码和RPA的结合,让自动化流程的开发门槛大大降低,但API调用中的异常问题不会消失。通过掌握Try-Catch、重试机制、响应解析等基础策略,我们就可以用UiPath写出一段段“有韧性”的代码。它们能够应对超时、服务器错误、业务错误等常见意外,让机器人不再脆弱。当然,任何技术都有适用边界,我们要清楚它擅长什么、不擅长什么,在设计流程时把异常处理当成一项必须的工作对待,而不是可有可无的点缀。这样,机器人才能真正成为我们可靠的帮手,而不是一个需要随时盯着的“问题少年”。
评论
围绕“低代码与RPA结合,UiPath在API调用时的异常处理策略”参与讨论