kotlin Ktor类型中的自定义异常安全路由序列化失败

voj3qocg  于 2023-08-06  发布在  Kotlin
关注(0)|答案(1)|浏览(131)
@Resource("/articles/{version}")
data class Articles(val version: @Contextual ApiVersion)

fun main() {
    embeddedServer(CIO, port = 8080) {
        // plugins
        install(Resources) {
            serializersModule = SerializersModule { contextual(ApiVersion::class, ApiVersionSerializer) }
        }
        install(ContentNegotiation) { json() }
        install(StatusPages) {
            exception<InvalidApiVersion> { call, cause ->
                call.respond(HttpStatusCode.NotFound, cause.message)
            }
        }

        // Routes
        routing {
            get<Articles> { article ->
                call.respondText("articles version ${article.version}")
            }
        }
    }.start(true)
}

字符串
我有一个路由/article/${version},版本类型为enum class ApiVersion,为此我有一个自定义序列化器。

internal data class InvalidApiVersion(override val message: String) : Exception(message)

enum class ApiVersion(val version: String) {
    VERSION_1_0("v1.0"),
    VERSION_1_1("v1.1"),
    VERSION_1_2("v1.2"),
    VERSION_1_3("v1.3");
    companion object {
        fun fromString(versionString: String): ApiVersion =
            try { entries.associateBy(ApiVersion::version).getValue(versionString) }
            catch (e: Exception) { throw InvalidApiVersion("Invalid path: $versionString - is not a valid or supported api version.") }
    }
}

object ApiVersionSerializer : KSerializer<ApiVersion> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ApiVersion", PrimitiveKind.STRING)
    override fun deserialize(decoder: Decoder): ApiVersion =
        ApiVersion.fromString(decoder.decodeString())

    override fun serialize(encoder: Encoder, value: ApiVersion) {
        encoder.encodeString(value.version)
    }
}


现在,当我尝试使用无效版本访问路由时(例如:articles/v1.88)我从ktor获取BadRequestException而不是自定义异常InvalidApiVersion
为什么会这样?**
由于一些REST规范,我需要返回一个无效的API版本404 NotFound而不是400 BadRequest,实现这一点的一种方法是在StatusPages处理程序中捕获BadRequestException并返回404,但问题是我不想将所有BadRequest转换为404。

在这种情况下,如果我得到InvalidApiVersion而不是BadRequestException,那就太好了。有没有办法做到这一点?
更新:

@aleksei-tirman answer解决了上面代码中的问题,但在某些情况下,自定义异常可能会更深入,我的意思是,你可能需要执行cause?.cause?.cause?.cause来获取实际的根异常。我可能是错的,但下面的功能工作!

install(StatusPages) {
    exception<BadRequestException> { call, cause ->
        val root = getRootCause(cause)
        if (root is InvalidApiVersion) {
            call.respond(HttpStatusCode.NotFound, root.message)
        } else {
            throw cause
        }
    }
}
fun getRootCause(exception: Throwable): Throwable {
    var rootCause: Throwable = exception
    for (depth in 1 until 50) { rootCause.cause?.let { rootCause = it } ?: break }
    return rootCause
}

66bbxpm5

66bbxpm51#

由于BadRequestException的原因是InvalidApiVersion,您可以在StatusPages插件的异常处理程序中检查:

install(StatusPages) {
    exception<BadRequestException> { call, cause ->
        if (cause.cause is InvalidApiVersion) {
            call.respond(HttpStatusCode.NotFound, cause.cause?.message!!)
        } else {
            throw cause
        }
    }
}

字符串

相关问题