Repository navigation
Malformed JSON request body results in 500 instead of 400 for Vert.x JsonObject/JsonArray, JSON-B and JSON-P bodies #57823
Description
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:
quarkus-rest-jacksonwithio.vertx.core.json.JsonObject/JsonArrayas body.VertxJsonObjectBasicMessageBodyReadercallsnew JsonObject(String), and the resultingDecodeExceptionis not handled. The same body bound to aMapcorrectly returns 400.quarkus-rest-jsonbwith a POJO as body.JsonbMessageBodyReaderonly checks theJsonbExceptionfor empty input and otherwise rethrows it.- JSON-P types (
jakarta.json.JsonObject,JsonArray, …) as body. TheServerJson*Handlerclasses do not handleJsonParsingException.
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
- 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 - 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"; } }
- Run
./mvnw quarkus:dev. - 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
- 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 - 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"; } }
- Run
./mvnw quarkus:dev. - 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.
Activity