基于 Harbor API v2.0 的镜像仓库查询代理实践
需求
管理后台的镜像仓库页面需要展示各项目的镜像列表和标签。Harbor 的凭证不能暴露给浏览器,前端也不适合直接处理 Harbor 的分页协议,于是在后端加了一层代理服务。
方案
后端新增 RegistryService,通过 Basic Auth 调用 Harbor API v2.0,前端只与自家 API 交互。这样做带来三个额外好处:分页在服务端聚合完成、错误信息统一格式、镜像地址规范化。
关键接口
GET /projects:项目列表。GET /projects/{project_name}/repositories:仓库列表。GET /projects/{p}/repositories/{r}/artifacts?with_tag=true:含标签的镜像列表。
分页处理
Harbor 使用 page 和 page_size 分页,总数在响应头 X-Total-Count 中返回。仓库数量多时必须循环拉取并在服务端聚合,否则前端拿到的永远只是第一页。
镜像地址规范化
统一为 registry/project/image:tag 格式,页面展示、复制按钮、拉取命令都基于同一格式拼接。之前各处各写各的,出现过展示名和实际拉取地址不一致的低级问题。
代码片段
public List<ImageTag> listTags(String project, String repo) {
String url = harborBase + "/projects/" + project
+ "/repositories/" + urlEncode(repo)
+ "/artifacts?with_tag=true";
return restClient.get()
.header(HttpHeaders.AUTHORIZATION, basicAuth())
.retrieve()
.body(ArtifactPage.class)
.getTags();
}
配套策略
镜像保留策略设定为每个镜像仓库只保留最近 3 个版本,清理由 CI 流水线定时执行。页面上明确提示该策略,避免用户以为历史版本随时可以拉取。
代理层的价值不只是隐藏凭证——分页聚合、错误统一、地址规范化,这些「脏活」放在服务端,前端才能保持简单。