发生异常时未调用@ExceptionHandler方法

时间:2019-05-27 14:02:51

标签: java rest spring-boot spring-test

我有一个如下的全局异常处理程序:-

@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);
    }
}

我通过使用TestRestTemplate进行集成测试来调用其余的API。

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());

    }

我的rest API具有自定义HttpMessageConverter,如下所示,它将输入请求csv转换为rest控制器中的java对象。此自定义消息转换器具有方法readInternal,该方法抛出异常HttpMessageNotReadableException,但仍未调用异常处理程序方法'validationException'。 Junit只会崩溃而失败。

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];
    }

有没有一种方法,当使用TestRestTemplate调用Spring Rest API时,我们可以在出现异常时调用异常处理程序方法吗?

1 个答案:

答案 0 :(得分:0)

我认为这里的问题是HttpMessageNotReadableException不是由Controller引发的,而是由Spring基础结构在调用控制器之前引发的。

但是@ControllerAdvice仅处理Controller引发的异常。

在Spring的DispatcherServlet中,还有一个ErrorHandler在这种情况下被调用。也许这是您的解决方案?

以下是一些有关此的信息: https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc#going-deeper