【问题标题】:@ExceptionHandler method not getting invoked when exception occurred发生异常时未调用@ExceptionHandler 方法
【发布时间】:2019-10-13 02:52:27
【问题描述】:

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

@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 时,在出现异常时调用异常处理程序方法?

【问题讨论】:

  • 你的方法也应该从你使用restTemplate的地方抛出HttpMessageNotReadableException。
  • 您能详细说明一下吗?我的 rest API 方法抛出 HttpMessageNotReadableException 异常,但它没有被 @Exception 处理程序中存在的 validationException 方法捕获 - 这就是问题所在。
  • 提供你提到的方法restTemplate.exchange ...代码
  • 我用实际的@Test junit 方法编辑了上面的帖子。请看一看。
  • 尝试将请求放在第一个参数中? public final ResponseEntity validationException(WebRequest request, HttpMessageNotReadableException ex)

标签: java rest spring-boot spring-test


【解决方案1】:

我认为这里的问题是 HttpMessageNotReadableException 不是由控制器抛出的,而是在调用控制器之前由 spring 基础设施抛出的。

但@ControllerAdvice 只处理控制器抛出的异常。

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

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

【讨论】:

  • 但是我的要求是有一个集成测试用例来测试我的rest API。所以我需要测试用例进入@ExceptionHandler 流程​​。这个异常 HttpMessageNotReadableException 是由我创建的自定义 httpMessageConverter 类引发的,该类用于将输入 csv 转换为 java 对象。 (在上面的帖子中粘贴了自定义转换器类)
  • 即使是这种情况,当应用程序部署到服务器时从 Postman 调用时它是如何工作的?它仅在从 TestRestTemplate 调用时不起作用。
  • 看看这是否有帮助,baeldung.com/spring-boot-testresttemplate。特别尝试#5,好像你可以将RestTemplate的设置复制到你的TestRestTemplate
猜你喜欢
  • 2014-08-11
  • 1970-01-01
  • 1970-01-01
  • 2017-03-29
  • 1970-01-01
  • 2011-07-12
  • 2011-10-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多