【问题标题】:How to return exception message to calling api in Java?如何返回异常消息以在 Java 中调用 api?
【发布时间】:2019-01-18 13:45:48
【问题描述】:

我有一个已经实现的控制器方法,它返回一个值或抛出一个异常。

@Override
    @PostMapping(Endpoint.SUB_RESOURCE)
    @ResponseStatus(HttpStatus.CREATED)
    public EmployeeBankAccountOutputDto createBankAccount(@ApiParam("Account data") @RequestBody final EmployeeBankAccountInputDto accountCreationDto) {

        try {
            return service.createBankAccount(accountCreationDto);
        } catch (InvalidAccountDataException ex) {
            throw new InvalidAccountDataResponseException(ex, ex.getErrors());} 
        } catch (CountryNotFoundException ex) {
            throw new CountryNotFoundResponseException(ex.getCountryCode(), ex);
        }
    }

在这段代码中,我只想在警告级别记录异常消息,而不是抛出异常,因为我不想看到类似:

14:37:02,610 ERROR [] c.m.w.ApiExceptionHandler:135 -  Account API error occurred: 400

所以如果我只是把

log.warn(ex.getMessage()); 到第一个 catch 语句,它是给

缺少返回语句

该 catch 块的错误正常。那么如何处理不抛出异常调用api并正确发送异常消息,因为方法的返回类型是dto?

【问题讨论】:

标签: java spring spring-boot exception exception-handling


【解决方案1】:

如果无法创建帐户,您不想返回任何内容。
至少您想返回一个400 错误响应以向客户端发出问题信号。

您可以使用ResponseEntity 作为方法的返回类型来表示响应代码。这也意味着您也必须在成功的情况下更改返回对象的方式。
如果您不想传输特定的错误消息而只想传输错误代码,则可以通过这种方式更改方法:

public ResponseEntity<EmployeeBankAccountOutputDto> createBankAccount(@ApiParam("Account data") @RequestBody final EmployeeBankAccountInputDto accountCreationDto) {

    try {
        return  ResponseEntity.ok(service.createBankAccount(accountCreationDto));
    } catch (InvalidAccountDataException ex) {
      log.warn(ex.getMessage());
      return ResponseEntity.badRequest();
    } 
}

如果您还想传输特定的错误消息,您可以使用响应的正文:

public ResponseEntity<EmployeeBankAccountOutputDto> createBankAccount(@ApiParam("Account data") @RequestBody final EmployeeBankAccountInputDto accountCreationDto) {

    try {
        return  ResponseEntity.ok(service.createBankAccount(accountCreationDto));
    } catch (InvalidAccountDataException ex) {
      log.warn(ex.getMessage());
      return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage());
    } 
}

请注意,如果您需要在多个控制器中执行此处理,这可能意味着异常处理程序将是一种更简洁的方法。
例如以统一的方式处理InvalidAccountDataException

@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {

    Logger LOGGER = LoggerFactory.getLogger(MyExceptionHandler.class);

    @ExceptionHandler(value = { InvalidAccountDataException.class })
    protected ResponseEntity<Object> handleGenericExceptions(InvalidAccountDataException ex, WebRequest request) {
        log.warn(ex.getMessage());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage());      
    }

}

当然,如果需要,您可以添加更多方法来处理更多类型的异常。

【讨论】:

  • 我在考虑写几乎一样的东西。 +1
  • @J-Alex 感谢您的公平 :)
  • 如果我使用 responseentity 调用 api 怎么样?它不会受到影响吧?还是我也需要更改调用 api ?
  • 不,它对客户没有任何改变。它只是实现方面的一个抽象。
【解决方案2】:

如果您没有从第一个 catch 块中抛出异常,并且在这种情况下抛出并捕获了异常,该方法应该返回什么?

这就是您收到编译器错误的原因,因为没有返回值,因为缺少返回语句或未引发异常,这会导致执行流程突然停止,因此您可以执行以下:

public EmployeeBankAccountOutputDto createBankAccount(@ApiParam("Account data") @RequestBody final EmployeeBankAccountInputDto accountCreationDto) {
       EmployeeBankAccountInputDto employeeBankAccountInputDto = null;
       try {
            employeeBankAccountInputDto = service.createBankAccount(accountCreationDto);
       } catch (InvalidAccountDataException ex) {
          log.warn(ex.getMessage()); 
       } catch (CountryNotFoundException ex) {
           throw new CountryNotFoundResponseException(ex.getCountryCode(), ex);
       }
       return employeeBankAccountInputDto;
}

应该理想地将返回的对象包装在 ResponseEntity 中并添加消息和响应代码(正如 davidxxx 在他的评论中解释的那样)。

public ResponseEntity<EmployeeBankAccountOutputDto> createBankAccount(@ApiParam("Account data") @RequestBody final EmployeeBankAccountInputDto accountCreationDto) {
       EmployeeBankAccountInputDto employeeBankAccountInputDto = null;
       try {
            employeeBankAccountInputDto = service.createBankAccount(accountCreationDto);
       } catch (InvalidAccountDataException ex) {
            log.warn(ex.getMessage()); 
            return new ResponseEntity(employeeBankAccountInputDto, HttpStatus.BAD_REQUEST);
       } catch (CountryNotFoundException ex) {
            throw new CountryNotFoundResponseException(ex.getCountryCode(), ex);
       }
       return new ResponseEntity(employeeBankAccountInputDto, HttpStatus.OK);
 }

【讨论】:

  • 在您的第一种方法中,您将返回 2XX 响应代码。客户不应将成功响应视为问题。我认为你应该只保留第二种方式。
猜你喜欢
  • 2017-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-21
  • 2019-12-04
  • 2013-12-31
  • 2015-12-19
  • 2017-07-01
相关资源
最近更新 更多