spring-cloud / spring-cloud/spring-cloud-gateway

Is it Intended for Exceptions Thrown in Gateway Filters to be Handled by @RestControllerAdvice in a Reactive Environment?

Open
#3,771 0 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

waiting-for-triage
Dominant language
Java
Stars
4.9k
Forks
3.5k
Avg merge
20h 57m
Merged PRs (30d)
8

Description

Hello,

I am using Spring Cloud Gateway in a reactive environment with Spring Boot, WebFlux, and Java 17. In our gateway application, we've set up a global exception handler using @RestControllerAdvice with an @ExceptionHandler for RuntimeException. Our expectation was that exceptions thrown within Gateway filters would be handled by WebFlux’s default error handling mechanism (for example, through an ErrorWebExceptionHandler), not by controller-level exception handlers.

However, we are observing that an exception thrown in our custom Gateway filter is caught by our global exception handler. This results in a custom error response (HTTP 200 OK with a message such as "RuntimeException Handler: exception occurred") rather than an appropriate error response with an HTTP error status.

What We're Observing:

  • We have a custom Gateway filter (HelloFilter) that deliberately throws a RuntimeException when the request method is not POST.
  • We also have a simple HelloController that handles both GET and POST requests for the /hello endpoint.
  • When sending a GET request to the /hello endpoint through the gateway, the exception from the filter is intercepted by our global exception handler (configured with @RestControllerAdvice), returning a 200 OK response with the message:
    RuntimeException Handler: exception occurred
    

Expected Behavior:
We expected that exceptions thrown in Gateway filters should not be handled by controller-level @RestControllerAdvice handlers. Instead, such exceptions should be handled by the WebFlux error handling mechanism (for example, a custom ErrorWebExceptionHandler), ensuring that appropriate HTTP error status codes (e.g., 4xx or 5xx) are returned.

Actual Behavior:
Currently, our configuration causes exceptions thrown in the Gateway filter chain to be caught by our global exception handler. This results in a 200 OK response along with our custom error message, which is counterintuitive in an error scenario.

Steps to Reproduce:

  1. Global Exception Handler:
    Define a global exception handler in your gateway application:

    @RestControllerAdvice
    public class GlobalExceptionHandler {
        @ExceptionHandler(RuntimeException.class)
        public ResponseEntity<String> handleRuntimeException(RuntimeException exception) {
            return ResponseEntity.ok("RuntimeException Handler: " + exception.getMessage());
        }
    }
    
  2. Custom Gateway Filter:
    Create a custom Gateway filter that throws a RuntimeException for non-POST requests:

    @Component
    public class HelloFilter extends AbstractGatewayFilterFactory<Object> {
        @Override
        public GatewayFilter apply(Object config) {
            return (exchange, chain) -> {
                if (exchange.getRequest().getMethod() != HttpMethod.POST) {
                    throw new RuntimeException("exception occurred");
                }
                return chain.filter(exchange);
            };
        }
    }
    
  3. HelloController:
    Add a simple controller to demonstrate normal downstream behavior:

    @RestController
    public class HelloController {
        @PostMapping("/hello")
        public String hello() {
            return "Hello World";
        }
        
        @GetMapping("/hello")
        public String badHello() {
            return "Bad Hello";
        }
    }
    

    Note: While the HelloController provides a basic endpoint for comparison, the key focus is on the exception thrown by the HelloFilter.

  4. Route Configuration (application.yml):

    spring:
      cloud:
        gateway:
          routes:
            - id: first-service
              uri: lb://first-service
              predicates:
                - Path=/hello/**
              filters:
                - HelloFilter
    server:
      port: 19091
    

    Ensure that the downstream service (first-service) is properly registered or reachable, so that normal POST requests to /hello are forwarded to the HelloController.

  5. Test:

    • Sending a POST request to http://localhost:19091/hello should route correctly to the HelloController and return "Hello World".
    • Sending a GET request to http://localhost:19091/hello will trigger the HelloFilter (because the request method is not POST), causing a RuntimeException, which is then intercepted by the GlobalExceptionHandler.

    The response you observe for the GET request is:

    HTTP/1.1 200 OK
    Content-Type: text/plain;charset=UTF-8
    Content-Length: 44
    
    RuntimeException Handler: exception occurred
    

Environment:

  • Spring Cloud Gateway version: 4.2.1
  • Spring Boot version: 3.4.4
  • Java version: 17
  • Reactive Environment: WebFlux

Discussion/Questions:

  1. Is it intended behavior in a reactive Spring Cloud Gateway environment that exceptions thrown in Gateway filters are handled by global @RestControllerAdvice exception handlers?
  2. Should errors originating at the Gateway level be handled exclusively by WebFlux’s default error handling mechanism (e.g., through an ErrorWebExceptionHandler) rather than by controller-level exception handlers?
  3. Are there any best practices or recommended configurations for handling exceptions in a Gateway environment to ensure that error responses include appropriate HTTP status codes?

Additional Context:
Any insights, documentation references, or guidance regarding proper error handling configuration in the context of Spring Cloud Gateway's reactive environment would be greatly appreciated.

Thank you for your assistance!

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the supplied HelloFilter and GlobalExceptionHandler setup from the issue, using application.yml to configure the route. Review how exceptions move through the reactive gateway filter chain and ErrorWebExceptionHandler. Done means documenting the intended handler and confirming that gateway failures produce an appropriate HTTP error status rather than the shown 200 response.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring-boot
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.