KTor Gson 带接口的数据转换
KTor Gson DataConversion with interface
我正在尝试在 KTor 中注册用于数据转换的 PubicKey
接口,这样我就可以像这样轻松地接收 public 密钥:
data class StoreRequest(
val publicKey: PublicKey
)
...
val publicKey: PublicKey = call.receive<StoreRequest>().publicKey
为此我使用了这个页面:https://ktor.io/servers/features/data-conversion.html
我注册了这个数据转换器:
convert<PublicKey> {
decode { values, _ ->
// When I add a breakpoint here it won't be reached.
values.singleOrNull()?.let { key ->
val encryptedKey = Base64.getDecoder().decode(key.split(" ")[1])
val inputStream = DataInputStream(ByteArrayInputStream(encryptedKey))
val format = String(ByteArray(inputStream.readInt()).also(inputStream::readFully))
if (format != "ssh-rsa") throw RuntimeException("Unsupported format")
val publicExponent = ByteArray(inputStream.readInt()).also(inputStream::readFully)
val modulus = ByteArray(inputStream.readInt()).also(inputStream::readFully)
val spec = RSAPublicKeySpec(BigInteger(modulus), BigInteger(publicExponent))
val keyFactory = KeyFactory.getInstance("RSA")
keyFactory.generatePublic(spec)
}
}
}
但出于某种原因,Gson 正在抱怨,因为我正在使用一个接口:
java.lang.RuntimeException: Unable to invoke no-args constructor for interface java.security.PublicKey. Register an InstanceCreator with Gson for this type may fix this problem`
所以我猜我需要为初始值创建一个 InstanceCreator
。
这是初始PublicKey
class我创建的:
class PkTest : PublicKey {
override fun getAlgorithm(): String = ""
override fun getEncoded(): ByteArray = ByteArray(0)
override fun getFormat(): String = ""
}
...
install(ContentNegotiation) {
gson {
setPrettyPrinting()
registerTypeAdapter(PublicKey::class.java, InstanceCreator<PublicKey> { PkTest() // This is called when I add a breakpoint })
}
}
但这也不行!这是我得到的异常:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 2 column 16 path $.publicKey
出于某种原因,当我提供这个时,它期望提供的密钥是一个 JSON 对象:
{
"publicKey": "ssh-rsa AAAAB3NzaC1yc2EABAADAQABAAACAQDBPL2s+25Ank3zS6iHUoVk0tS63dZM0LzAaniiDon0tdWwq4vcL4+fV8BsAEcpMeijS92JhDDc9FccXlHbdDcmd6c4ITOt9h9xxhIefGsi1FTVJ/EjVtbqF5m0bu7ruIMGvuP1p5s004roHx9y0UdHvD/yNWLISMhy4nio6jLailIj3FS53Emj1WRNsOrpja3LzPXzhuuj6YnD9yfByT7iGZipxkmleaXrknChPClLI9uhcqtAzBLdd0NVTJLOt/3+d1cSNwdBw9e53wJvpEmH+P8UOZd+oV/y7cHIej4jQpBXVvpJR1Yaluh5RuxY90B0hSescUAj5g/3HVPpR/gE7op6i9Ab//0iXF15uWGlGzipI4lA2/wYEtv8swTjmdCTMNcTDw/1huTDEzZjghIKVpskHde/Lj416c7eSByLqsMg2OhlZGChKznpIjhuNRXz93DwqKuIKvJKSnhqaJDxmDGfG7nlQ/eTwGeAZ6VR50yMPiRTIpuYd767+Nsg486z7p0pnKoBlL6ffTbfeolUX2b6Nb9ZIOxJdpCSNTQRKQ50p4Y3S580cUM1Y2EfjlfIQG1JdmTQYB75AZXi/cB2PvScmF0bXRoj7iHg4lCnSUvRprWA0xbwzCW/wjNqw6MyRX42FFlvSRrmfaxGZxKYbmk3TzBv+Fp+CADPqQm3OQ== dynmem@memmen.frl"
}
我怎样才能 'trick' GSON 接受 PublicKey
的字符串?还是我做错了什么?
我认为 GSON 想要将一个 JSON 对象序列化为一个 PublicKey
。但我希望它接受 String
。我认为这应该是可能的,因为 class 像 UUID
和 Date
工作得很好......
我解决了!我没有使用 InstanceCreator
,而是使用了 JsonDeserializer
:
install(ContentNegotiation) {
gson {
setPrettyPrinting()
registerTypeAdapter(PublicKey::class.java, JsonDeserializer<PublicKey> { json, _, _ ->
// TODO some type checking.
val key = json.asString
val encryptedKey = Base64.getDecoder().decode(key.split(" ")[1])
val inputStream = DataInputStream(ByteArrayInputStream(encryptedKey))
val format = String(ByteArray(inputStream.readInt()).also(inputStream::readFully))
if (format != "ssh-rsa") throw RuntimeException("Unsupported format")
val publicExponent = ByteArray(inputStream.readInt()).also(inputStream::readFully)
val modulus = ByteArray(inputStream.readInt()).also(inputStream::readFully)
val spec = RSAPublicKeySpec(BigInteger(modulus), BigInteger(publicExponent))
val keyFactory = KeyFactory.getInstance("RSA")
keyFactory.generatePublic(spec)
})
}
}
这部分可以完全删除:
convert<PublicKey> {
decode { values, _ ->
...
}
}
我正在尝试在 KTor 中注册用于数据转换的 PubicKey
接口,这样我就可以像这样轻松地接收 public 密钥:
data class StoreRequest(
val publicKey: PublicKey
)
...
val publicKey: PublicKey = call.receive<StoreRequest>().publicKey
为此我使用了这个页面:https://ktor.io/servers/features/data-conversion.html
我注册了这个数据转换器:
convert<PublicKey> {
decode { values, _ ->
// When I add a breakpoint here it won't be reached.
values.singleOrNull()?.let { key ->
val encryptedKey = Base64.getDecoder().decode(key.split(" ")[1])
val inputStream = DataInputStream(ByteArrayInputStream(encryptedKey))
val format = String(ByteArray(inputStream.readInt()).also(inputStream::readFully))
if (format != "ssh-rsa") throw RuntimeException("Unsupported format")
val publicExponent = ByteArray(inputStream.readInt()).also(inputStream::readFully)
val modulus = ByteArray(inputStream.readInt()).also(inputStream::readFully)
val spec = RSAPublicKeySpec(BigInteger(modulus), BigInteger(publicExponent))
val keyFactory = KeyFactory.getInstance("RSA")
keyFactory.generatePublic(spec)
}
}
}
但出于某种原因,Gson 正在抱怨,因为我正在使用一个接口:
java.lang.RuntimeException: Unable to invoke no-args constructor for interface java.security.PublicKey. Register an InstanceCreator with Gson for this type may fix this problem`
所以我猜我需要为初始值创建一个 InstanceCreator
。
这是初始PublicKey
class我创建的:
class PkTest : PublicKey {
override fun getAlgorithm(): String = ""
override fun getEncoded(): ByteArray = ByteArray(0)
override fun getFormat(): String = ""
}
...
install(ContentNegotiation) {
gson {
setPrettyPrinting()
registerTypeAdapter(PublicKey::class.java, InstanceCreator<PublicKey> { PkTest() // This is called when I add a breakpoint })
}
}
但这也不行!这是我得到的异常:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 2 column 16 path $.publicKey
出于某种原因,当我提供这个时,它期望提供的密钥是一个 JSON 对象:
{
"publicKey": "ssh-rsa AAAAB3NzaC1yc2EABAADAQABAAACAQDBPL2s+25Ank3zS6iHUoVk0tS63dZM0LzAaniiDon0tdWwq4vcL4+fV8BsAEcpMeijS92JhDDc9FccXlHbdDcmd6c4ITOt9h9xxhIefGsi1FTVJ/EjVtbqF5m0bu7ruIMGvuP1p5s004roHx9y0UdHvD/yNWLISMhy4nio6jLailIj3FS53Emj1WRNsOrpja3LzPXzhuuj6YnD9yfByT7iGZipxkmleaXrknChPClLI9uhcqtAzBLdd0NVTJLOt/3+d1cSNwdBw9e53wJvpEmH+P8UOZd+oV/y7cHIej4jQpBXVvpJR1Yaluh5RuxY90B0hSescUAj5g/3HVPpR/gE7op6i9Ab//0iXF15uWGlGzipI4lA2/wYEtv8swTjmdCTMNcTDw/1huTDEzZjghIKVpskHde/Lj416c7eSByLqsMg2OhlZGChKznpIjhuNRXz93DwqKuIKvJKSnhqaJDxmDGfG7nlQ/eTwGeAZ6VR50yMPiRTIpuYd767+Nsg486z7p0pnKoBlL6ffTbfeolUX2b6Nb9ZIOxJdpCSNTQRKQ50p4Y3S580cUM1Y2EfjlfIQG1JdmTQYB75AZXi/cB2PvScmF0bXRoj7iHg4lCnSUvRprWA0xbwzCW/wjNqw6MyRX42FFlvSRrmfaxGZxKYbmk3TzBv+Fp+CADPqQm3OQ== dynmem@memmen.frl"
}
我怎样才能 'trick' GSON 接受 PublicKey
的字符串?还是我做错了什么?
我认为 GSON 想要将一个 JSON 对象序列化为一个 PublicKey
。但我希望它接受 String
。我认为这应该是可能的,因为 class 像 UUID
和 Date
工作得很好......
我解决了!我没有使用 InstanceCreator
,而是使用了 JsonDeserializer
:
install(ContentNegotiation) {
gson {
setPrettyPrinting()
registerTypeAdapter(PublicKey::class.java, JsonDeserializer<PublicKey> { json, _, _ ->
// TODO some type checking.
val key = json.asString
val encryptedKey = Base64.getDecoder().decode(key.split(" ")[1])
val inputStream = DataInputStream(ByteArrayInputStream(encryptedKey))
val format = String(ByteArray(inputStream.readInt()).also(inputStream::readFully))
if (format != "ssh-rsa") throw RuntimeException("Unsupported format")
val publicExponent = ByteArray(inputStream.readInt()).also(inputStream::readFully)
val modulus = ByteArray(inputStream.readInt()).also(inputStream::readFully)
val spec = RSAPublicKeySpec(BigInteger(modulus), BigInteger(publicExponent))
val keyFactory = KeyFactory.getInstance("RSA")
keyFactory.generatePublic(spec)
})
}
}
这部分可以完全删除:
convert<PublicKey> {
decode { values, _ ->
...
}
}