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

使用C#读取他人私有Git仓库时遭遇404错误的排查求助

解决私有GitHub仓库API调用返回404的问题

我来帮你搞定这个问题!你浏览器能打开仓库但API返回404,核心原因几乎都是Token权限不足或者Token使用方式错误——GitHub对私有仓库的未授权访问会返回404(避免泄露仓库存在),而不是直观的403权限错误,这很容易误导人。

第一步:获取正确的访问Token

首先你需要生成一个有足够权限的GitHub Token,步骤如下:

  • 登录GitHub,点击右上角头像 → Settings → Developer settings
  • 选择Personal access tokens,推荐用Fine-grained tokens(更安全,权限更细),或者选Tokens (classic)
    • 对于Fine-grained tokens:
      1. 填写Token名称,设置有效期
      2. 在Repository access里选择你要访问的私有仓库(或者选All repositories如果需要批量访问)
      3. 在Repository permissions下找到Contents,设置为Read(只读足够了)
      4. 滚动到底部生成Token,复制保存好(只显示一次)
    • 对于Tokens (classic):
      1. 填写Token名称,勾选repo权限(这个权限包含私有仓库的读写访问)
      2. 生成并保存Token

第二步:修正你的代码

Octokit 代码修正

你的Octokit部分注释掉了Credentials设置,导致未授权访问私有仓库,所以返回404。加上Token即可:

static async Task<IEnumerable<string>> ListContentsOctokit(string repoOwner, string repoName, string path, string token)
{
    var client = new GitHubClient(new Octokit.ProductHeaderValue("Github-API-Test"));
    // 加上Token认证
    client.Credentials = new Credentials(token);
    var contents = await client.Repository.Content.GetAllContents(repoOwner, repoName, path);
    return contents.Select(content => content.Name);
}

调用的时候传入你的Token:

var octokitResults = await ListContentsOctokit(repoOwner, repoName, path, "你的Token");

HttpClient 代码修正

你的HttpClient实现有两个问题:一是没加Authorization头,二是用了不推荐的Basic认证方式,改成Bearer Token更稳妥:

1. 修改HttpClient初始化方法

private static HttpClient GetGithubHttpClient(string token)
{
    return new HttpClient
    {
        BaseAddress = new Uri("https://api.github.com"),
        DefaultRequestHeaders =
        {
            {"User-Agent", "Github-API-Test"},
            // 使用Bearer Token方式认证
            {"Authorization", $"token {token}"}
        }
    };
}

2. 修正ListContents方法

static async Task<IEnumerable<string>> ListContents(string repoOwner, string repoName, string path, string token)
{
    using (var client = GetGithubHttpClient(token))
    {
        var resp = await client.GetAsync($"repos/{repoOwner}/{repoName}/contents/{path}");
        // 可以在这里加个状态码检查,方便调试
        if (!resp.IsSuccessStatusCode)
        {
            var errorMsg = await resp.Content.ReadAsStringAsync();
            throw new Exception($"API请求失败:{resp.StatusCode} - {errorMsg}");
        }
        var bodyString = await resp.Content.ReadAsStringAsync();
        var bodyJson = JToken.Parse(bodyString);
        return bodyJson.SelectTokens("$.[*].name").Select(token => token.Value<string>());
    }
}

调用时传入Token:

var httpClientResults = await ListContents(repoOwner, repoName, path, "你的Token");

修正GithubClient中的递归读取代码

你的getRepo方法里用了Basic认证,而且有个小bug(给错误的请求对象加User-Agent头),改成Bearer Token并修复bug:

private static async Task<Directory> readDirectory(String name, HttpClient client, string uri, string access_token)
{
    //get the directory contents
    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri);
    // 替换成Bearer Token认证
    request.Headers.Add("Authorization", $"token {access_token}");
    request.Headers.Add("User-Agent", "lk-github-client");

    //parse result
    HttpResponseMessage response = await client.SendAsync(request);
    String jsonStr = await response.Content.ReadAsStringAsync();
    response.Dispose();

    FileInfo[] dirContents = JsonConvert.DeserializeObject<FileInfo[]>(jsonStr);

    //read in data
    Directory result;
    result.name = name;
    result.subDirs = new List<Directory>();
    result.files = new List<FileData>();

    foreach (FileInfo file in dirContents)
    {
        if (file.type == "dir")
        {
            //read in the subdirectory
            Directory sub = await readDirectory(file.name, client, file._links.self, access_token);
            result.subDirs.Add(sub);
        }
        else
        {
            //get the file contents;
            HttpRequestMessage downLoadUrl = new HttpRequestMessage(HttpMethod.Get, file.download_url);
            downLoadUrl.Headers.Add("Authorization", $"token {access_token}");
            // 修复bug:给downLoadUrl加User-Agent,不是之前的request
            downLoadUrl.Headers.Add("User-Agent", "lk-github-client");
            
            HttpResponseMessage contentResponse = await client.SendAsync(downLoadUrl);
            String content = await contentResponse.Content.ReadAsStringAsync();
            contentResponse.Dispose();
            
            result.files.Add(new FileData { name = file.name, contents = content });
        }
    }
    return result;
}

常见坑点提醒

  • 404≠仓库不存在:GitHub为了保护私有仓库隐私,未授权访问时会返回404,而不是403权限错误,所以遇到404先检查Token权限。
  • 必须设置User-Agent:GitHub API要求所有请求必须带User-Agent头,否则会返回403。
  • Token权限要匹配:如果是只读访问,不要给多余的写权限,Fine-grained tokens比classic tokens更安全。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 09:03:34