У меня есть приложение SpringBoot для микроуслуг, в котором мне трудно извлекать сообщение FeignException из клиента feign. Я пробовал решение здесь , но когда вызывается эта строка String result = CharStreams.toString(reader);
, выдается IOException: stream is closed.
. Это странно, потому что я использовал этот точный код в другом проекте, и не было выдано исключение.
Это пользовательское исключение из вызываемой службы:
public class CustomException extends RuntimeException {
public CustomException(String s) {
super(s);
}
}
Это пользовательское исключение ответ:
public class CustomExceptionResponse {
private String message;
private HttpStatus status;
public CustomExceptionResponse(String message, HttpStatus status) {
this.message = message;
this.status = status;
}
//getters and setters
}
это пользовательский обработчик исключений в вызываемой службе:
@ControllerAdvice
@RestController
public class ExceptionsHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(CustomException.class)
public final ResponseEntity<CustomExceptionResponse> handleCustomException(Exception ex, WebRequest request) {
CustomExceptionResponse response = new CustomExceptionResponse(ex.getMessage(), HttpStatus.UNAUTHORIZED);
return new ResponseEntity<>(response, HttpStatus.UNAUTHORIZED);
}
}
и здесь выдается исключение:
@Service
public class CustomServiceImpl implements CustomService {
private CustomRepo repo;
@Autowired
public CustomServiceImpl(CustomRepo repo) {
this.repo = repo;
}
@Override
public int foo() {
//some repo call
if (condition)
throw new CustomException("Custom Message");
else
//return some result
}
}
при необходимости, я могу добавить дополнительную информацию
SpringBoot: 2.2.6 java: 1.8; springCloud: Hoxton.RELEASE NetflixEurekaClient: 2.2.2.RELEASE
EDIT Это клиентский класс симуляции:
public interface CustomServiceClient {
@GetMapping(value = "/custom/{arg}")
ResponseEntity<CustomEntity> customCheck(@PathVariable("arg") Long arg);
}
, откуда он вызывается:
public class CustomApiController implements CustomApi {
private final SessionServiceImpl sessionService;
private final CustomServiceClient customServiceClient;
@Autowired
public CustomApiController(CustomServiceClient customServiceClient,
SessionServiceImpl sessionService) {
this.customServiceClient = customServiceClient;
this.sessionService = sessionService;
}
@Override
public ResponseEntity<CustomEntity> customCheck(@NotNull @Valid @PathVariable("arg") Long arg) {
CallerCustomEntity entity;
ResponseEntity<CustomEntity> customCheckResponse = customServiceClient
.customCheck(arg);
entity = Transformer.transformToModel(customCheckResponse.getBody());
String sessionId = sessionService.createSession(entity);
entity.setSessionId(sessionId);
return ResponseEntity.ok(Transformer.transformToDto(entity));
}
}
У меня также есть этот класс congfig (в вызывающей программе):
@Configuration
public class ClientConfig {
@Autowired
private Environment env;
@PostConstruct
private void configure() {
// when the application runs in BCN a proxy is needed
if (Boolean.valueOf(env.getProperty("client.proxy.enabled"))) {
String proxyHost = env.getProperty("client.proxy.host");
String proxyPort = env.getProperty("client.proxy.port");
String proxyUser = env.getProperty("client.proxy.user");
String proxyPassword = env.getProperty("client.proxy.password");
String nonProxyHosts = env.getProperty("client.proxy.nonProxyHosts");
Authenticator.setDefault(
new Authenticator() {
@Override
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
proxyUser, proxyPassword.toCharArray());
}
}
);
System.setProperty("http.proxyHost", proxyHost);
System.setProperty("http.proxyPort", proxyPort);
System.setProperty("https.proxyHost", proxyHost);
System.setProperty("https.proxyPort", proxyPort);
System.setProperty("http.proxyUser", proxyUser);
System.setProperty("http.proxyPassword", proxyPassword);
System.setProperty("http.nonProxyHosts", nonProxyHosts); // for localhost and on-premise
}
}
}
и этот класс конфигурации (в вызывающей программе):
@Configuration
public class FeignConfiguration {
private static final int CONNECT_TIME_OUT_MILLIS = 150000;
private static final int READ_TIME_OUT_MILLIS = 150000;
@Bean
public Request.Options options() {
return new Request.Options(CONNECT_TIME_OUT_MILLIS, READ_TIME_OUT_MILLIS);
}
}
этот CORSFilter также ( в вызывающей программе):
@Component
public class SimpleCORSFilter implements Filter {
private final Logger log = LoggerFactory.getLogger(SimpleCORSFilter.class);
public SimpleCORSFilter() {
log.info("SimpleCORSFilter init");
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Session-ID, Accept, X-Requested-With, remember-me, "
+ "Authentication");
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void destroy() {
}
}
Трассировка стека:
java.io.IOException: stream is closed
at java.base/sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.ensureOpen(HttpURLConnection.java:3478)
at java.base/sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:3503)
at java.base/sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:297)
at java.base/sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:339)
at java.base/sun.nio.cs.StreamDecoder.read(StreamDecoder.java:188)
at java.base/java.io.InputStreamReader.read(InputStreamReader.java:185)
at java.base/java.io.Reader.read(Reader.java:229)
at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:1680)
at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:1659)
at org.apache.commons.io.IOUtils.copy(IOUtils.java:1636)
at org.apache.commons.io.IOUtils.toString(IOUtils.java:685)
at com.example.webAppService.exception.decoder.FeignErrorDecoder.decode(FeignErrorDecoder.java:25)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:151)
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:80)
at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)
at com.sun.proxy.$Proxy138.login(Unknown Source)
at com.example.webAppService.controller.rest.CustomApiController.customCheck(CustomApiController.java:42)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.example.webAppService.client.rest.config.SimpleCORSFilter.doFilter(SimpleCORSFilter.java:35)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:109)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1594)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:830)
EDIT 2 :
это кодировщик FeignErrorDecoder в вызывающей программе, как видно здесь :
@Component
public class FeignErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
Reader reader = null;
try {
reader = response.body().asReader();
String result = CharStreams.toString(reader);//this line throws the exception because the InputStream from reader has closed=true:
JSONObject jsonObject = new JSONObject(result);
if(jsonObject.has("httpStatus") && jsonObject.has("message")){
final HttpStatus status = HttpStatus.valueOf(jsonObject.get("httpStatus").toString());
final String message = jsonObject.get("message").toString();
return new ResponseStatusException(status,message);
}
else{
return new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Communication Error");
}
} catch (IOException e) {
e.printStackTrace();
}
return new Exception("Internal server Error");
}
}