8.6 KiB
8.6 KiB
App 客户端对接文档
本文面向 Android、iOS、Windows、macOS、Linux 等客户端,用于对接 VersionFlow 的公开检查更新接口。
1. 接口概览
客户端不需要登录后台,也不需要 JWT。每个 App 在后台创建后会生成一个公开标识 AppKey,客户端使用它检查是否有新版本。
接口地址:
GET /api/client/v1/update
本地 Docker 环境示例:
GET http://localhost:8080/api/client/v1/update?appKey={appKey}&platform=android&channel=stable¤tBuild=100
生产环境请替换为你的正式域名:
GET https://your-domain.com/api/client/v1/update?appKey={appKey}&platform=android&channel=stable¤tBuild=100
2. 请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
appKey |
string | 是 | 后台应用详情页中的 AppKey。它是公开客户端标识,不是密钥。 |
platform |
string | 是 | 平台键,例如 android、ios、windows、macos、linux。 |
channel |
string | 否 | 发布渠道,默认 stable。可使用 beta 或后台自定义渠道。 |
currentBuild |
number | 否 | 当前客户端整数构建号,默认 0。必须大于等于 0。 |
注意:版本新旧只比较 buildNumber/currentBuild,不要用 versionName 做大小判断。
3. 成功响应
无可用更新:
{
"hasUpdate": false,
"forceUpdate": false,
"release": null
}
有可用更新:
{
"hasUpdate": true,
"forceUpdate": false,
"release": {
"id": "2f0f4c1a-7c8f-4d5b-90fa-5cb0b2f3e1a1",
"versionName": "2.4.0",
"buildNumber": 240,
"downloadUrl": "https://downloads.example.com/app-2.4.0.apk",
"releaseNotes": "- 新增功能\n- 修复问题",
"sha256": "可选的 64 位 SHA-256",
"fileSize": 104857600,
"publishedAt": "2026-07-20T05:00:00+00:00"
}
}
字段说明:
| 字段 | 说明 |
|---|---|
hasUpdate |
是否存在比 currentBuild 更高的已生效版本。 |
forceUpdate |
是否必须升级。后台版本标记强制更新,或当前构建号低于 minimumSupportedBuild 时为 true。 |
release.versionName |
展示版本号,例如 2.4.0。仅用于显示。 |
release.buildNumber |
目标版本整数构建号,用于比较版本新旧。 |
release.downloadUrl |
安装包下载外链,系统不上传、不代理安装包。 |
release.releaseNotes |
Markdown 更新说明。App 内展示时建议做安全渲染或纯文本展示。 |
release.sha256 |
可选校验值。客户端下载后可用它校验安装包完整性。 |
release.fileSize |
可选文件大小,单位字节。 |
release.publishedAt |
发布时间,UTC 时间。 |
4. 错误响应
| HTTP 状态码 | 场景 |
|---|---|
400 |
参数无效,例如缺少 appKey、platform 为空、currentBuild 为负数。 |
404 |
AppKey 不存在、App 已停用、平台不存在/停用、渠道不存在/停用。 |
429 |
触发 IP/AppKey 限流。客户端应稍后重试。 |
500 |
服务端异常。客户端应降级为“不提示更新”,并记录日志。 |
错误响应使用 ASP.NET Core 标准 ProblemDetails 结构,常见格式如下:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.5",
"title": "Application not found",
"status": 404
}
5. 版本选择规则
服务端会在指定 appKey + platform + channel 下选择已经生效、未归档且构建号最高的版本。
规则如下:
- App 停用时,客户端接口返回
404。 - 草稿、归档版本不会返回给客户端。
- 定时发布版本在到达
scheduledAt之前不可见,到期后可被选中。 - 当前客户端
currentBuild >= 最新 buildNumber时,返回hasUpdate=false。 - 存在更新且版本标记了
forceUpdate=true时,返回forceUpdate=true。 - 存在更新且
currentBuild < minimumSupportedBuild时,返回forceUpdate=true。 stable、beta和自定义渠道互相隔离,不会串用版本。- Android、iOS、Windows、macOS、Linux 等平台互相隔离,不会串用版本。
6. App 端推荐流程
启动后或进入设置页时检查更新即可,不建议每次前后台切换都请求。
推荐流程:
- 读取当前客户端构建号
currentBuild。 - 根据当前包的平台和渠道组装请求。
- 请求失败时静默降级,不阻塞 App 启动。
hasUpdate=false时不提示。hasUpdate=true && forceUpdate=false时显示可取消的更新弹窗。hasUpdate=true && forceUpdate=true时显示不可取消的强制更新弹窗。- 用户确认后打开
downloadUrl,或进入系统下载流程。 - 如果返回了
sha256,下载安装包后做完整性校验。
7. JavaScript/TypeScript 示例
type UpdateResponse = {
hasUpdate: boolean
forceUpdate: boolean
release: null | {
id: string
versionName: string
buildNumber: number
downloadUrl: string
releaseNotes: string
sha256?: string | null
fileSize?: number | null
publishedAt: string
}
}
export async function checkUpdate() {
const baseUrl = 'https://your-domain.com'
const params = new URLSearchParams({
appKey: 'replace-with-app-key',
platform: 'android',
channel: 'stable',
currentBuild: String(100),
})
const response = await fetch(`${baseUrl}/api/client/v1/update?${params}`)
if (response.status === 404) return null
if (response.status === 429) throw new Error('检查更新过于频繁,请稍后再试')
if (!response.ok) throw new Error(`检查更新失败:${response.status}`)
const result = (await response.json()) as UpdateResponse
if (!result.hasUpdate || !result.release) return null
return result
}
8. Android Kotlin 示例
data class UpdateResponse(
val hasUpdate: Boolean,
val forceUpdate: Boolean,
val release: ReleaseInfo?
)
data class ReleaseInfo(
val id: String,
val versionName: String,
val buildNumber: Long,
val downloadUrl: String,
val releaseNotes: String,
val sha256: String?,
val fileSize: Long?,
val publishedAt: String
)
// 使用 OkHttp / Retrofit 均可,示例只展示 URL 组装
val url = HttpUrl.Builder()
.scheme("https")
.host("your-domain.com")
.addPathSegments("api/client/v1/update")
.addQueryParameter("appKey", "replace-with-app-key")
.addQueryParameter("platform", "android")
.addQueryParameter("channel", "stable")
.addQueryParameter("currentBuild", BuildConfig.VERSION_CODE.toString())
.build()
9. iOS Swift 示例
struct UpdateResponse: Decodable {
let hasUpdate: Bool
let forceUpdate: Bool
let release: ReleaseInfo?
}
struct ReleaseInfo: Decodable {
let id: String
let versionName: String
let buildNumber: Int64
let downloadUrl: String
let releaseNotes: String
let sha256: String?
let fileSize: Int64?
let publishedAt: String
}
var components = URLComponents(string: "https://your-domain.com/api/client/v1/update")!
components.queryItems = [
URLQueryItem(name: "appKey", value: "replace-with-app-key"),
URLQueryItem(name: "platform", value: "ios"),
URLQueryItem(name: "channel", value: "stable"),
URLQueryItem(name: "currentBuild", value: "100")
]
let (data, response) = try await URLSession.shared.data(from: components.url!)
let http = response as! HTTPURLResponse
if http.statusCode == 200 {
let result = try JSONDecoder().decode(UpdateResponse.self, from: data)
// 根据 result.hasUpdate / result.forceUpdate 展示更新弹窗
}
10. 后台发布注意事项
为了让客户端能正确收到更新,后台发布版本时请确认:
- App 处于启用状态。
- 平台和渠道处于启用状态。
buildNumber大于线上客户端的currentBuild。- 版本状态是已发布,或定时发布时间已经到期。
downloadUrl是可公开访问的 HTTP/HTTPS 绝对地址。- 强制更新策略按需设置:
forceUpdate或minimumSupportedBuild。
11. 本地调试
启动服务:
docker compose up -d
访问后台:
http://localhost:8080/
首次启动前配置引导管理员:
export Admin__BootstrapUsername='admin'
export Admin__BootstrapPassword='replace-with-a-random-password-of-at-least-12-characters'
首次登录后必须修改密码。正式环境使用 HTTPS,并保持 Admin__CookieSecure=true;不存在固定
默认密码,也不再通过请求头管理密钥登录。
检查更新示例:
curl "http://localhost:8080/api/client/v1/update?appKey=replace-with-app-key&platform=android&channel=stable¤tBuild=0"
停止服务:
docker compose down