Skip to content

Commit

Permalink
Merge pull request #43126 from geoand/rest-jackson-mismatched
Browse files Browse the repository at this point in the history
Allows users to exclude DefaultMismatchedInputException
  • Loading branch information
gsmet authored Sep 10, 2024
2 parents e3b1b23 + e9cddc0 commit bea1fbd
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 7 deletions.
23 changes: 21 additions & 2 deletions docs/src/main/asciidoc/rest.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1415,9 +1415,28 @@ In both cases, importing those modules will allow HTTP message bodies to be read
and serialised to JSON, for <<resource-types,all the types not already registered with a more specific
serialisation>>.

==== Advanced Jackson-specific features
==== Jackson-specific features

When using the `quarkus-rest-jackson` extension there are some advanced features that Quarkus REST supports.
===== Exception handling

By default, Quarkus provides a built-in `ExceptionMapper` for `MismatchedInputException` which returns an HTTP 400 status code
along with a good error message in Dev and Test modes, about what went wrong during serialization of an entity.

[NOTE]
====
There are situations where various Jackson related exceptions need to handled in a uniform way.For example, the application may need to handle all `JsonMappingException` the same way.
This becomes a problem when taking JAX-RS / Jakarta REST rules into account, because the exception mapper `ExceptionMapper` for `MismatchedInputException` would be used instead of the user provide
`ExceptionMapper` for `JsonMappingException` (as `MismatchedInputException` is a subtype of `JsonMappingException`).
One solution for this case is to configure the following:
[source,properties]
----
quarkus.class-loading.removed-resources."io.quarkus\:quarkus-rest-jackson"=io/quarkus/resteasy/reactive/jackson/runtime/mappers/BuiltinMismatchedInputExceptionMapper.class
----
which essentially makes Quarkus ignore the `ExceptionMapper` for `MismatchedInputException` completely.
====

[[secure-serialization]]
===== Secure serialization
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@
import io.quarkus.resteasy.reactive.jackson.EnableSecureSerialization;
import io.quarkus.resteasy.reactive.jackson.SecureField;
import io.quarkus.resteasy.reactive.jackson.runtime.ResteasyReactiveServerJacksonRecorder;
import io.quarkus.resteasy.reactive.jackson.runtime.mappers.DefaultMismatchedInputException;
import io.quarkus.resteasy.reactive.jackson.runtime.mappers.NativeInvalidDefinitionExceptionMapper;
import io.quarkus.resteasy.reactive.jackson.runtime.security.RolesAllowedConfigExpStorage;
import io.quarkus.resteasy.reactive.jackson.runtime.security.SecurityCustomSerialization;
Expand Down Expand Up @@ -108,6 +107,7 @@ public class ResteasyReactiveJacksonProcessor {
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final List<String> HANDLED_MEDIA_TYPES = List.of(MediaType.APPLICATION_JSON, APPLICATION_NDJSON,
APPLICATION_STREAM_JSON);
public static final String DEFAULT_MISMATCHED_INPUT_EXCEPTION = "io.quarkus.resteasy.reactive.jackson.runtime.mappers.BuiltinMismatchedInputExceptionMapper";

@BuildStep
void feature(BuildProducer<FeatureBuildItem> feature) {
Expand All @@ -130,9 +130,16 @@ ReinitializeVertxJsonBuildItem vertxJson() {
}

@BuildStep
ExceptionMapperBuildItem exceptionMappers() {
return new ExceptionMapperBuildItem(DefaultMismatchedInputException.class.getName(),
MismatchedInputException.class.getName(), Priorities.USER + 100, false);
void exceptionMappers(BuildProducer<ExceptionMapperBuildItem> producer) {
try {
Thread.currentThread().getContextClassLoader().loadClass(DEFAULT_MISMATCHED_INPUT_EXCEPTION);
} catch (NoClassDefFoundError | ClassNotFoundException e) {
// the class is not available, likely due to quarkus.class-loading.removed-resources."io.quarkus\:quarkus-rest-jackson"=io/quarkus/resteasy/reactive/jackson/runtime/mappers/DefaultMismatchedInputException.class
return;
}

producer.produce(new ExceptionMapperBuildItem(DEFAULT_MISMATCHED_INPUT_EXCEPTION,
MismatchedInputException.class.getName(), Priorities.USER + 100, false));
}

@BuildStep
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.quarkus.resteasy.reactive.jackson.deployment.test;

import java.util.function.Supplier;

import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import com.fasterxml.jackson.databind.DatabindException;

import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;

public class ExceptionInReaderWithExcludedBuiltInAndIncludedCustomMapperTest {

@RegisterExtension
static QuarkusUnitTest test = new QuarkusUnitTest()
.setArchiveProducer(new Supplier<>() {
@Override
public JavaArchive get() {
return ShrinkWrap.create(JavaArchive.class)
.addClasses(FroMage.class, FroMageEndpoint.class, DatabindExceptionMapper.class);
}
}).overrideConfigKey("quarkus.class-loading.removed-resources.\"io.quarkus\\:quarkus-rest-jackson\"",
"io/quarkus/resteasy/reactive/jackson/runtime/mappers/BuiltinMismatchedInputExceptionMapper.class");

@Test
public void test() {
RestAssured.with().contentType("application/json").body("{\"name\": \"brie\"}").put("/fromage")
.then().statusCode(999);
}

@Provider
public static class DatabindExceptionMapper implements ExceptionMapper<DatabindException> {

@Override
public Response toResponse(DatabindException exception) {
return Response.status(999).build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

import io.quarkus.runtime.LaunchMode;

public class DefaultMismatchedInputException
@SuppressWarnings("unused")
public class BuiltinMismatchedInputExceptionMapper
implements ExceptionMapper<com.fasterxml.jackson.databind.exc.MismatchedInputException> {

@Override
Expand Down

0 comments on commit bea1fbd

Please sign in to comment.