FeignRetryableException When Service is down - java

I am using OpenFeign client in Spring Boot without using Ribbon or Eureka. I created a custom error decoder which handles response errors as intended but connection refused errors seem to bypass my custom decoder.
P.S. When my remote service is up, I can make requests and receive responses.
I am new to Java and Spring and I am wondering if I need to wrap all my calls with try catch, or adding my custom error handler should be catching the error since it seems cleaner to handle all errors in one place
public class FeignErrorDecoder implements ErrorDecoder {
private final ErrorDecoder defaultErrorDecoder = new Default();
#Override
public Exception decode(String methodKey, Response response) {
if (response.status() >= 400 && response.status() <= 499) {
//handle with custom exception
}
if (response.status() >=500) {
//handle with custom exception
}
return defaultErrorDecoder.decode(methodKey, response);
}
}
#Configuration
public class FeignConfig {
//other beans here
#Bean
public ErrorDecoder feignErrorDecoder() {
return new FeignErrorDecoder();
}
}

Related

Is throws declaration in feign client useless without defined error decoder?

I have a feign client like this
#FeignClient(name = "client")
public interface SomeClient {
#RequestLine("GET /?q={q}")
void execute(URI baseUrl, #Param("q") String q) throws SomeExceptionInMyCode;
}
Looking to this throws SomeExceptionInMyCode I'm asking myself when this exception will be thrown. There is no configuration for client defined, no error decoder. Exception looks like this.
public class SomeExceptionInMyCode extends Exception{
private final int statusCode;
private final String reason;
private final String body;
// getters and setters
}
Will there be an automatic attempt to decode HTTP response to this exception in case of failure? Or throws SomeExceptionInMyCode is useless and can be removed without any impact.
I searched inside my code and this exception is never created.
Will there be an automatic attempt to decode http response to this exception in case of failure?
Nope, it doesn't work like this and the SomeExceptionMyCode will not be thrown. The throws clause is useless. Even if the endpoint throws this exception from its implementation, it will be wrapped as a cause of FeignException.
The correct way to handle feign client exceptions is using Custom exception handling implementing ErrorDecoder:
public class StashErrorDecoder implements ErrorDecoder {
#Override
public Exception decode(String methodKey, Response response) {
if (response.status() >= 400 && response.status() <= 499) {
// return 4XX exception
}
if (response.status() >= 500 && response.status() <= 599) {
// return 5XX exception
}
}
}
At this point you can perform the custom exception creation and rethrowing.
An alternative solution is to use Spring-alike #RestControllerAdvice:
#RestControllerAdvice
public class ExceptionHandler {
#ExceptionHandler(FeignException.class)
public String handleFeignStatusException(FeignException e, HttpServletResponse response) {
// ...
}
}
What if the StashErrorDecoder throws a checked exception? That is allowed. In this case the throws clause in the interface surely helps. This way you can catch and handle the exception thrown by Feign. At least it should work this way.

Spring WebFlux block Http.CONNECT method

We have some security tests around our company in which the apps are tested in different ways. One of them is to try a CONNECT like:
telnet localhost 8080
CONNECT http://test.com HTTP/1.1
and in that case to return a 400 or 405. The existing Spring MVC apps return a 400, but it seems that our new Spring WebFlux:5.1.2.RELEASE app(Netty server) return a 200.
The first thing that I did was to shift to latest spring WebFlux version:5.1.4.RELEASE, and in this case the response http error code was:404, but was still not good enough. So I tried to:
Create a webFilter
Modify the CORS filter
Modify Spring Security chain
,but all these solutions failed. How'd you fix that? It would be a good idea to create a custom http handler ?
I've created a custom http handler:
public class AppContextPathCompositeHandler extends ContextPathCompositeHandler {
public AppContextPathCompositeHandler(Map<String, ? extends HttpHandler> handlerMap) {
super(handlerMap);
}
#Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
if (HttpMethod.CONNECT.name().equals(request.getMethodValue())) {
response.setStatusCode(HttpStatus.METHOD_NOT_ALLOWED);
return response.setComplete();
}
return super.handle(request, response);
}
}
and it was configured like:
#Configuration
public class NettyReactiveWebServerConfig extends NettyReactiveWebServerFactory {
#Value("${server.context-path}")
private String contextPath;
#Override
public WebServer getWebServer(HttpHandler httpHandler) {
Map<String, HttpHandler> handlerMap = new HashMap<>();
handlerMap.put(contextPath, httpHandler);
return super.getWebServer(new AppContextPathCompositeHandler(handlerMap));
}
}

Dropwizard custom ExceptionMapper for javax.validation.ConstraintViolationException

I'm using Dropwizard and would like to create a custom ExceptionMapper to handle javax.validation.ConstraintViolationException. I've created a customer mapper:
#Provider
public class MyExceptionMapper implements ExceptionMapper<Exception> {
public Response toResponse(Exception exception) {
return Response.status(500)
.entity(exception.getMessage())
.type(MediaType.TEXT_PLAIN)
.build();
}
}
and configured it in my Dropwizard app:
#Override
public void run(HelloWorldConfiguration configuration, Environment environment) {
ServerFactory serverFactory = configuration.getServerFactory();
if (serverFactory instanceof AbstractServerFactory) {
((AbstractServerFactory) serverFactory).setRegisterDefaultExceptionMappers(false);
}
environment.jersey().register(new MyExceptionMapper());
}
When a resource/controller throws a javax.validation.ConstraintViolationException exception it's not handled by my custom mapper, instead it goes through org.glassfish.jersey.server.validation.internal.ValidationExceptionMapper which returns a 400 which is not what I want.
Is it possible to override/remove Jersey's mapper? I would have thought setting setRegisterDefaultExceptionMappers(false); would do the trick but it seems that it doesn't.

Hystrix - how to register ExceptionMapper

My Hystrix/Feign app makes calls to other web services.
I would like to propagate error codes/messages from these web services.
I implemented ErrorDecoder, which correctly decodes exceptions returned and rethrow them.
Unfortunately these Exceptions are wrapped by HystrixRuntimeException and the JSON returned in not what I want (generic error message, always 500 http status).
Most likely I need an ExceptionMapper, I created one like this:
#Provider
public class GlobalExceptionHandler implements
ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable e) {
System.out.println("ABCD 1");
if(e instanceof HystrixRuntimeException){
System.out.println("ABCD 2");
if(e.getCause() != null && e.getCause() instanceof HttpStatusCodeException)
{
System.out.println("ABCD 3");
HttpStatusCodeException exc = (HttpStatusCodeException)e.getCause();
return Response.status(exc.getStatusCode().value())
.entity(exc.getMessage())
.type(MediaType.APPLICATION_JSON).build();
}
}
return Response.status(500).entity("Internal server error").build();
}
}
Unfortunately this code is not being picked-up by my application (debug statements are not visible in logs).
How could I register it with my Application?
I couldn't make use of an ExceptionMapper.
I solved this problem using ResponseEntityExceptionHandler.
Here is the code:
#EnableWebMvc
#ControllerAdvice
public class ServiceExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(HystrixRuntimeException.class)
#ResponseBody
ResponseEntity<String> handleControllerException(HttpServletRequest req, Throwable ex) {
if(ex instanceof HystrixRuntimeException) {
HttpStatusCodeException exc = (HttpStatusCodeException)ex.getCause();
return new ResponseEntity<>(exc.getResponseBodyAsString(), exc.getStatusCode());
}
return new ResponseEntity<String>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}

Jersey Exception Mapping Different Context

So I am build an mvc application using Jersey. A method that accepts Path parameters (#PathParam).
If a custom exception is thrown (ExampleException) then a 404 Not Found response is returned using an exception mapper.
#Provider
public class ExampleExceptionMapper implements ExceptionMapper<ExampleException> {
#Override
public Response toResponse(ExampleException ex) {
return Response.status(Status.NOT_FOUND).entity("Not Found - " + ex.getMessage()).build();
}
}
However, I am implementing #FormParam's so a user POSTs to the server. The same exact exception is raised, but instead I should return a 400 Bad Request response. Without modifying the exception how would I be able to make the exception mapper return the proper response code?
Simplest way is create multiple ExceptionMappers, each for specific subclass of ExampleException.
But you want to have the same exception for both cases and decide whether to throw 404 for GET/PathParam and POST/FormParam, you can inject the request into the mapper and check what method it is:
#Provider
public class ExampleExceptionMapper implements ExceptionMapper<ExampleException> {
#Context Request request;
#Override
public Response toResponse(ExampleException ex) {
if ("POST".equals(requset.getMethod()))
return Response.status(Status.BAD_REQUEST).build();
else
return Response.status(Status.NOT_FOUND).entity("Not Found - " + ex.getMessage()).build();
}
}
If you want to decide by PathParams, you can inject UriInfo:
#Provider
public class ExampleExceptionMapper implements ExceptionMapper<ExampleException> {
#Context UriInfo info;
#Override
public Response toResponse(ExampleException ex) {
if (info.getPathParameters().isEmpty())) //please make better condition based on your needs
return Response.status(Status.BAD_REQUEST).build();
else
return Response.status(Status.NOT_FOUND).entity("Not Found - " + ex.getMessage()).build();
}
}

Categories