以下是一个使用 Kotlin 和 Ktor 构建 Android 后端 API 的详细示例,包含常见功能实现:
1. 项目搭建 (build.gradle.kts)
plugins {
application
kotlin("jvm") version "1.9.0"
id("io.ktor.plugin") version "2.3.4"
id("org.jetbrains.kotlin.plugin.serialization") version "1.9.0"
}
dependencies {
implementation("io.ktor:ktor-server-core")
implementation("io.ktor:ktor-server-netty")
implementation("io.ktor:ktor-server-content-negotiation")
implementation("io.ktor:ktor-serialization-kotlinx-json")
implementation("io.ktor:ktor-server-status-pages")
implementation("io.ktor:ktor-server-auth")
implementation("io.ktor:ktor-server-auth-jwt")
// 数据库
implementation("org.jetbrains.exposed:exposed-core:0.41.1")
implementation("org.jetbrains.exposed:exposed-jdbc:0.41.1")
implementation("com.h2database:h2:2.1.214")
// 日志
implementation("ch.qos.logback:logback-classic:1.4.7")
// 测试
testImplementation("io.ktor:ktor-server-test-host")
}
2. 主入口文件 (Application.kt)
fun main() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
install(ContentNegotiation) {
json()
}
install(StatusPages) {
exception<Throwable> { call, cause ->
when (cause) {
is ApiException -> {
call.respond(
HttpStatusCode.fromValue(cause.statusCode),
ErrorResponse(cause.message ?: "Unknown error")
)
}
else -> {
call.respond(
HttpStatusCode.InternalServerError,
ErrorResponse("Internal server error")
)
}
}
}
}
install(Authentication) {
jwt {
realm = "My API"
verifier(JwtConfig.verifier)
validate { credential ->
if (credential.payload.getClaim("userId").asString() != "") {
JWTPrincipal(credential.payload)
} else {
null
}
}
}
}
DatabaseFactory.init()
configureRouting()
}.start(wait = true)
}
class ApiException(
val statusCode: Int,
override val message: String
) : RuntimeException(message)
data class ErrorResponse(val message: String)
3. 路由配置 (Routing.kt)
fun Application.configureRouting() {
routing {
// 公共路由
route("/api") {
post("/register") {
// 用户注册逻辑
}
post("/login") {
// 用户登录逻辑
}
// 需要认证的路由
authenticate {
route("/users") {
get {
// 获取所有用户
}
get("{id}") {
// 获取单个用户
}
}
route("/posts") {
get {
// 获取所有帖子
}
post {
// 创建新帖子
}
put("{id}") {
// 更新帖子
}
}
}
}
}
}
4. 数据库配置 (Database.kt)
object DatabaseFactory {
fun init() {
val database = Database.connect(
url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1",
driver = "org.h2.Driver"
)
transaction {
SchemaUtils.create(Users, Posts)
}
}
}
object Users : Table() {
val id = uuid("id").primaryKey().autoGenerate()
val username = varchar("username", 50).uniqueIndex()
val password = varchar("password", 100)
val createdAt = datetime("created_at")
}
object Posts : Table() {
val id = uuid("id").primaryKey().autoGenerate()
val title = varchar("title", 100)
val content = text("content")
val authorId = (uuid("author_id") references Users.id)
val createdAt = datetime("created_at")
}
5. JWT 配置 (JwtConfig.kt)
object JwtConfig {
private const val SECRET = "your_secret_key_here"
private const val ISSUER = "your_issuer"
private const val AUDIENCE = "your_audience"
private const val EXPIRATION = 3600L // 1小时
val verifier = JWT
.require(Algorithm.HMAC256(SECRET))
.withIssuer(ISSUER)
.withAudience(AUDIENCE)
.build()
fun generateToken(userId: UUID): String {
return JWT.create()
.withIssuer(ISSUER)
.withAudience(AUDIENCE)
.withClaim("userId", userId.toString())
.withExpiresAt(Date(System.currentTimeMillis() + EXPIRATION * 1000))
.sign(Algorithm.HMAC256(SECRET))
}
}
6. 数据模型 (Models.kt)
@Serializable
data class UserRequest(
val username: String,
val password: String
)
@Serializable
data class UserResponse(
val id: String,
val username: String,
val createdAt: String
)
@Serializable
data class PostRequest(
val title: String,
val content: String
)
@Serializable
data class PostResponse(
val id: String,
val title: String,
val content: String,
val authorId: String,
val createdAt: String
)
7. 示例端点实现 (UserRoutes.kt)
fun Route.userRoutes() {
route("/users") {
get {
val users = transaction {
Users.selectAll().map { row ->
UserResponse(
id = row[Users.id].toString(),
username = row[Users.username],
createdAt = row[Users.createdAt].toString()
)
}
}
call.respond(users)
}
get("{id}") {
val id = call.parameters["id"] ?: throw ApiException(400, "Missing ID")
val user = transaction {
Users.select { Users.id eq UUID.fromString(id) }.singleOrNull()
} ?: throw ApiException(404, "User not found")
call.respond(
UserResponse(
id = user[Users.id].toString(),
username = user[Users.username],
createdAt = user[Users.createdAt].toString()
)
)
}
}
}
8. 测试示例 (UserRoutesTest.kt)
class UserRoutesTest {
@Test
fun testGetUsers() = testApplication {
application {
configureRouting()
}
client.get("/api/users").apply {
assertEquals(HttpStatusCode.OK, status)
val users = Json.decodeFromString<List<UserResponse>>(bodyAsText())
assertTrue(users.isNotEmpty())
}
}
}
关键功能说明:
- 认证系统:使用 JWT 进行身份验证
- 数据库集成:使用 Exposed ORM 和 H2 数据库
- 错误处理:统一异常处理中间件
- RESTful API:实现标准 CRUD 操作
- 内容协商:支持 JSON 序列化
- 路由分层:模块化路由配置
运行与测试:
- 启动应用:
./gradlew run
- 访问 API:
GET http://localhost:8080/api/users
POST http://localhost:8080/api/register
- 使用 Postman 或 curl 进行测试
扩展建议:
- 添加 Swagger/OpenAPI 文档
- 实现文件上传功能
- 添加 Redis 缓存层
- 集成邮件服务
- 实现分页和过滤功能
- 添加速率限制
- 配置 CORS 支持
注意:实际部署时需要:
- 使用生产级数据库(如 PostgreSQL)
- 配置 HTTPS
- 设置环境变量管理敏感信息
- 添加监控和日志系统
- 考虑容器化部署(Docker)