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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 22:22:00