Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如果Controller 的方法使用了 HttpServletResponse 入参,那么会导致 ControllerAdvice 拦截器失效,从而导致 全局响应格式失效 #66

Open
onekr-billy opened this issue Jan 3, 2025 · 0 comments

Comments

@onekr-billy
Copy link

描述 Bug (Describe the bug)

在 Spring Boot 控制器方法中添加 HttpServletResponse 作为参数会导致 @ControllerAdvice 组件(特别是 ResponseBodyAdvice,并可能影响通过 @ExceptionHandler 实现的一致性错误处理)被绕过。这会导致响应格式不一致,并可能破坏集中式异常处理。

重现步骤 (To Reproduce)

创建一个 Spring Boot 项目,其中包含一个实现了 ResponseBodyAdvice 的 @ControllerAdvice 组件,用于全局响应格式化(例如,将响应包装在 Result 对象中)。

@ControllerAdvice
public class GlobalResponseHandler implements ResponseBodyAdvice<Object> {
    // ... (实现代码,如同之前的回复中所示)
}

创建一个包含两个方法的控制器:一个不带 HttpServletResponse,另一个带 HttpServletResponse。

@RestController
public class MyController {

    @GetMapping("/test")
    public String test() {
        return "hello";
    }

    @GetMapping("/testWithResponse")
    public void testWithResponse(HttpServletResponse response) throws IOException {
        response.getWriter().write("hello with response");
    }
}

向两个端点发送请求。

观察到 /test 端点的响应被 ResponseBodyAdvice 正确格式化(例如,包装在 Result 对象中),而 /testWithResponse 端点的响应则没有。

预期行为 (Expected behavior)

无论 HttpServletResponse 是否作为参数存在,@ControllerAdvice 组件都应一致地应用其逻辑。在这种情况下,ResponseBodyAdvice 应该始终格式化响应,@ExceptionHandler 应该始终处理异常并格式化错误响应。

截图 (Screenshots)

(如果适用,你可以在此处包含截图。例如,显示不同响应格式的网络请求的截图。)

示例:

/test 响应(正确):

{
  "code": 200,
  "message": "success",
  "data": "hello"
}

/testWithResponse 响应(不正确):

hello with response
桌面环境 (Desktop)

操作系统 (OS): (你的操作系统,例如 macOS、Windows、Linux)
浏览器 (Browser): (你的浏览器,例如 Chrome、Firefox)
版本 (Version): (你的浏览器版本)
移动设备 (Smartphone)

(在这种特定情况下不适用,因为这是服务器端问题)

补充说明 (Additional context)

出现此行为的原因是,当 HttpServletResponse 是方法参数时,Spring MVC 假定控制器方法将完全处理响应,从而绕过 ResponseBodyAdvice 拦截器。这可能导致 API 响应不一致,并使集中式异常处理效果降低。

建议的解决方案 (Proposed Solution) (这将包含在 PR 的提交消息/描述中)

建议的解决方案是尽可能避免直接使用 HttpServletResponse 作为方法参数。使用 ResponseEntity 可以更好地控制响应,如果绝对需要直接访问 HttpServletResponse,则使用 ServletWebRequest。例如,可以将有问题的方法重写为:

@GetMapping("/testWithResponseFixed")
public ResponseEntity<String> testWithResponseFixed() {
    return ResponseEntity.ok("hello with response");
}

或者使用 ServletWebRequest

@GetMapping("/testWithResponseFixed")
public String testWithResponseFixed(ServletWebRequest webRequest) throws IOException {
        webRequest.getResponse().getWriter().write("hello with response");
        return null; 
}

这种方法保持了响应格式的一致性,并允许 @ControllerAdvice 按预期工作。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant