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

如何在Kotlin Ktor路由间不存储数据传递API响应数据?

在Ktor路由间传递数据(无需存入数据库)

针对你要在/search/field路由获取API数据后,传递到/search/found路由展示的需求,这里提供几种无需数据库的可行方案:

方法1:用Flash Session(推荐,一次性传递场景)

Flash Session是Ktor的特性,数据仅在下一次请求中有效,之后自动清除,完美匹配你跳转后展示一次的需求。

步骤1:配置Flash Session

在你的Application.kt中添加Session配置,并定义存储数据的实体类:

import io.ktor.server.sessions.*
import io.ktor.server.application.*

// 定义存储书籍数据的Flash Session类
data class BookFlashSession(
    val title: String?,
    val author: String?,
    val publisher: String?,
    val pageCount: Int?
)

fun Application.configureSessions() {
    install(Sessions) {
        cookie<BookFlashSession>("BOOK_FLASH_SESSION") {
            cookie.path = "/"
            cookie.httpOnly = true // 防止前端JS读取,提升安全性
            cookie.secure = false // 生产环境建议设为true(HTTPS环境)
        }
    }
}

步骤2:修改Routing.kt传递和接收数据

route("search") {
    get {
        call.respond(FreeMarkerContent("search.ftl", model = null))
    }

    get("field") {
        // API请求获取数据的逻辑保持不变
        val title = volumeInfoObject?.get("title") as? String
        val author = authors?.get(0) as? String
        val publisher = volumeInfoObject?.get("publisher") as? String
        val pageCount = volumeInfoObject?.get("pageCount") as? Int

        client.close()
        // 将数据存入Flash Session
        call.sessions.set(BookFlashSession(title, author, publisher, pageCount))
        call.respondRedirect("/search/found")
    }

    get("found") {
        // 从Flash Session取出数据
        val bookSession = call.sessions.get<BookFlashSession>()
        // 构建传给FreeMarker的模型
        val model = mapOf(
            "book" to mapOf(
                "title" to bookSession?.title,
                "author" to bookSession?.author,
                "publisher" to bookSession?.publisher,
                "pageCount" to bookSession?.pageCount
            )
        )
        // 可选:手动清除Session(默认下次请求自动清除)
        call.sessions.clear<BookFlashSession>()
        call.respond(FreeMarkerContent("found.ftl", model = model))
    }
}

步骤3:修改Found.ftl渲染数据

<#-- @ftlvariable name="book" type="java.util.Map" -->
<#import "_layout.ftl" as layout />
<@layout.header>
<div>
    <div class="text-center">
        <h1 class="display-4">搜索结果</h1>
    </div>
    <div class="container">
        <div class="row">
            <div class="col-sm-3">
                <div class="img"><img src="/static/500x900.png" class="img-fluid" alt="书籍封面"></div>
            </div>
            <div class="col-sm-9">
                <div class="row">
                    <div class="col-8 col-sm-6"></div>
                    <div class="col-4 col-sm-6">
                        <form action="/save-book" method="post">
                            <legend>书籍信息</legend>
                            <div class="mb-3">
                                <label for="title" class="form-label">书名</label>
                                <input type="text" id="title" class="form-control" name="title" value="${book.title!}">
                            </div>
                            <div class="mb-3">
                                <label for="author" class="form-label">作者</label>
                                <input type="text" id="author" class="form-control" name="author" value="${book.author!}">
                            </div>
                            <div class="mb-3">
                                <label for="publisher" class="form-label">出版社</label>
                                <input type="text" id="publisher" class="form-control" name="publisher" value="${book.publisher!}">
                            </div>
                            <div class="mb-3">
                                <label for="pageCount" class="form-label">页数</label>
                                <input type="text" id="pageCount" class="form-control" name="pageCount" value="${book.pageCount!}">
                            </div>
                            <button type="submit" class="btn btn-primary">保存</button>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
</@layout.header>

方法2:用URL查询参数(适合简单、非敏感数据)

如果数据都是简单字符串/数字,也可以直接拼在跳转URL的查询参数里,缺点是数据会暴露在URL中,且长度有限制:

修改Routing.kt

import java.net.URLEncoder
import java.nio.charset.StandardCharsets

route("search") {
    // ... 其他路由逻辑

    get("field") {
        // 获取API数据逻辑不变
        val title = volumeInfoObject?.get("title") as? String
        val author = authors?.get(0) as? String
        val publisher = volumeInfoObject?.get("publisher") as? String
        val pageCount = volumeInfoObject?.get("pageCount") as? Int

        client.close()
        // 构建带查询参数的跳转URL
        val redirectUrl = buildString {
            append("/search/found?")
            title?.let { append("title=${URLEncoder.encode(it, StandardCharsets.UTF_8.name())}&") }
            author?.let { append("author=${URLEncoder.encode(it, StandardCharsets.UTF_8.name())}&") }
            publisher?.let { append("publisher=${URLEncoder.encode(it, StandardCharsets.UTF_8.name())}&") }
            pageCount?.let { append("pageCount=$it") }
        }.removeSuffix("&") // 移除末尾多余的&符号
        call.respondRedirect(redirectUrl)
    }

    get("found") {
        // 从查询参数中读取数据
        val title = call.request.queryParameters["title"]
        val author = call.request.queryParameters["author"]
        val publisher = call.request.queryParameters["publisher"]
        val pageCount = call.request.queryParameters["pageCount"]?.toIntOrNull()
        
        val model = mapOf(
            "book" to mapOf(
                "title" to title,
                "author" to author,
                "publisher" to publisher,
                "pageCount" to pageCount
            )
        )
        call.respond(FreeMarkerContent("found.ftl", model = model))
    }
}

方法3:用普通Session(需长期保留数据场景)

如果需要数据在用户会话中保留更长时间,可以用普通Session,但记得在保存到数据库后手动清除:

// 在Application.kt配置普通Session
data class BookSession(
    val title: String?,
    val author: String?,
    val publisher: String?,
    val pageCount: Int?
)

fun Application.configureSessions() {
    install(Sessions) {
        cookie<BookSession>("BOOK_SESSION") {
            cookie.path = "/"
            cookie.httpOnly = true
            cookie.secure = false
        }
    }
}

// Routing.kt中使用
get("field") {
    // 获取数据后存入Session
    call.sessions.set(BookSession(title, author, publisher, pageCount))
    call.respondRedirect("/search/found")
}

get("found") {
    val bookSession = call.sessions.get<BookSession>()
    val model = mapOf("book" to bookSession)
    // 保存到数据库后清除Session
    call.sessions.clear<BookSession>()
    call.respond(FreeMarkerContent("found.ftl", model = model))
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 19:50:26