代码之家  ›  专栏  ›  技术社区  ›  Paul Marcelin Bejan

如何检索FeignClient生成的errorResponse?

  •  0
  • Paul Marcelin Bejan  · 技术社区  · 1 年前

    我正在寻找一种方法来检索由异常引起的原始响应,该异常已通过FeignClient在称为RemoteService的微服务中引发。

    在微服务“地图”中,我公开了一个API来通过id查找位置:

    @RestController
    @RequiredArgsConstructor
    @RequestMapping("/api/location")
    public class LocationRestController {
    
        private final LocationService locationService;
    
        @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
        public @ResponseBody LocationDto findById(@PathVariable Long id) throws FunctionalException {
            return locationService.findByIdToDto(id);
        }
    
    }
    

    如果找到,API将返回LocationDto,否则,它将抛出由RestControllerAdvice处理的FunctionalException,以便返回ExceptionResponse:

    @RestControllerAdvice
    public class ExceptionRestController {
    
        @ResponseBody
        @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
        @ExceptionHandler(value = { FunctionalException.class })
        public ExceptionResponse handleFunctionalException(FunctionalException exception) {
            return new ExceptionResponse(exception, Map.of("uniqueIdentifier", exception.getUniqueIdentifier()));
        }
    
        @ResponseBody
        @ExceptionHandler(Exception.class)
        @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
        public ExceptionResponse handleAllUncaughtException(Exception exception) {
            return new ExceptionResponse(exception);
        }
    
    }
    

    异常响应是:

    @Log4j2
    @Data
    public class ExceptionResponse {
    
        @JsonProperty
        @JsonSerialize(using = LocalDateTimeSerializer.class)
        @JsonDeserialize(using = LocalDateTimeDeserializer.class)
        private final LocalDateTime timestamp;
    
        @JsonProperty
        private final String status;
    
        @JsonProperty
        private final String error;
    
        @JsonProperty
        private String uniqueIdentifier = "";
    
        @JsonProperty
        private final String message;
    
        @JsonProperty
        private final String stackTrace;
        
        public ExceptionResponse(Exception exception) {
            timestamp = LocalDateTime.now();
            status = String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value());
            error = HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
            message = exception.getMessage();
            stackTrace = ExceptionUtils.getStackTrace(exception);
            log.error(stackTrace);
        }
        
        public ExceptionResponse(Exception exception, Map<String, String> fieldValue) {
            timestamp = LocalDateTime.now();
            status = fieldValue.getOrDefault("status", String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()));
            error = fieldValue.getOrDefault("error", HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
            uniqueIdentifier = fieldValue.getOrDefault("uniqueIdentifier", "");
            message = fieldValue.getOrDefault("message", exception.getMessage());
            stackTrace = ExceptionUtils.getStackTrace(exception);
            log.error(stackTrace);
        }
    
        @JsonCreator
        public ExceptionResponse(LocalDateTime timestamp, String status, String error, String uniqueIdentifier,
                String message, String stackTrace) {
            this.timestamp = timestamp;
            this.status = status;
            this.error = error;
            this.uniqueIdentifier = uniqueIdentifier;
            this.message = message;
            this.stackTrace = stackTrace;
        }
    
    }
    

    在微服务“超市”中,我不得不使用OpenFeign将微服务的API称为“地图”:

    @FeignClient(name = "mapsClient", url = "http://localhost:9888", configuration = ClientConfiguration.class)
    public interface MapsRemoteService {
    
        @GetMapping(value = "/api/location/{id}")
        LocationDto findLocationById(@PathVariable("id") Long id);
    
    }
    

    现在,当用DB中不存在的id调用这个MapsMoteService.findLocationById(id)时,我想检索ExceptionResponse

    所以我尝试创建一个ErrorDecoder:

    客户端配置:

    @Configuration
    public class ClientConfiguration {
    
        @Bean
        public OkHttpClient client() {
            return new OkHttpClient();
        }
    
        @Bean
        Logger.Level feignLoggerLevel() {
            return Logger.Level.FULL;
        }
        
        @Bean
        public ErrorDecoder errorDecoder() {
            return new ClientErrorDecoder();
        }
    
    }
    

    错误解码器:

    public class ClientErrorDecoder implements ErrorDecoder {
    
        @Override
        public Exception decode(String methodKey, Response response) {
            ObjectMapper mapper = new ObjectMapper();
            RemoteServiceException remoteServiceException = null;
            
            // Attempt 1
            try (InputStream inputStream = response.body().asInputStream())  {
                ExceptionResponse exceptionResponse = mapper.readValue(inputStream, ExceptionResponse.class);
                remoteServiceException = buildRemoteServiceException(exceptionResponse);
            } catch (IOException e) {
    
            }
            
            // Attempt 2
            try (Reader reader = response.body().asReader())  {
                ExceptionResponse exceptionResponse = mapper.readValue(reader, ExceptionResponse.class);
                remoteServiceException = buildRemoteServiceException(exceptionResponse);
            } catch (IOException e) {
    
            }
            
            // Attempt 3
            try {
                String body = response.body().toString();
                ExceptionResponse exceptionResponse = mapper.readValue(body, ExceptionResponse.class);
                remoteServiceException = buildRemoteServiceException(exceptionResponse);
            } catch (IOException e) {
    
            }
            
            if(remoteServiceException == null){
                return new ErrorDecoder.Default().decode(methodKey, response);
            }
    
            return remoteServiceException;
        }
        
        private static RemoteServiceException buildRemoteServiceException(ExceptionResponse exceptionResponse) {
            return new RemoteServiceException(exceptionResponse.getMessage(), exceptionResponse.getUniqueIdentifier());
        }
    
    }
    

    我尝试了我在网上找到的所有东西,但都不起作用。

    奇怪的是,我将inputStream作为临时文件保存到资源文件夹中。这是正确的。

    我使用以下代码编写文件:

    try (InputStream inputStream = response.body().asInputStream())  {
        File targetFile = new File("src/main/resources/targetFile.tmp");
    
        java.nio.file.Files.copy(
                  inputStream, 
                  targetFile.toPath(), 
                  StandardCopyOption.REPLACE_EXISTING);
    
        IOUtils.closeQuietly(inputStream);
    } catch (IOException e) {
        // return new ErrorDecoder.Default().decode(methodKey, response);
    }
    

    这是文件的内容:

    {
        "timestamp":"2023-07-25T22:38:48.530815",
        "status":"500",
        "error":"Internal Server Error",
        "uniqueIdentifier":"bc4bd3ae-2d40-4863-9e9f-6b10ecece27d",
        "message":"No Location found with the id: 10",
        "stackTrace":"7711 characters avoided for readability"
    }
    

    并且它完美地表示具有6个字段的类ExceptionResponse。

    所以在使用时似乎出现了问题

    mapper.readValue(inputStream, ExceptionResponse.class);
    
    0 回复  |  直到 1 年前
    推荐文章