KTOR服务器拒绝插件注册请求问题排查与解决
问题:Ktor服务器返回空响应体,无法解析请求对象(Minecraft服务器注册场景)
我正在开发一款Ktor服务器,用于支持玩家使用游戏内货币交易IG物品,要求玩家通过Minecraft服务器完成注册(借助MC昵称发放物品)。但从Minecraft服务器发送注册请求时,Ktor服务器返回空响应体,无法将请求转换为目标对象;用Postman测试则可正常响应执行。
错误日志
[08:54:25] [Server thread/WARN]: java.lang.NullPointerException: Cannot invoke "fr.plaglefleau.inventorytojson.models.ClientRegisterReceive.isRegister()" because "clientRegisterReceive" is null [08:54:25] [Server thread/WARN]: at fr.plaglefleau.inventorytojson.listeners.ConnectListener$2.run(ConnectListener.java:214) [08:54:25] [Server thread/WARN]: at org.bukkit.craftbukkit.v1_20_R1.scheduler.CraftTask.run(CraftTask.java:82) [08:54:25] [Server thread/WARN]: at org.bukkit.craftbukkit.v1_20_R1.scheduler.CraftScheduler.mainThreadHeartbeat(CraftScheduler.java:415) [08:54:25] [Server thread/WARN]: at net.minecraft.server.MinecraftServer.b(MinecraftServer.java:1257) [08:54:25] [Server thread/WARN]: at net.minecraft.server.dedicated.DedicatedServer.b(DedicatedServer.java:388) [08:54:25] [Server thread/WARN]: at net.minecraft.server.MinecraftServer.a(MinecraftServer.java:1198) [08:54:25] [Server thread/WARN]: at net.minecraft.server.MinecraftServer.w(MinecraftServer.java:1015) [08:54:25] [Server thread/WARN]: at net.minecraft.server.MinecraftServer.lambda$0(MinecraftServer.java:304) [08:54:25] [Server thread/WARN]: at java.base/java.lang.Thread.run(Thread.java:833)
Minecraft端注册请求代码
public void register(Player player, String message) { String[] register = message.replace(">","").replace("<","").split(";"); String json = new Gson().toJson( new Register( player.getName(), register[0], register[1] ) ); System.out.println(ConsoleColor.YELLOW + json + ConsoleColor.RESET); new BukkitRunnable() { public void run() { URI uri = URI.create("http://127.0.0.1/api/client/register"); HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder(uri) .POST(HttpRequest.BodyPublishers.ofString(json)) .header("Authorization", "Bearer " + Token.TOKEN) .build(); try { //send and get the response HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(ConsoleColor.YELLOW + response.body() + ConsoleColor.RESET); if(response.body().isBlank()) { System.out.println(ConsoleColor.YELLOW + "The body is Blank" + ConsoleColor.RESET); } ClientRegisterReceive clientRegisterReceive = new Gson().fromJson(response.body(), ClientRegisterReceive.class); if(clientRegisterReceive.isRegister()) { player.sendMessage(ChatColor.GREEN + clientRegisterReceive.getMessage() + "\nWelcome " + player.getName() + ChatColor.RESET); registerPlayers.remove(player.getUniqueId()); players.remove(player.getUniqueId()); } else { player.sendMessage(ChatColor.DARK_RED + clientRegisterReceive.getMessage() + ChatColor.RESET); player.sendMessage(ChatColor.YELLOW + "Please type your password and email like this : <password>;<email>" + ChatColor.RESET); } } catch (Exception e) { e.printStackTrace(); } } }.runTask(this.plugin); }
Ktor服务器日志
2023-07-09 09:07:52.911 [eventLoopGroupProxy-4-1] TRACE io.ktor.server.sessions.Sessions - Sessions found for /api/client/register: connect 2023-07-09 09:07:52.966 [eventLoopGroupProxy-4-1] TRACE io.ktor.routing.Routing - Trace for [api, client, register] /, segment:0 -> SUCCESS @ / /, segment:0 -> SUCCESS @ / /(method:GET), segment:0 -> FAILURE "Selector didn't match" @ /(method:GET) /connect, segment:0 -> FAILURE "Selector didn't match" @ /connect /register, segment:0 -> FAILURE "Selector didn't match" @ /register /(authenticate user-session), segment:0 -> SUCCESS @ /(authenticate user-session) /(authenticate user-session)/profile, segment:0 -> FAILURE "Selector didn't match" @ /(authenticate user-session)/profile /(authenticate user-session)/stores, segment:0 -> FAILURE "Selector didn't match" @ /(authenticate user-session)/stores /(authenticate user-session)/editUser, segment:0 -> FAILURE "Selector didn't match" @ /(authenticate user-session)/editUser /(authenticate user-session)/editSalary, segment:0 -> FAILURE "Selector didn't match" @ /(authenticate user-session)/editSalary /(authenticate user-session)/editMember, segment:0 -> FAILURE "Selector didn't match" @ /(authenticate user-session)/editMember /(authenticate user-session)/editStock, segment:0 -> FAILURE "Selector didn't match" @ /(authenticate user-session)/editStock /(authenticate user-session)/bailoutStock, segment:0 -> FAILURE "Selector didn't match" @ /(authenticate user-session)/bailoutStock /(authenticate user-session)/addStock, segment:0 -> FAILURE "Selector didn't match" @ /(authenticate user-session)/addStock /(authenticate user-session)/ajouterUnMembre, segment:0 -> FAILURE "Selector didn't match" @ /(authenticate user-session)/ajouterUnMembre /(authenticate user-session)/createShop, segment:0 -> FAILURE "Selector didn't match" @ /(authenticate user-session)/createShop /(authenticate user-session)/removeMember, segment:0 -> FAILURE "Selector didn't match" @ /(authenticate user-session)/removeMember /(authenticate user-session)/disconnect, segment:0 -> FAILURE "Selector didn't match" @ /(authenticate user-session)/disconnect /assets, segment:0 -> FAILURE "Selector didn't match" @ /assets /(authenticate api-bearer), segment:0 -> SUCCESS @ /(authenticate api-bearer) /(authenticate api-bearer)/api, segment:1 -> SUCCESS @ /(authenticate api-bearer)/api /(authenticate api-bearer)/api/, segment:1 -> SUCCESS @ /(authenticate api-bearer)/api/ /(authenticate api-bearer)/api/client, segment:2 -> SUCCESS @ /(authenticate api-bearer)/api/client /(authenticate api-bearer)/api/client/, segment:2 -> SUCCESS @ /(authenticate api-bearer)/api/client/ /(authenticate api-bearer)/api/client/connect, segment:2 -> FAILURE "Selector didn't match" @ /(authenticate api-bearer)/api/client/connect /(authenticate api-bearer)/api/client/inventory, segment:2 -> FAILURE "Selector didn't match" @ /(authenticate api-bearer)/api/client/inventory /(authenticate api-bearer)/api/client/register, segment:3 -> SUCCESS @ /(authenticate api-bearer)/api/client/register /(authenticate api-bearer)/api/client/register/(method:POST), segment:3 -> SUCCESS @ /(authenticate api-bearer)/api/client/register/(method:POST) /(authenticate api-bearer)/api/client/debit, segment:2 -> FAILURE "Selector didn't match" @ /(authenticate api-bearer)/api/client/debit /(authenticate api-bearer)/api/client/credit, segment:2 -> FAILURE "Selector didn't match" @ /(authenticate api-bearer)/api/client/credit /(authenticate api-bearer)/api/client/pay, segment:2 -> FAILURE "Selector didn't match" @ /(authenticate api-bearer)/api/client/pay /(authenticate api-bearer)/api/client/updateInventory, segment:2 -> FAILURE "Selector didn't match" @ /(authenticate api-bearer)/api/client/updateInventory /(authenticate api-bearer)/api/store, segment:1 -> FAILURE "Selector didn't match" @ /(authenticate api-bearer)/api/store Matched routes: "" -> "(authenticate api-bearer)" -> "api" -> "<slash>" -> "client" -> "<slash>" -> "register" -> "(method:POST)" Route resolve result: SUCCESS @ /(authenticate api-bearer)/api/client/register/(method:POST) 2023-07-09 09:07:52.985 [eventLoopGroupProxy-4-1] TRACE io.ktor.server.auth.Authentication - Trying to authenticate /api/client/register with api-bearer 2023-07-09 09:07:53.008 [eventLoopGroupProxy-4-1] TRACE io.ktor.server.auth.Authentication - Authentication succeeded for /api/client/register with provider io.ktor.server.auth.BearerAuthenticationProvider@5adad75b 2023-07-09 09:07:53.084 [eventLoopGroupProxy-4-1] TRACE i.k.server.engine.DefaultTransform - No Default Transformations found for class io.ktor.utils.io.ByteBufferChannel and expected type TypeInfo(type=class fr.plaglefleau.models.api.client.receive.RegisterReceive, reifiedType=class fr.plaglefleau.models.api.client.receive.RegisterReceive, kotlinType=fr.plaglefleau.models.api.client.receive.RegisterReceive) for call /api/client/register 2023-07-09 09:07:53.121 [eventLoopGroupProxy-4-1] TRACE i.k.s.p.c.ContentNegotiation - Skipping content converter for request type class fr.plaglefleau.models.api.client.receive.RegisterReceive because content type */* does not match application/json 2023-07-09 09:07:53.121 [eventLoopGroupProxy-4-1] TRACE i.k.s.p.c.ContentNegotiation - No suitable content converter found for request type class fr.plaglefleau.models.api.client.receive.RegisterReceive 2023-07-09 09:07:53.138 [eventLoopGroupProxy-4-1] TRACE io.ktor.server.sessions.Sessions - Sending session data for /api/client/register: connect 2023-07-09 09:07:53.141 [eventLoopGroupProxy-4-1] TRACE i.k.s.p.c.ContentNegotiation - Skipping response body transformation from HttpStatusCode to OutgoingContent for the POST /api/client/register request because the HttpStatusCode type is ignored. See [ContentNegotiationConfig::ignoreType].
请求JSON示例
{ "username":"PlagLeFleau", "password":"PlagLeFleau0745", "email":"louisdufourd@gmail.com" }
问题原因
从Ktor服务器日志中的关键信息可以定位问题:
Skipping content converter for request type class fr.plaglefleau.models.api.client.receive.RegisterReceive because content type */* does not match application/json
Minecraft端发送请求时,没有在请求头中声明Content-Type: application/json,导致Ktor的ContentNegotiation插件无法识别请求体的格式,找不到合适的转换器解析请求,最终返回空响应体。客户端解析空响应时,就会出现NullPointerException。
修复方案
在构造HttpRequest时,添加Content-Type请求头,指定为application/json:
HttpRequest request = HttpRequest.newBuilder(uri) .POST(HttpRequest.BodyPublishers.ofString(json)) .header("Authorization", "Bearer " + Token.TOKEN) .header("Content-Type", "application/json") .build();
内容的提问来源于stack exchange,提问作者Plag
相关产品推荐
相关产品推荐

