From 88c7e2cc3cc5cc59376bde3eeb8b8f2d3e89366a Mon Sep 17 00:00:00 2001 From: Sejin Oh Date: Fri, 31 Jul 2026 09:42:01 +0900 Subject: [PATCH] fix: decouple HTTP status from body code in Zuul BlockResponse ZuulBlockFallbackProvider reused BlockResponse#code as both the response body's business code and the actual HTTP status code, so a custom fallback provider could not set body code (e.g. 10018) while keeping HTTP status 200. Add a separate `status` field for the HTTP status, keep `code` for the body only, and keep the old 3-arg constructor for backward compatibility (status defaults to code). Fixes #2985 --- .../gateway/zuul/fallback/BlockResponse.java | 25 ++++++++++- .../zuul/filters/SentinelZuulPreFilter.java | 2 +- .../zuul/fallback/BlockResponseTest.java | 44 +++++++++++++++++++ .../gateway/zuul2/fallback/BlockResponse.java | 25 ++++++++++- .../endpoint/SentinelZuulEndpoint.java | 2 +- .../zuul2/fallback/BlockResponseTest.java | 44 +++++++++++++++++++ 6 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/BlockResponseTest.java create mode 100644 sentinel-adapter/sentinel-zuul2-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/BlockResponseTest.java diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/BlockResponse.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/BlockResponse.java index 435850fce6..6b1a9361aa 100644 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/BlockResponse.java +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/BlockResponse.java @@ -24,19 +24,42 @@ public class BlockResponse { /** - * HTTP status code. + * HTTP status code used for the actual HTTP response. + */ + private int status; + + /** + * Business code carried in the response body, independent of {@link #status}. */ private int code; private String message; private String route; + /** + * @deprecated use {@link #BlockResponse(int, int, String, String)} instead. + * The given {@code code} will be used as both the HTTP status and the body code. + */ + @Deprecated public BlockResponse(int code, String message, String route) { + this(code, code, message, route); + } + + public BlockResponse(int status, int code, String message, String route) { + this.status = status; this.code = code; this.message = message; this.route = route; } + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + public int getCode() { return code; } diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java index 95e643281d..ed26232468 100644 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java @@ -142,7 +142,7 @@ public Object run() throws ZuulException { // Set fallback response. ctx.setResponseBody(blockResponse.toString()); - ctx.setResponseStatusCode(blockResponse.getCode()); + ctx.setResponseStatusCode(blockResponse.getStatus()); // Set Response ContentType ctx.getResponse().setContentType("application/json; charset=utf-8"); } finally { diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/BlockResponseTest.java b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/BlockResponseTest.java new file mode 100644 index 0000000000..9bdb4fbed9 --- /dev/null +++ b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/BlockResponseTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Test cases for {@link BlockResponse}. + */ +public class BlockResponseTest { + + @Test + public void testDeprecatedConstructorKeepsStatusAndCodeInSync() { + BlockResponse response = new BlockResponse(429, "blocked", "/foo"); + + Assert.assertEquals(429, response.getStatus()); + Assert.assertEquals(429, response.getCode()); + } + + @Test + public void testStatusAndCodeCanBeCustomizedIndependently() { + // Body code can differ from the actual HTTP status, see issue #2985. + BlockResponse response = new BlockResponse(200, 10018, "blocked", "/foo"); + + Assert.assertEquals(200, response.getStatus()); + Assert.assertEquals(10018, response.getCode()); + Assert.assertTrue(response.toString().contains("\"code\":10018")); + } +} diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/BlockResponse.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/BlockResponse.java index 5209f2329a..dc35b69f85 100644 --- a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/BlockResponse.java +++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/BlockResponse.java @@ -24,19 +24,42 @@ public class BlockResponse { /** - * HTTP status code. + * HTTP status code used for the actual HTTP response. + */ + private int status; + + /** + * Business code carried in the response body, independent of {@link #status}. */ private int code; private String message; private String route; + /** + * @deprecated use {@link #BlockResponse(int, int, String, String)} instead. + * The given {@code code} will be used as both the HTTP status and the body code. + */ + @Deprecated public BlockResponse(int code, String message, String route) { + this(code, code, message, route); + } + + public BlockResponse(int status, int code, String message, String route) { + this.status = status; this.code = code; this.message = message; this.route = route; } + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + public int getCode() { return code; } diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/endpoint/SentinelZuulEndpoint.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/endpoint/SentinelZuulEndpoint.java index 6713127415..0c6ca95ff5 100644 --- a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/endpoint/SentinelZuulEndpoint.java +++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/endpoint/SentinelZuulEndpoint.java @@ -42,7 +42,7 @@ public HttpResponseMessage apply(HttpRequestMessage request) { ZuulBlockFallbackProvider zuulBlockFallbackProvider = ZuulBlockFallbackManager .getFallbackProvider(fallBackRoute); BlockResponse response = zuulBlockFallbackProvider.fallbackResponse(fallBackRoute, throwable); - HttpResponseMessage resp = new HttpResponseMessageImpl(context, request, response.getCode()); + HttpResponseMessage resp = new HttpResponseMessageImpl(context, request, response.getStatus()); resp.setBodyAsText(response.toString()); return resp; } diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/BlockResponseTest.java b/sentinel-adapter/sentinel-zuul2-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/BlockResponseTest.java new file mode 100644 index 0000000000..a811a75148 --- /dev/null +++ b/sentinel-adapter/sentinel-zuul2-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/BlockResponseTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.csp.sentinel.adapter.gateway.zuul2.fallback; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Test cases for {@link BlockResponse}. + */ +public class BlockResponseTest { + + @Test + public void testDeprecatedConstructorKeepsStatusAndCodeInSync() { + BlockResponse response = new BlockResponse(429, "blocked", "/foo"); + + Assert.assertEquals(429, response.getStatus()); + Assert.assertEquals(429, response.getCode()); + } + + @Test + public void testStatusAndCodeCanBeCustomizedIndependently() { + // Body code can differ from the actual HTTP status, see issue #2985. + BlockResponse response = new BlockResponse(200, 10018, "blocked", "/foo"); + + Assert.assertEquals(200, response.getStatus()); + Assert.assertEquals(10018, response.getCode()); + Assert.assertTrue(response.toString().contains("\"code\":10018")); + } +}