Mapping RestTemplate response to java Object - spring-boot

I am using RestTemplate get data from remote rest service and my code is like this.
ResponseEntity<List<MyObject >> responseEntity = restTemplate.exchange(request, responseType);
But rest service will return just text message saying no record found if there are no results and my above line of code will throw exception.
I could map result first to string and later use Jackson 2 ObjectMapper to map to MyObject.
ResponseEntity<String> responseEntity = restTemplate.exchange(request, responseType);
String jsonInput= response.getBody();
List<MyObject> myObjects = objectMapper.readValue(jsonInput, new TypeReference<List<MyObject>>(){});
But I don't like this approach. Is there any better solution for this.

First of all you could write a wrapper for the whole API. Annotate it with #Component and you can use it wherever you want though Springs DI. Have a look at this example project which shows of generated code for a resttemplate client by using swagger codegen.
As you said you tried implementing a custom responserrorhandler without success I assume that the API returns the response body "no record found" while the status code is 200.
Therefore you could create a custom AbstractHttpMessageConverter as mentioned in my second answer. Because you are using springs resttemplate which is using the objectmapper with jackson we don't event have to use this very general super class to create our own. We can use and extend the more suited AbstractJackson2HttpMessageConverter class.
An implementation for your specific use case could look as follows:
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
public class WeirdAPIJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
public static final String NO_RECORD_FOUND = "no record found";
public WeirdAPIJackson2HttpMessageConverter() {
// Create another constructor if you want to pass an already existing ObjectMapper
// Currently this HttpMessageConverter is applied for every MediaType, this is application-dependent
super(new ObjectMapper(), MediaType.ALL);
}
#Override
public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
try (BufferedReader br = new BufferedReader(new InputStreamReader(inputMessage.getBody(), DEFAULT_CHARSET))) {
String responseBodyStr = br.lines().collect(Collectors.joining(System.lineSeparator()));
if (NO_RECORD_FOUND.equals(responseBodyStr)) {
JavaType javaType = super.getJavaType(type, contextClass);
if(Collection.class.isAssignableFrom(javaType.getRawClass())){
return Collections.emptyList();
} else if( Map.class.isAssignableFrom(javaType.getRawClass())){
return Collections.emptyMap();
}
return null;
}
}
return super.read(type, contextClass, inputMessage);
}
}
The custom HttpMessageConverter is checking the response body for your specific "no record found". If this is the case, we try to return a default value depending on the generic return type. Atm returning an empty list if the return type is a sub type of Collection, an empty set for Set and null for all other Class types.
Furthermore I created a RestClientTest using a MockRestServiceServer to demonstrate you how you can use your RestTemplate within the aforementioned API wrapper component and how to set it up to use our custom AbstractJackson2HttpMessageConverter.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.client.ExpectedCount;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Optional;
import static org.junit.Assert.*;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {RestTemplateResponseErrorHandlerIntegrationTest.MyObject.class})
#RestClientTest
public class RestTemplateResponseErrorHandlerIntegrationTest {
static class MyObject {
// This just refers to your MyObject class which you mentioned in your answer
}
private final static String REQUEST_API_URL = "/api/myobjects/";
private final static String REQUEST_API_URL_SINGLE = "/api/myobjects/1";
#Autowired
private MockRestServiceServer server;
#Autowired
private RestTemplateBuilder builder;
#Test
public void test_custom_converter_on_weird_api_response_list() {
assertNotNull(this.builder);
assertNotNull(this.server);
RestTemplate restTemplate = this.builder
.messageConverters(new WeirdAPIJackson2HttpMessageConverter())
.build();
this.server.expect(ExpectedCount.once(), requestTo(REQUEST_API_URL))
.andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.OK).body(WeirdAPIJackson2HttpMessageConverter.NO_RECORD_FOUND));
this.server.expect(ExpectedCount.once(), requestTo(REQUEST_API_URL_SINGLE))
.andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.OK).body(WeirdAPIJackson2HttpMessageConverter.NO_RECORD_FOUND));
ResponseEntity<List<MyObject>> response = restTemplate.exchange(REQUEST_API_URL,
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<MyObject>>() {
});
assertNotNull(response.getBody());
assertTrue(response.getBody().isEmpty());
Optional<MyObject> myObject = Optional.ofNullable(restTemplate.getForObject(REQUEST_API_URL_SINGLE, MyObject.class));
assertFalse(myObject.isPresent());
this.server.verify();
}
}

What I usually do in my projects with restTemplate is save the response in a java.util.Map and create a method that converts that Map in the object I want. Maybe saving the response in an abstract object like Map helps you with that exception problem.
For example, I make the request like this:
List<Map> list = null;
List<MyObject> listObjects = new ArrayList<MyObject>();
HttpHeaders headers = new HttpHeaders();
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<Map> response = restTemplate.exchange(url, HttpMethod.GET, entity, Map.class);
if (response != null && response.getStatusCode().value() == 200) {
list = (List<Map>) response.getBody().get("items"); // this depends on the response
for (Map item : list) { // we iterate for each one of the items of the list transforming it
MyObject myObject = transform(item);
listObjects.add(myObject);
}
}
The function transform() is a custom method made by me: MyObject transform(Map item); that receives a Map object and returns the object I want. You can check if there was no records found first instead of calling the method transform.

Related

I am trying to get Header info from Request Controller and read into IntegrationFlow

I wanted to understand where is best location to read headers and use them inside my IntegrationFlow layer.
ServiceController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/api/v1/integration")
public class ServiceController {
#Autowired
private ServiceGateway gateway;
#GetMapping(value = "info")
public String info() {
return gateway.info();
}
}
ServiceGateway.java
import org.springframework.integration.annotation.Gateway;
import org.springframework.integration.annotation.MessagingGateway;
#MessagingGateway
public interface ServiceGateway {
#Gateway(requestChannel = "integration.info.gateway.channel")
public String info();
}
ServiceConfig.java
import java.net.URISyntaxException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.integration.annotation.IntegrationComponentScan;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.http.dsl.Http;
import org.springframework.messaging.MessageHeaders;
#Configuration
#EnableIntegration
#IntegrationComponentScan
public class ServiceConfig {
#Bean
public IntegrationFlow info() throws URISyntaxException {
String uri = "http://localhost:8081/hellos/simpler";
return IntegrationFlows.from("integration.info.gateway.channel")
.handle(Http.outboundGateway(uri).httpMethod(HttpMethod.POST).expectedResponseType(String.class)).get();
}
}
From Consumer I am receiving some Header meta data. I want to know in above flow whether it is good idea from following approaches:
Read headers in Controller and then pass through into my IntegrationFlow: For this I am not aware how to pass through.
Is there best or any way exist to read request headers into IntegrationFlow layer?
For this second approach I have tried below code but runtime I am getting error as channel is one way and hence stopping the flow.
return IntegrationFlows.from("integration.info.gateway.channel").handle((request) -> {
MessageHeaders headers = request.getHeaders();
System.out.println("-----------" + headers);
}).handle(Http.outboundGateway(uri).httpMethod(HttpMethod.POST).expectedResponseType(String.class)).get();
My problem is how to send request parameters from incoming call to carry those internally invoking another rest call. Here I wanted to transform the data from request headers and construct into new json body and then send this to http://localhost:8081/hellos/simpler URL.
The flow:
I am trying to construct this RequestBody before sending to internal REST POST call:
A gateway method with no paylaod is for receiving data, not requesting it.
https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-endpoints.html#gateway-calling-no-argument-methods
Add a #Header annotated parameter to the gateway.
https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-endpoints.html#gateway-configuration-annotations
#MessagingGateway
public interface ServiceGateway {
#Gateway(requestChannel = "integration.info.gateway.channel")
public String info("", #Header("x-api") String xApi);
}
This will send a message with an empty string as the payload with the header set.

Get response body from NoFallbackAvailableException in spring cloud circuit breaker resilience4j

I want to call a third party API. I use spring cloud circuit breaker resilience4j.
Here is my service class :
package ir.co.isc.resilience4jservice.service;
import ir.co.isc.resilience4jservice.model.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
#Service
public class EmployeeService {
#Autowired
private RestTemplate restTemplate;
#Autowired
private CircuitBreakerFactory circuitBreakerFactory;
public Employee getEmployee() {
try {
String url = "http://localhost:8090/employee";
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuit-breaker");
return circuitBreaker.run(() -> restTemplate.getForObject(url, Employee.class));
} catch (NoFallbackAvailableException e) {
//I should extract error response body and do right action then return correct answer
return null;
}
}
}
ResilienceConfig:
package ir.co.isc.resilience4jservice.config;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.timelimiter.TimeLimiterConfig;
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory;
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;
#Configuration
public class CircuitBreakerConfiguration {
#Bean
public Customizer<Resilience4JCircuitBreakerFactory> defaultCustomizer() {
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
.slidingWindowSize(10)
.minimumNumberOfCalls(10)
.failureRateThreshold(25)
.permittedNumberOfCallsInHalfOpenState(3)
.build();
TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
.timeoutDuration(Duration.ofSeconds(4))
.build();
return factory ->
factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
.circuitBreakerConfig(circuitBreakerConfig)
.timeLimiterConfig(timeLimiterConfig)
.build());
}
}
in some situation third party api return ResponseEntity with statusCode = 500 and
body = {"errorCode":"CCBE"}.
response is look like this :
[503] during [POST] to [http://localhost:8090/employee]:[{"errorCode":"CCBE"}]
When I call this API and get internal server error with body, my catch block catchs api response.
In catch block I need retrieve response body and do some actions according to errorCode.
But I can not do this.
How can I extract body in this situation?

How to make Spring boot CSV message converter display CSV inline and not download when using a browser

I created a spring starter project in eclipse . Most of the code was from this link https://www.logicbig.com/tutorials/spring-framework/spring-web-mvc/csv-msg-converter.html.
I added content negotiation configuration to accept headers, path extension and parameters. It works great from postman.
But when I try in a browser http://localhost:8080/employeelist.csv. In all the cases CSV is getting downloaded in a file. I want it displayed inline on the browser. I tried to set content disposition as inline in Request mapping, http output message header but still CSV is always getting downloaded.
What should I be doing to get csv displayed inline? I had previously successfully displayed CSV inline in a browser by having separate request mapping method for CSV and make the method return void and accept httpservletresponse as parameter. But I want to use content negotiation and a single method for all formats - XML, CSV, json. Whatever format selected should be displayed inline in the browser.
Is that possible ?
Thanks a lot for your time.
Update : added portions of code which were edited
package ti.projects;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
#SuppressWarnings("deprecation")
#EnableWebMvc
#Configuration
#ComponentScan("ti.projects")
public class AppConfig extends WebMvcConfigurerAdapter {
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new CsvHttpMessageConverter<>());
}
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(true).favorParameter(true).parameterName("mediaType").ignoreAcceptHeader(false)
.useJaf(false).mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("csv", new MediaType("text", "csv"));
}
}
package ti.projects;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.Arrays;
import java.util.List;
#Controller
public class ExampleController {
#RequestMapping(
value = "/newEmployee",
consumes = "text/csv",
produces = MediaType.TEXT_PLAIN_VALUE,
method = RequestMethod.POST)
#ResponseBody
#ResponseStatus(HttpStatus.OK)
public String handleRequest (#RequestBody EmployeeList employeeList) {
System.out.printf("In handleRequest method, employeeList: %s%n", employeeList.getList());
String s = String.format("size: " + employeeList.getList().size());
System.out.println(s);
return s;
}
#RequestMapping(
value = "/employeeList",
produces = {"text/csv", "application/json"},
method = RequestMethod.GET
)
#ResponseBody
#ResponseStatus(HttpStatus.OK)
public EmployeeList handleRequest2 () {
List<Employee> list = Arrays.asList(
new Employee("1", "Tina", "111-111-1111"),
new Employee("2", "John", "222-222-2222")
);
EmployeeList employeeList = new EmployeeList();
employeeList.setList(list);
return employeeList;
}
}
package ti.projects;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class ContentNegotiationApplication {
public static void main(String[] args) {
SpringApplication.run(ContentNegotiationApplication.class, args);
}
}
The browser (should) use the provided mime type to decide how to display or process the response. What should work is using a MIME of text/plain to let the browser render the received content as text.
You can set the MIME type of your response in your spring Controller like this:
#GetMapping(produces = MediaType.TEXT_PLAIN_VALUE)
public String renderCsv() {...}
If you want to offer different MIME types with one method you have three options:
Use query parameter (e.g. ...?contentType=json)
Use path parameter (e.g..../{contentType})
Use accept header of client (preferably?)
You can register different MessageConverter for each contentType and configure a ContentNegotiationConfigurer to automatically choose the correct converter depending on given MIME type and your preferences.
I'll try to attach an example tonight.

Generic JPA repository for runtime generated entities

In my scenario im generating hibernate entity classes at runtime under "com.mrg.domain" package. And in my generic restcontroller i can create instances of these entities according to #PathVariable. Below code works fine for this..
#RequestMapping( value = "/{entity}", method = RequestMethod.POST)
public #ResponseBody RestResponse createEntity(#PathVariable String entity, #RequestBody String requestBody) {
Object model = null;
ObjectMapper mapper = new ObjectMapper();
try {
// ex : if {entitiy} param is equal "post" modelName will be "Post"
String modelName = Character.toUpperCase(entity.charAt(0)) + entity.substring(1);
// Creating a new instance according to modelName
Class<?> clazz = Class.forName("com.mrg.domain." + modelName);
model = clazz.newInstance();
// Converting #RequestBody json String to domain object..
model = mapper.readValue(requestBody, clazz);
} catch(Exception ex){
// TODO handle exceptions & inform user..
}
return new RestResponse(model.toString());
}
Now the next step i am trying to implement is a generic jpa repository(something like below) so that i can persist runtime generated models without implementing repositories for each entity. But couldn't find a solution yet.
#Repository
public interface GenericRepository<T> extends PagingAndSortingRepository<T, Long>{ }
Below topic and many other topics implemented generic repositories but also repositories per entities that uses generic repo. Since i have runtime generated entities repo implementation per entity doesnt work for me..
How to make generic jpa repository? Should I do this? Why?
Any suggestion or a way for achieving this? I'm new to generics and reflection so if what im trying to accomplish is not possible, tell me reasons and i would be appreciate..
Thanks and regards,
You could use this pattern. This one uses EJB but can be used in Spring etc.
#Stateless
public abstract class AbstractRepository<T> {
#PersistenceContext
protected EntityManager em;
public abstract Class<T> getActualClass();
public T getSingleResult(Map<String, String> params) {
// build querytext based on params
TypedQuery<T> query = em.createQuery(queryText.toString(), getActualClass());
............
}
}
Now for the implementation class:
#Stateless
public class InputStreamRepository extends AbstractRepository<InputDataStream> {
#Override
public Class<InputDataStream> getActualClass() {
return InputDataStream.class;
}
}
The getActualClass method will give you the Entity's class impl info.
I had a react application where different data is defined in JSON and in the server side, I need to store this in the DB. My initial approach was to create entities , repositories and controller for all of this seperately. But another possible approach for CRUD operation is with MongoDB & JPA. Here is the idea.
import java.util.List;
import org.bson.Document;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/api/generic")
#CrossOrigin(origins = { "*" })
public class GenericController {
#Autowired
private MongoTemplate mongoTemplate;
#PostMapping
public ResponseEntity<Document> addData(#RequestBody String data) {
JSONObject jsonObject = new JSONObject(data);
String documentName = jsonObject.getString("documentName");
Document doc = Document.parse(data);
Document insertedDoc = mongoTemplate.insert(doc, documentName);
return new ResponseEntity<>(insertedDoc, HttpStatus.CREATED);
}
#GetMapping("/{documentName}")
public List<Document> getData(#PathVariable String documentName) {
List<Document> allData = mongoTemplate.findAll(Document.class, documentName);
return allData;
}
}

how do I throw a error on unknown fields in json request to spring restapi

I have a spring rest api which gets json data and binds to a pojo GetData.
Whenever i recieve unknown fields it doesnt fail or throw any exception. My requirement here is it should throw a error when it receives unknown fields in json data.
public ResponseEntity<Error> saveLocation(#Valid #RequestBody GetData getdata,BindingResult bindingResults) {
Below is my Pojo GetData
public class GetData{
#JsonProperty("deviceID")
#Pattern(regexp="^[\\p{Alnum}][-\\p{Alnum}\\p{L}]+[\\p{Alnum}]$",message = "Not a valid Device Id")
private String deviceID;
#JsonProperty("Coordinates")
#Pattern(regexp="^[\\p{Alnum}\\-][\\.\\,\\-\\_\\p{Alnum}\\p{L}\\s]+|",message = "Coordinates are not valid")
private String coordinates;}
Below is my json request.
{
"deviceID" : "01dbd619-843b-4197-b954",
"Coordinates" : "12.984012,80.246712",
}
Now if i send a request with an extra field say country. It doesn't throw any error.
{
"deviceID" : "01dbd619-843b-4197-b954",
"Coordinates" : "12.984012,80.246712",
"country" : "dsasa"
}
Please suggest how can i have an error for unknown properties being sent in a json request
You can try out any one of the below implementations, it works for me. You will have to override one more method from ResponseEntityExceptionHandler or by using ExceptionHandler.
1. By Overriding Method of ResponseEntityExceptionHandler
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
#ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(CustomExceptionHandler.class);
//Other Handlers
// Handle 400 Bad Request Exceptions
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
log.info(ex.getLocalizedMessage() + " ",ex);
final CustomErrorMessage errorMessage = new CustomErrorMessage(ex.getLocalizedMessage(), InfoType.ERROR, HttpStatus.BAD_REQUEST, ex.fillInStackTrace().toString());
return handleExceptionInternal(ex, errorMessage, headers, errorMessage.getStatus(), request);
}
//Other Handlers
}
Apart from above implementation you can try out the below one also, if you want to throw error only if unrecognised properties are present in request payload or empty property and empty value is present like below JSON
{
"":""
}
2. Using ExceptionHandler
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class GenericExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GenericExceptionHandler.class);
#ExceptionHandler(value = {UnrecognizedPropertyException.class})
#ResponseStatus(HttpStatus.BAD_REQUEST)
protected ResponseEntity<Object> handleUnrecognizedPropertyException(UnrecognizedPropertyException ex) {
log.info(ex.getLocalizedMessage() + " ",ex);
final String error = "JSON parse error: Unrecognized field " + "[ " + ex.getPropertyName() + " ]";
final CustomErrorMessage errorMessage = new CustomErrorMessage(error, InfoType.ERROR, HttpStatus.BAD_REQUEST);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorMessage);
}
}
Note : For above both implementations to work properly, you need to add the below line in your application.properties file.
spring.jackson.deserialization.fail-on-unknown-properties=true
Hope this will help you :)
You need to configure your ObjectMapper to handle such cases:
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
Alternatively you can use:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown = false)
public class GetData {
}

Resources