Spring Kotlin coRouter配置失效返回404,GraphQL路由无法生成求助
问题描述
搭建集成graphql-kotlin的Spring Kotlin服务器,coRouter配置完全失效,所有路由返回404,导致GraphQL无法生成对应路由。已尝试更换Spring、graphql-kotlin版本,调整依赖库,甚至编写了简单的coRouter测试路由(返回字符串),仍无法解决。
相关代码
IndexController.kotlin
package com.project.client.general import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.web.reactive.function.server.bodyValueAndAwait import org.springframework.web.reactive.function.server.coRouter @Configuration class IndexConfig { @Bean fun mainRouter() = coRouter { "/" { ok().bodyValueAndAwait(getIndex().trimIndent()) } } suspend fun getIndex(): String { return "Welcome to the API!" } }
build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { // id("org.springframework.boot") version "3.0.3" // id("io.spring.dependency-management") version "1.1.0" // kotlin("jvm") version "1.7.22" // kotlin("plugin.spring") version "1.7.22" // kotlin("plugin.jpa") version "1.7.22" id("org.springframework.boot") version "2.7.5" id("io.spring.dependency-management") version "1.0.15.RELEASE" kotlin("jvm") version "1.6.21" kotlin("plugin.spring") version "1.6.21" kotlin("plugin.jpa") version "1.6.21" } group = "com.project.client" version = "0.0.1-SNAPSHOT" java.sourceCompatibility = JavaVersion.VERSION_17 configurations { compileOnly { extendsFrom(configurations.annotationProcessor.get()) } } repositories { mavenCentral() } extra["springShellVersion"] = "3.0.0" dependencies { // Spring implementation("org.springframework.boot:spring-boot-starter-actuator") implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.hibernate.validator:hibernate-validator:8.0.0.Final") developmentOnly("org.springframework.boot:spring-boot-devtools") implementation("org.springframework.boot:spring-boot-starter-validation") testImplementation("org.springframework.boot:spring-boot-starter-test") // Webflux implementation("org.springframework.boot:spring-boot-starter-webflux") testImplementation("io.projectreactor:reactor-test") implementation("io.projectreactor.kotlin:reactor-kotlin-extensions") implementation("io.projectreactor.addons:reactor-extra") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core") // Jpa + db implementation("org.flywaydb:flyway-core") implementation("org.springframework.boot:spring-boot-starter-data-jpa") // implementation("org.springframework.data:spring-data-r2dbc") // implementation("io.r2dbc:r2dbc-postgresql") runtimeOnly("org.postgresql:postgresql") // GraphQL implementation("com.expediagroup:graphql-kotlin-spring-server:7.0.0-alpha.3") implementation("com.expediagroup:graphql-kotlin-hooks-provider:7.0.0-alpha.3") implementation ("com.graphql-java:graphql-java-extended-scalars:20.0") // Security // implementation("org.springframework.boot:spring-boot-starter-oauth2-client") // implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server") // implementation("org.springframework.boot:spring-boot-starter-security") // testImplementation("org.springframework.security:spring-security-test") // implementation("com.auth0:auth0-spring-security-api:1.5.3") // implementation("com.auth0:auth0:2.0.0") implementation("org.springframework.boot:spring-boot-starter-quartz") // Email implementation("org.springframework.boot:spring-boot-starter-thymeleaf") // implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity6") // implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity5") implementation("org.springframework.boot:spring-boot-starter-mail") // Kotlin implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") compileOnly("org.projectlombok:lombok") annotationProcessor("org.projectlombok:lombok") // Logging runtimeOnly("io.micrometer:micrometer-registry-datadog") implementation("io.github.oshai:kotlin-logging-jvm:4.0.0-beta-22") // compileOnly(project(":shared")) } dependencyManagement { imports { mavenBom("org.springframework.shell:spring-shell-dependencies:${property("springShellVersion")}") } } tasks.withType<KotlinCompile> { kotlinOptions { freeCompilerArgs = listOf("-Xjsr305=strict") jvmTarget = "17" } } tasks.withType<Test> { useJUnitPlatform() }
SpringApplication.kotlin
package com.project.client import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.runApplication import org.springframework.context.ConfigurableApplicationContext import org.springframework.data.jpa.repository.config.EnableJpaRepositories import org.springframework.scheduling.annotation.EnableAsync import org.springframework.scheduling.annotation.EnableScheduling @SpringBootApplication @EntityScan @EnableJpaRepositories @EnableScheduling @EnableAsync class ClientBackendApplication { companion object { var context: ConfigurableApplicationContext? = null } } fun main(args: Array<String>) { ClientBackendApplication.context = runApplication<ClientBackendApplication>(*args) }
解决步骤
1. 移除Servlet栈依赖,启用Reactive栈
你的build.gradle.kts中同时引入了spring-boot-starter-web(Servlet栈)和spring-boot-starter-webflux(Reactive栈)。Spring Boot默认优先使用Servlet栈,导致coRouter(Reactive路由)无法被加载。必须移除spring-boot-starter-web依赖:
// 删除这一行 // implementation("org.springframework.boot:spring-boot-starter-web")
2. 修正coRouter路由DSL语法
当前路由写法未指定HTTP请求方法,属于语法错误。需要明确指定请求方法(比如GET):
@Bean fun mainRouter() = coRouter { GET("/") { ok().bodyValueAndAwait(getIndex().trimIndent()) } }
3. 更换稳定版graphql-kotlin
你使用的7.0.0-alpha.3是测试版本,存在兼容性风险。针对Spring Boot 2.7.x,建议使用graphql-kotlin 6.x稳定版:
// 替换为稳定版 implementation("com.expediagroup:graphql-kotlin-spring-server:6.4.1") implementation("com.expediagroup:graphql-kotlin-hooks-provider:6.4.1")
4. 验证依赖版本兼容性
确保hibernate-validator版本与Spring Boot 2.7.x兼容。Spring Boot 2.7.x默认依赖hibernate-validator 6.x,你使用的8.0.0.Final是Jakarta EE 9+版本,与Spring Boot 2.7.x(Jakarta EE 8)不兼容,需要降级:
// 替换为兼容版本 implementation("org.hibernate.validator:hibernate-validator:6.2.5.Final")
5. 确认组件扫描范围
@SpringBootApplication默认扫描当前包(com.project.client)及子包,IndexConfig在com.project.client.general下,属于子包,扫描范围没问题。如果仍有问题,可以显式指定扫描包:
@SpringBootApplication(scanBasePackages = ["com.project.client"])
验证
完成上述修改后,重启服务器,访问http://localhost:8080应该能看到返回的"Welcome to the API!",同时graphql-kotlin的默认路由(如/graphql、/graphiql)也会正常生效。
内容的提问来源于stack exchange,提问作者Bartosz Prządka

