如何在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
相关产品推荐
相关产品推荐

