一、为什么低代码和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写出一段段“有韧性”的代码。它们能够应对超时、服务器错误、业务错误等常见意外,让机器人不再脆弱。当然,任何技术都有适用边界,我们要清楚它擅长什么、不擅长什么,在设计流程时把异常处理当成一项必须的工作对待,而不是可有可无的点缀。这样,机器人才能真正成为我们可靠的帮手,而不是一个需要随时盯着的“问题少年”。