1

I have a global exception handler as below :-

@ControllerAdvice
@RestController
public class GlobalExceptionHandler {

@ExceptionHandler(value= {HttpMessageNotReadableException.class})
    public final ResponseEntity<ErrorDetails> validationException(HttpMessageNotReadableException ex, WebRequest request) {

        System.out.println("This was called.");
        if(ex.getCause() instanceof CsvRequiredFieldEmptyException){

            CsvRequiredFieldEmptyException csvExp = (CsvRequiredFieldEmptyException) ex.getCause();
            String exceptionDtls = csvExp.getMessage().concat(" ").concat(" at line number "+csvExp.getLineNumber()+ " in the csv filw.");
            ErrorDetails errorDetails = new ErrorDetails(LocalDate.now(),exceptionDtls, request.getDescription(false));
            return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
        }

        return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
    }
}

I am invoking the rest API by using TestRestTemplate for integration testing.

ResponseEntity<?> response = restTemplate.exchange(ITestUtils.createURLWithPort(postUrlCsv,
                host,port ), HttpMethod.POST,listingDocEnt, String.class);

@Test
    public void uploadListingCsvTest_Returns400BadReq_WhenCodeMissing() throws HttpMessageNotReadableException {

        // Step 1 : Create the Http entity object which contains the request body and headers.
        HttpEntity<ListingList> listingDocEnt = new HttpEntity<ListingList>(createTestDataForNewVehicleListingCodeMissing(),
                getHttpHeaderCsv());

        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON));

        List<HttpMessageConverter<?>> csvMessgeonverter = new ArrayList<>();
        csvMessgeonverter.add(new CsvHttpMessageConverter<>());
        csvMessgeonverter.add(converter);
        TestRestTemplate restTemplate = new TestRestTemplate();
        restTemplate.getRestTemplate().setMessageConverters(csvMessgeonverter);

        ResponseEntity<?> response = restTemplate.exchange(ITestUtils.createURLWithPort(postUrlCsv,
                host,port ), HttpMethod.POST,listingDocEnt, String.class);

        // Check if the response is not null and the http status code is - 201 Created.
        Assert.assertNotNull(response);
        Assert.assertEquals(HttpStatus.BAD_REQUEST,response.getStatusCode());

    }

My rest API has custom HttpMessageConverter which is as below which converts the input request csv to java object in the rest controller. This custom message converter has a method readInternal which throws an exception HttpMessageNotReadableException , but still the exception handler method 'validationException' is not getting invoked. The Junit simply breaks and fails.

public class CsvHttpMessageConverter<T, L extends ListParam<T>>
          extends AbstractHttpMessageConverter<L> {

    public CsvHttpMessageConverter () {
        super(new MediaType("text", "csv"));
    }

    @Override
    protected boolean supports (Class<?> clazz) {
        return ListParam.class.isAssignableFrom(clazz);
    }

    @Override
    protected L readInternal (Class<? extends L> clazz,HttpInputMessage inputMessage)
              throws IOException, HttpMessageNotReadableException {

        HeaderColumnNameMappingStrategy<T> strategy = new HeaderColumnNameMappingStrategy<>();
        Class<T> t = toBeanType(clazz.getGenericSuperclass());
        strategy.setType(t);

        CSVReader csv = new CSVReader(new InputStreamReader(inputMessage.getBody()));
        CsvToBean<T> csvToBean = new CsvToBean<>();

        List<T> beanList = null;

        try {
            beanList = csvToBean.parse(strategy, csv);

        } catch(Exception exception){
            throw new HttpMessageNotReadableException("Exception while parsing the CSV file.",exception.getCause());
        }

        try {
            L l = clazz.newInstance();
            l.setList(beanList);
            return l;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    protected void writeInternal (L l, HttpOutputMessage outputMessage)
              throws IOException, HttpMessageNotWritableException {

        HeaderColumnNameMappingStrategy<T> strategy = new HeaderColumnNameMappingStrategy<>();
        strategy.setType(toBeanType(l.getClass().getGenericSuperclass()));

        OutputStreamWriter outputStream = new OutputStreamWriter(outputMessage.getBody());
        StatefulBeanToCsv<T> beanToCsv =
                  new StatefulBeanToCsvBuilder(outputStream)
                            .withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
                            .withMappingStrategy(strategy)
                            .build();

            try {
                beanToCsv.write(l.getList());
            } catch (CsvDataTypeMismatchException e) {
                throw new HttpMessageNotWritableException("Exception while parsing the CSV file.",e);
            } catch (CsvRequiredFieldEmptyException e) {
                throw new HttpMessageNotWritableException("Exception while parsing the CSV file.",e);
            }
            outputStream.close();

    }

    @SuppressWarnings("unchecked")
    private Class<T> toBeanType (Type type) {
        return (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];
    }

Is there a way that when invoking a spring rest API using TestRestTemplate we can invoke the exception handler method when there is an exception?

10
  • Your method should also throw HttpMessageNotReadableException from where you are using restTemplate. Commented May 27, 2019 at 14:04
  • Could you please elaborate a little more? My rest API method is throwing HttpMessageNotReadableException exception , but it is not caught by validationException method which is present inside the @Exception Handler - that is the problem. Commented May 27, 2019 at 14:08
  • provide the method where you have mentioned restTemplate.exchange ... code Commented May 27, 2019 at 14:09
  • I have edited above post with actual @Test junit method. Please have a look. Commented May 27, 2019 at 14:13
  • try to put request in the first parameter? public final ResponseEntity<ErrorDetails> validationException(WebRequest request, HttpMessageNotReadableException ex) Commented May 27, 2019 at 14:14

1 Answer 1

2

I think the problem here is that the HttpMessageNotReadableException is not thrown by the Controller, but instead by the spring infrastructure BEFORE the controller is invoked.

But a @ControllerAdvice does only handle exceptions that are thrown by the Controller.

In spring's DispatcherServlet there is also an ErrorHandler that is invoked in such cases. Maybe this is a solution for you?

Here are some infos about this: https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc#going-deeper

Sign up to request clarification or add additional context in comments.

3 Comments

But my requirement is to have an integration test case to test my rest API. And so I need the test case to go into the @ExceptionHandler flow. This exception HttpMessageNotReadableException is thrown by the custom httpMessageConverter class that I have created to convert the input csv to java object. ( Pasted the custom converter class in the above post )
Even if that is the case how does it work when invoked from Postman when the application is deployed to server? It only does not work when invoked from the TestRestTemplate.
See if this helps, baeldung.com/spring-boot-testresttemplate . Try especially #5, seems like you can copy the settings of RestTemplate to your TestRestTemplate

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.