使用 RestTemplate 在循环中处理异常

Posted

技术标签:

【中文标题】使用 RestTemplate 在循环中处理异常【英文标题】:Handle exceptions in a loop with RestTemplate 【发布时间】:2022-01-24 05:06:55 【问题描述】:

我有一个对象列表,对于每个对象,我需要使用 RestTemplate 访问外部 API。为此,我将 for 循环与 try-catch 块一起使用。如果外部 API 确实以 4xx、5xx 状态代码响应,我需要创建错误列表并抛出自定义异常,该异常使用异常处理程序处理以发送客户端友好的消息,这些消息也会发送电子邮件通知。要求是删除 try-catch 块并在循环中命中外部 API 并创建错误列表并检查错误列表是否不为空抛出异常并使用电子邮件通知一次发送所有错误消息异常处理方法handleApplicationErrors。但我相信当任何异常发生时 for 循环将是一个中断,如果没有 try-catch 块,我将无法创建错误消息列表,有什么可能的方法吗?

public void method() 
    List<Objects> objects = fetchObjects();
    List<String> errorList = new ArrayList();

    for(int i=0;i<objects.size();i++) 
      try
        hitExternalApi(object)
      
      catch(Exception e)
        errorList.add("Error Message")
      
    
    if(!errorList.isEmpty()) 
       throw new ErrorDTO(Status.BAD_REQUEST, errorList);
    


  @Override
  public void hitExternalApi(Object object)  
    httpHeaders.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity<Object> request = new HttpEntity<>(object, httpHeaders);
    restTemplate.exchange(url, HttpMethod.POST, request, Void.class);
  

@ExceptionHandler(ErrorDTO.class)
  public ResponseEntity<Problem> handleApplicationErrors(NativeWebRequest request, ErrorDTO error) 
    notificationService.sendNotification(error.getErrors());
    Problem problem =
        Problem.builder()
            .withStatus(error.getStatus())
            .withTitle(error.getMessage())
            .withDetail(error.getErrors().toString())
            .build();
    return create(error, problem, request);
  

【问题讨论】:

'hitExternalApi' 方法是什么样的?您需要在此方法中添加功能以在 response.getStatusCode() 不是 2xx(成功的 http 代码)时抛出异常。 你好@ProgrammerBoy,这是一个正常的rest模板交换方法调用,没有任何try-catch块。如果我在此方法中抛出异常,我将无法创建错误消息列表并为其发送单个通知。 您还应该检查 exchange 返回的 ResponseEntity 的状态代码。但是为什么你说你需要删除 try-catch 块呢?有必要处理hitExternalApi抛出的任何异常。 是的,我可以使用状态码并基于 for 循环或方法中的 try-catch 块来管理它。但是团队中的一位成员建议删除 try-catch 块,并为我们发送到外部 API 的所有对象创建错误消息列表,并与整个列表一起发送电子邮件。所以我很想知道是否可以在不使用 try-catch 块的情况下做同样的事情。如果我删除 try-catch 并根据响应状态代码执行此操作,它将抛出 4xx、5xx 的异常,并且无法为循环中的所有对象创建错误消息列表。 【参考方案1】:

您应该能够通过将 try-catch 块向下移动到 hitExternalApi 方法来解决您的要求,如果您需要传递任何信息,请让它返回一个布尔值或 DTO。然后,for 循环可以检查 hitExternalApi 的返回值并在需要时填充 errorList。 修改代码示例:

public void method() 
   List<Objects> objects = fetchObjects();
   List<String> errorList = new ArrayList();

   for (int i = 0; i < objects.size(); i++) 
     if (!hitExternalApi(object)) 
       errorList.add("Error Message");
     
   
   if (!errorList.isEmpty()) 
     throw new ErrorDTO(Status.BAD_REQUEST, errorList);
   
 

 public boolean hitExternalApi(Object object) 
   httpHeaders.setContentType(MediaType.APPLICATION_JSON);
   HttpEntity<Object> request = new HttpEntity<>(object, httpHeaders);
   try 
     restTemplate.setErrorHandler();
     restTemplate.exchange(url, HttpMethod.POST, request, Void.class);
     return true;
    catch (Exception exception) 
     return false;
   
 

默认情况下,(因此当使用DefaultResponseErrorHandler 时),restTemplate.exchange(...) 的调用将针对 4xx 和 5xx 响应抛出某种 RestClientException,因此您需要在某处处理此异常或提供您自己的ResponseErrorHandler 并围绕它构建逻辑。

【讨论】:

【参考方案2】:

你可以创建一个额外的对象来控制流程

public class ApiResponsePojo 
    private HttpStatus status;
    private String message;
    private String data;

您可以使用这样的类并将其修改为您想要存储消息的方式。您可以通过状态检查是否有任何错误并在状态中填充一条消息

【讨论】:

正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center。【参考方案3】:

您可以将 for 循环放在一个函数中,并在调用该函数后调用异常。

请参考以下示例以获得更好的理解:

之前

public class ExceptionInLoop
public static void sampleMethod()
  String str[] = "Mango", "Apple", "Banana", "Grapes", "Oranges";
     try 
        for(int i=0; i<=10; i++) 
           System.out.println(str[i]);
           System.out.println(i);
        
     catch (ArrayIndexOutOfBoundsException ex)
        System.out.println("Exception occurred");
  
  System.out.println("hello");

public static void main(String args[]) 
  sampleMethod();

之后

public class ExceptionInLoop
public static void print(String str) 
  System.out.println(str);

public static void sampleMethod()throws ArrayIndexOutOfBoundsException   
  String str[] = "Mango", "Apple", "Banana", "Grapes", "Oranges";
     for(int i=0; i<=10; i++) 
        try 
           print(str[i]);
           System.out.println(i);
         catch(Exception e)
        System.out.println(i);
     
  

public static void main(String args[]) 
  try
     sampleMethod();
  catch(ArrayIndexOutOfBoundsException e) 
     System.out.println("");
  

【讨论】:

以上是关于使用 RestTemplate 在循环中处理异常的主要内容,如果未能解决你的问题,请参考以下文章

restTemplate 调用异常处理,处理http协议层的错误状态40*,30*等

Spring Resttemplate 异常处理

使用 RestTemplate 测试 ExceptionHandler

SpringWeb 系列教程 RestTemplate 4xx/5xx 异常信息捕获

使用RestTemplate请求,报java.lang.IllegalStateException: No instances avaliable for ip地址 异常

RestTemplate的异常 Not enough variables available to expand