spring-cloud / spring-cloud-gateway

An API Gateway built on Spring Framework and Spring Boot providing routing and more.
http://cloud.spring.io
Apache License 2.0
4.53k stars 3.32k forks source link

Incorrect response codes for routing PUT/DELETE requests #3509

Closed predhme closed 1 month ago

predhme commented 2 months ago

I am using the latest Spring Cloud Gateway MVC via Spring Boot

I am attempting to place a relatively naive Gateway behind an Nginx Ingress that will sit in between a bunch of Microservices. The idea is that I can perform things like, authentication, authorization, etc in the gateway while deferring to the microservices for the business logic. I have a working implementation for the most part with one major problem. For example, consider an Entity microservice:

@RestController
@RequestMapping("/entity")
public class EntityController {

    @GetMapping(value = "/{id}", consumes = MediaType.ALL_VALUE)
    @ResponseStatus(HttpStatus.OK)
    public Entity getEntity(@PathVariable("id") String id) {
        return entityService.get(id);
    }

    @PutMapping(value = "/{id}", consumes = MediaType.ALL_VALUE)
    @ResponseStatus(HttpStatus.OK)
    public Entity updateEntity(@PathVariable("id") String id) {
        return entityService.update(id);
    }

    @DeleteMapping(value = "/{id}", consumes = MediaType.ALL_VALUE)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public Entity deleteEntity(@PathVariable("id") String id) {
        return entityService.delete(id);
    }

}

In front of the Entity service I have a route

@Value("${services.entity.uri}")
private String entityApiUri;

@Bean
public RouterFunction<ServerResponse> entityRoute() {
    return route()
            .route(path("/entity/**"), http(entityApiUri))
            .before(
                 auth().andThen(
                 audit()).andThen(
                 stripPrefix()))
            .build();
}

The GET endpoints work when I call it with valid and invalid IDs. That is, I get a 200 or 404 respectively. However, PUT and DELETE return a 500 error. In fact, looks like I am getting a FileNotFoundException:

2024-08-27T18:04:17.936Z ERROR 1 --- [api-gateway] [nio-8080-exec-2] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with path [/api-gateway] threw exception [Request processing failed: org.springframework.web.client.ResourceAccessException: I/O error on DELETE request for "http://entity-service:8080/entity-service/entity/1": http://entity-service:8080/entity-service/entity/1] with root cause
java.io.FileNotFoundException: http://entity-service:8080/entity-service/entity/1
    at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown Source)
    at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
    at org.springframework.http.client.SimpleClientHttpResponse.getBody(SimpleClientHttpResponse.java:89)
    at org.springframework.web.client.DefaultRestClient$DefaultConvertibleClientHttpResponse.getBody(DefaultRestClient.java:748)
    at org.springframework.cloud.gateway.server.mvc.handler.RestClientProxyExchange.doExchange(RestClientProxyExchange.java:50)
    at org.springframework.cloud.gateway.server.mvc.handler.RestClientProxyExchange.lambda$exchange$2(RestClientProxyExchange.java:42)
    at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchangeInternal(DefaultRestClient.java:495)
    at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchange(DefaultRestClient.java:465)
    at org.springframework.cloud.gateway.server.mvc.handler.RestClientProxyExchange.exchange(RestClientProxyExchange.java:42)
    at org.springframework.cloud.gateway.server.mvc.handler.ProxyExchangeHandlerFunction.handle(ProxyExchangeHandlerFunction.java:120)
    at org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions$LookupProxyExchangeHandlerFunction.handle(HandlerFunctions.java:107)
    at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$ofRequestProcessor$3(HandlerFilterFunction.java:83)
    at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$apply$2(HandlerFilterFunction.java:70)
    at org.springframework.web.servlet.function.support.HandlerFunctionAdapter.handle(HandlerFunctionAdapter.java:108)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
    at org.springframework.web.servlet.FrameworkServlet.doDelete(FrameworkServlet.java:936)
    at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:596)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
    at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
...

I would expect the PUT and DELETE methods to behave in the same fashion. That is, that they would return 404 errors.

predhme commented 1 month ago

I am fairly confident that this issue is caused by: https://github.com/spring-projects/spring-framework/issues/33020

That is slated to be fixed in 6.2.0 of spring. Once the Cloud packages pick up that new version, this issue should be resolved.

spencergibb commented 1 month ago

the 2024.0.0 snapshots and milestones use framework 6.2