You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Octokit实现文件存在则更新、不存在则创建(避免try-catch)

问题:使用Octokit实现文件不存在则创建、存在则更新的简洁方式?

我想了解是否有一种简洁的方式,使用Octokit库实现指定文件不存在时创建新文件,存在则更新它?目前我通过try-catch块来实现,但希望找到除try-catch之外更直接的方法。

以下是我的代码实现:

private IRepositoryContentsClient Content => _client.Repository.Content;

public void TransmitLog(string logFilePath)
{
    var logContent = LogFileToString(logFilePath);
    var githubPath = GenerateGithubPath(logFilePath);
    try
    {
        UpdateLog(githubPath, logContent);
    }
    catch (NotFoundException)
    {
        CreateLog(githubPath, logContent);
    }
    catch (AggregateException) 
    {
        CreateLog(githubPath, logContent);
    }
}

private void UpdateLog(string githubPath, string logContent)
{
    // MY APP FAILS HERE
    var existingFile = Content.GetAllContentsByRef(
        _owner, _repo, githubPath, _branch).Result;
    // update the file
    var relevantSha = existingFile.First().Sha;
    var updateRequest = new UpdateFileRequest("Log update" + DateTime.UtcNow, logContent, relevantSha, _branch);
    var updateChangeSet = Content.UpdateFile(_owner, _repo, githubPath, updateRequest);
}

private void CreateLog(string githubPath, string logFileContent)
{
    // if file is not found, create it
    var createRequest =
        new CreateFileRequest("Log Creation" + DateTime.UtcNow, logFileContent, _branch);
    var createChangeSet = Content.CreateFile(_owner, _repo, githubPath, createRequest);
}

编辑说明:最初我将CreateLog和UpdateLog都声明为async void,这引发了一系列其他问题,现已修正,我现在真正关注的是如何避免这种try-catch结构。


解决方案

Octokit本身并没有提供直接的"upsert(存在则更新,不存在则创建)"接口,因为GitHub的REST API也没有单一的端点支持这个操作。不过我们可以优化逻辑,将"先尝试更新再捕获异常"的模式,改为主动检查文件存在性,让代码逻辑更清晰,减少不必要的API调用。

优化后的代码实现

private IRepositoryContentsClient Content => _client.Repository.Content;

// 改为async Task避免同步调用.Result带来的死锁问题
public async Task TransmitLog(string logFilePath)
{
    var logContent = LogFileToString(logFilePath);
    var githubPath = GenerateGithubPath(logFilePath);
    
    IReadOnlyList<RepositoryContent> existingFile = null;
    try
    {
        // 主动检查文件是否存在
        existingFile = await Content.GetAllContentsByRef(_owner, _repo, githubPath, _branch);
    }
    catch (NotFoundException)
    {
        // 文件不存在,直接创建
        await CreateLog(githubPath, logContent);
        return;
    }
    catch (AggregateException ae) when (ae.InnerException is NotFoundException)
    {
        // 处理异步调用包装的NotFoundException
        await CreateLog(githubPath, logContent);
        return;
    }

    // 文件存在,执行更新(直接复用已获取的SHA,无需再次调用API)
    await UpdateLog(githubPath, logContent, existingFile.First().Sha);
}

// 直接传入已获取的SHA,避免重复查询文件信息
private async Task UpdateLog(string githubPath, string logContent, string fileSha)
{
    var updateRequest = new UpdateFileRequest($"Log update {DateTime.UtcNow}", logContent, fileSha, _branch);
    await Content.UpdateFile(_owner, _repo, githubPath, updateRequest);
}

private async Task CreateLog(string githubPath, string logFileContent)
{
    var createRequest = new CreateFileRequest($"Log Creation {DateTime.UtcNow}", logFileContent, _branch);
    await Content.CreateFile(_owner, _repo, githubPath, createRequest);
}

优化点说明

  1. 主动检查存在性:先调用GetAllContentsByRef判断文件是否存在,而不是先尝试更新再捕获异常,逻辑更直观。
  2. 减少API调用:获取文件信息后直接复用其SHA值,避免在UpdateLog中重复查询文件内容。
  3. 异步优化:将方法改为async Task,避免使用.Result带来的潜在死锁问题,符合异步编程最佳实践。
  4. 精准异常过滤:使用when关键字过滤AggregateException,只处理内部为NotFoundException的情况,避免捕获无关异常。

如果想要完全避免try-catch,目前没有可行方案——因为GitHub API在文件不存在时必然会返回404,Octokit会将其包装为NotFoundException抛出,这是无法绕过的。但上述优化后的代码已经将异常处理从"控制流工具"转化为"错误处理手段",代码结构更清晰易读。


内容的提问来源于stack exchange,提问作者emilaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 02:10:15