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

如何在Ktor中获取用户通过Clikt传入的自定义配置变量

问题:Ktor中如何让后端服务获取Clikt传入的参数

我用Ktor做Web服务器,用户通过Kotlin Clikt命令行启动服务器时传入一个dbPath参数,后端处理部分REST请求时需要用到这个参数。目前Clikt类能正常接收参数并启动嵌入式服务器,但后端始终拿不到这个参数值,tiledbURI一直为null。

Clikt启动代码

class StartServer : CliktCommand(help = "Starts PHGv2 BrAPI Server") {

    private val myLogger = LogManager.getLogger(StartServer::class.java)
    val dbPath by option(help = "Full path to folder where TileDB datasets are stored.   ")
        .default("")
        .validate {
            require(it.isNotBlank()) {
                "--db-path must not be blank"
            }
        }

    override fun run() {
        setupDebugLogging()

        // Verify the uri is valid.  We only care about the hvcf dataset,
        // so check that one explicitly
        val hvcfExists = verifyURI(dbPath,"hvcf_dataset")
        if (!hvcfExists) {
            myLogger.error("hvcf_dataset does not exist in $dbPath.  Exiting.")
            return
        }

        // Create an Args list to pass to the server
        // This tells the endpoint code where the datasets are located.
        val dbUri = "-P:TILEDB_URI=${dbPath}"
        val args = arrayOf(dbUri)

        // commandLineEnvironment reads the application.config file
        embeddedServer(Netty, commandLineEnvironment(args)).start(wait = true)
    }
}

后端获取配置的代码

private val config = HoconApplicationConfig(ConfigFactory.load())

//val tiledbURI = environment.config.property("TILEDB_URI").getString()
val tiledbURI = config.property("TILEDB_URI").getString()

object SamplesService {

    private val myLogger = LogManager.getLogger(SamplesService::class.java)

    // Cached map of all taxa. Key is genoid mapped to Sample object
    private val taxa: Map<String, Sample> by lazy {
        taxaMap("${tiledbURI}/hvcf_dataset")
    }
     // ...其他代码
}

导入语句

import com.typesafe.config.ConfigFactory
import io.ktor.server.config.*

Hocon配置文件内容

callsPageSize=10
variantsPageSize=100

# For connecting to tiledb from ktor.  Users should un-comment
# and edit the TILEDB_URI variable to point to their tiledb folder
# When running junit tests,  replace ""/Users/lcj34" in the example below
# with the  path to your home directory.  The path should end with a /
# For other use cases, replace the path with the path to the tiledb folder
#TILEDB_URI="/Users/lcj34/temp/phgv2Tests/tempDir/testTileDBURI/"

# Server metadata params  You will need to fill these out to match your setup
contactEmail = "someone@cornell.edu"
documentationURL = "https://github.com/maize-genetics/phg_v2"
location = "Ithaca NY"
organizationName = "Institute for Genetic Diversity at Cornell University"
organizationURL = "https://www.maizegenetics.net/"
serverDescription = "Server to connect to the Maize PHG Tiledb through BrAPI calls."
serverName = "Maize  PHGv2"

ktor {
    deployment {
        port = 8080
        watch = [  build ]
    }
    application {
        modules = [ net.maizegenetics.phgv2.brapi.ApplicationKt.module ]
    }
}

注:配置文件中的TILEDB_URI处于注释状态,因为用户无法修改fat jar里的配置文件,仅用于开发者JUnit测试。

问题分析

  1. 直接用ConfigFactory.load()无法获取命令行参数:ConfigFactory.load()只会加载默认的配置文件(如application.conf),不会合并Clikt传入的-P:TILEDB_URI参数,因此tiledbURI始终为null。
  2. environment.config的访问范围问题:environment是Ktor应用模块函数的专属参数,只能在module函数内部访问,无法在顶层代码或object中直接调用,所以直接写environment.config会编译报错。

解决方案

方案一:通过Ktor模块传递配置(推荐)

利用Ktor的ApplicationEnvironment已经合并了命令行参数的特性,在模块中获取配置后传递给服务类:

  1. 修改SamplesService,添加可初始化的tiledbURI属性:
object SamplesService {
    lateinit var tiledbURI: String
    private val myLogger = LogManager.getLogger(SamplesService::class.java)
    
    // Cached map of all taxa. Key is genoid mapped to Sample object
    private val taxa: Map<String, Sample> by lazy {
        taxaMap("${tiledbURI}/hvcf_dataset")
    }
    // ...其他代码
}
  1. 在Ktor的module函数中,从environment.config获取参数并初始化服务类:
fun Application.module() {
    // 从合并后的配置中获取TILEDB_URI
    val tiledbURI = environment.config.property("TILEDB_URI").getString()
    // 初始化SamplesService的参数
    SamplesService.tiledbURI = tiledbURI
    
    // ...其他模块配置(如路由、插件等)
}

方案二:通过系统属性传递参数

在Clikt启动时将参数设置为系统属性,后端通过系统属性加载:

  1. 修改StartServer的run方法,添加系统属性设置:
override fun run() {
    setupDebugLogging()
    val hvcfExists = verifyURI(dbPath,"hvcf_dataset")
    if (!hvcfExists) {
        myLogger.error("hvcf_dataset does not exist in $dbPath.  Exiting.")
        return
    }
    
    // 将dbPath设置为系统属性
    System.setProperty("TILEDB_URI", dbPath)
    
    val dbUri = "-P:TILEDB_URI=${dbPath}"
    val args = arrayOf(dbUri)
    embeddedServer(Netty, commandLineEnvironment(args)).start(wait = true)
}
  1. 修改后端配置加载逻辑,优先读取系统属性:
// 先加载系统属性,再回退到配置文件
private val config = HoconApplicationConfig(
    ConfigFactory.systemProperties().withFallback(ConfigFactory.load())
)
val tiledbURI = config.property("TILEDB_URI").getString()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 14:06:04