diff --git a/core/src/main/java/io/confluent/rest/Application.java b/core/src/main/java/io/confluent/rest/Application.java index ebe0215ead..0095f6cd0c 100644 --- a/core/src/main/java/io/confluent/rest/Application.java +++ b/core/src/main/java/io/confluent/rest/Application.java @@ -77,6 +77,7 @@ import io.confluent.rest.exceptions.ConstraintViolationExceptionMapper; import io.confluent.rest.exceptions.GenericExceptionMapper; import io.confluent.rest.exceptions.WebApplicationExceptionMapper; +import io.confluent.rest.exceptions.JsonMappingExceptionMapper; import io.confluent.rest.extension.ResourceExtension; import io.confluent.rest.filters.CsrfTokenProtectionFilter; import io.confluent.rest.metrics.MetricsResourceMethodApplicationListener; @@ -568,6 +569,7 @@ protected void registerFeatures(Configurable config, T restConfig) { */ protected void registerExceptionMappers(Configurable config, T restConfig) { config.register(ConstraintViolationExceptionMapper.class); + config.register(JsonMappingExceptionMapper.class); config.register(new WebApplicationExceptionMapper(restConfig)); config.register(new GenericExceptionMapper(restConfig)); } diff --git a/core/src/main/java/io/confluent/rest/exceptions/JsonMappingExceptionMapper.java b/core/src/main/java/io/confluent/rest/exceptions/JsonMappingExceptionMapper.java new file mode 100644 index 0000000000..003189f976 --- /dev/null +++ b/core/src/main/java/io/confluent/rest/exceptions/JsonMappingExceptionMapper.java @@ -0,0 +1,42 @@ +/** + * Copyright 2021 Confluent Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.confluent.rest.exceptions; + +import com.fasterxml.jackson.databind.JsonMappingException; +import io.confluent.rest.entities.ErrorMessage; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +@Provider +public class JsonMappingExceptionMapper implements ExceptionMapper { + + public static final int BAD_REQUEST_CODE = 400; + + @Override + public Response toResponse(JsonMappingException exception) { + ErrorMessage message = new ErrorMessage( + BAD_REQUEST_CODE, + exception.getMessage() + ); + + return Response.status(BAD_REQUEST_CODE) + .entity(message) + .build(); + } +} diff --git a/core/src/test/java/io/confluent/rest/JsonMappingExceptionMapperTest.java b/core/src/test/java/io/confluent/rest/JsonMappingExceptionMapperTest.java new file mode 100644 index 0000000000..fb711039fd --- /dev/null +++ b/core/src/test/java/io/confluent/rest/JsonMappingExceptionMapperTest.java @@ -0,0 +1,64 @@ +/** + * Copyright 2021 Confluent Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.confluent.rest; + +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.confluent.rest.entities.ErrorMessage; +import io.confluent.rest.exceptions.JsonMappingExceptionMapper; +import org.junit.Before; +import org.junit.Test; + +import javax.ws.rs.core.Response; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class JsonMappingExceptionMapperTest { + + private JsonMappingExceptionMapper mapper; + + @Before + public void setUp() { + mapper = new JsonMappingExceptionMapper(); + } + + @Test + public void testJsonMappingException() { + try { + String json = "{\"name\":{}}"; + ObjectMapper mapper = new ObjectMapper(); + // try to parse a json where the User name is expecting a string but input an Object + mapper.reader().forType(User.class).readValue(json); + } catch (JsonMappingException e) { + Response response = mapper.toResponse(e); + assertEquals(400, response.getStatus()); + ErrorMessage out = (ErrorMessage)response.getEntity(); + assertEquals(400, out.getErrorCode()); + } catch (Exception e) { + fail("A JsonMappingException is expected."); + } + } + + class User { + public String name; + + User(String name) { + this.name = name; + } + } +}