Skip to content

Malformed JSON request body results in 500 instead of 400 for Vert.x JsonObject/JsonArray, JSON-B and JSON-P bodies #57823

Open

Description

@pschaub

Describe the bug

In Quarkus REST, a malformed JSON request body results in 500 Internal Server Error instead of 400 Bad Request for some body types:

  1. quarkus-rest-jackson with io.vertx.core.json.JsonObject / JsonArray as body. VertxJsonObjectBasicMessageBodyReader calls new JsonObject(String), and the resulting DecodeException is not handled. The same body bound to a Map correctly returns 400.
  2. quarkus-rest-jsonb with a POJO as body. JsonbMessageBodyReader only checks the JsonbException for empty input and otherwise rethrows it.
  3. JSON-P types (jakarta.json.JsonObject, JsonArray, …) as body. The ServerJson*Handler classes do not handle JsonParsingException.

In all cases the exception ends up in QuarkusErrorHandler.

Expected behavior

400 Bad Request, as for POJO/Map/JsonNode bodies with quarkus-rest-jackson (see also #52693).

Actual behavior

500 Internal Server Error, with these log entries:

ERROR [io.quarkus.vertx.http.runtime.QuarkusErrorHandler] (executor-thread-2) HTTP Request to /json/object failed, error id: ...: io.vertx.core.json.DecodeException: Failed to decode:Unexpected end-of-input within/between Object entries
ERROR [io.quarkus.vertx.http.runtime.QuarkusErrorHandler] (executor-thread-2) HTTP Request to /json/array failed, error id: ...: io.vertx.core.json.DecodeException: Failed to decode:Unexpected end-of-input within/between Array entries
ERROR [io.quarkus.vertx.http.runtime.QuarkusErrorHandler] (executor-thread-2) HTTP Request to /jsonb/dto failed, error id: ...: jakarta.json.bind.JsonbException: Internal error: Invalid token=EOF at (line no=1, column no=20, offset=19). Expected tokens are: [CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]
ERROR [io.quarkus.vertx.http.runtime.QuarkusErrorHandler] (executor-thread-2) HTTP Request to /jsonb/jsonp-object failed, error id: ...: jakarta.json.stream.JsonParsingException: Invalid token=EOF at (line no=1, column no=20, offset=19). Expected tokens are: [CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]
ERROR [io.quarkus.vertx.http.runtime.QuarkusErrorHandler] (executor-thread-2) HTTP Request to /jsonb/jsonp-array failed, error id: ...: jakarta.json.stream.JsonParsingException: Invalid token=EOF at (line no=1, column no=6, offset=5). Expected tokens are: [CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]

How to Reproduce?

Case 1: quarkus-rest-jackson

  1. Create a project:
    mvn io.quarkus.platform:quarkus-maven-plugin:3.40.1.1:create \
        -DprojectGroupId=org.acme -DprojectArtifactId=rest-jackson-repro \
        -Dextensions=rest-jackson -DnoCode
    cd rest-jackson-repro
  2. Add src/main/java/org/acme/JsonResource.java:
    package org.acme;
    
    import java.util.Map;
    
    import jakarta.ws.rs.Consumes;
    import jakarta.ws.rs.POST;
    import jakarta.ws.rs.Path;
    import jakarta.ws.rs.core.MediaType;
    
    import io.vertx.core.json.JsonArray;
    import io.vertx.core.json.JsonObject;
    
    @Path("/json")
    @Consumes(MediaType.APPLICATION_JSON)
    public class JsonResource {
    
        @POST
        @Path("/map")
        public String map(Map<String, Object> body) {
            return "ok";
        }
    
        @POST
        @Path("/object")
        public String object(JsonObject body) {
            return "ok";
        }
    
        @POST
        @Path("/array")
        public String array(JsonArray body) {
            return "ok";
        }
    }
  3. Run ./mvnw quarkus:dev.
  4. Send requests:
    curl -s -o /dev/null -w "%{http_code}\n" -X POST localhost:8080/json/object -H 'Content-Type: application/json' -d '{"value": 1}'   # 200
    curl -s -o /dev/null -w "%{http_code}\n" -X POST localhost:8080/json/map    -H 'Content-Type: application/json' -d '{"value": '     # 400
    curl -s -o /dev/null -w "%{http_code}\n" -X POST localhost:8080/json/object -H 'Content-Type: application/json' -d '{"value": '     # 500
    curl -s -o /dev/null -w "%{http_code}\n" -X POST localhost:8080/json/array  -H 'Content-Type: application/json' -d '[1,'            # 500

Cases 2 and 3: quarkus-rest-jsonb

  1. Create a project:
    mvn io.quarkus.platform:quarkus-maven-plugin:3.40.1.1:create \
        -DprojectGroupId=org.acme -DprojectArtifactId=rest-jsonb-repro \
        -Dextensions=rest-jsonb -DnoCode
    cd rest-jsonb-repro
  2. Add src/main/java/org/acme/JsonbResource.java:
    package org.acme;
    
    import jakarta.json.JsonArray;
    import jakarta.json.JsonObject;
    import jakarta.ws.rs.Consumes;
    import jakarta.ws.rs.POST;
    import jakarta.ws.rs.Path;
    import jakarta.ws.rs.core.MediaType;
    
    @Path("/jsonb")
    @Consumes(MediaType.APPLICATION_JSON)
    public class JsonbResource {
    
        public static class Dto {
            public Integer value;
        }
    
        @POST
        @Path("/dto")
        public String dto(Dto body) {
            return "ok";
        }
    
        @POST
        @Path("/jsonp-object")
        public String jsonpObject(JsonObject body) {
            return "ok";
        }
    
        @POST
        @Path("/jsonp-array")
        public String jsonpArray(JsonArray body) {
            return "ok";
        }
    }
  3. Run ./mvnw quarkus:dev.
  4. Send requests:
    curl -s -o /dev/null -w "%{http_code}\n" -X POST localhost:8080/jsonb/dto          -H 'Content-Type: application/json' -d '{"value": 1}'   # 200
    curl -s -o /dev/null -w "%{http_code}\n" -X POST localhost:8080/jsonb/dto          -H 'Content-Type: application/json' -d '{"value": '     # 500
    curl -s -o /dev/null -w "%{http_code}\n" -X POST localhost:8080/jsonb/jsonp-object -H 'Content-Type: application/json' -d '{"value": '     # 500
    curl -s -o /dev/null -w "%{http_code}\n" -X POST localhost:8080/jsonb/jsonp-array  -H 'Content-Type: application/json' -d '[1,'            # 500

The same result with 4.0.0.Beta1 instead of 3.40.1.1 in step 1.

Output of uname -a or ver

Linux 6.18.54.1-microsoft-standard-WSL2 x86_64 GNU/Linux

Output of java -version

OpenJDK 21.0.12.1 (Temurin-21.0.12.1+1)

Quarkus version or git rev

3.40.1.1, 4.0.0.Beta1, main (5e98984)

Build tool (ie. output of mvnw --version or gradlew --version)

Apache Maven 3.9.9

Additional information

The fix would be to map these exceptions to 400 in the server-side readers only. The *Basic* readers and the JSON-P handlers in common are shared with the REST Client, so they should stay unchanged. For JSON-B I'd only map JsonbExceptions caused by a JsonParsingException, so that e.g. a missing default constructor still results in a 500. I have a patch with tests and am happy to open a PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions