From 6afecf3370e66f6ecc1a57c1cbc1311ca9c96e52 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 1 Mar 2023 23:18:58 +0900 Subject: [PATCH] When getting ExceptionHandler in the controller, use target class in case of AOP Proxy. Fixes #2098 --- .../core/GenericResponseService.java | 9 +- .../api/v30/app202/Example2Controller.java | 23 +++++ .../api/v30/app202/ExampleController.java | 25 ++++++ .../api/v30/app202/MyExceptionHandler.java | 23 +++++ .../api/v30/app202/SpringDocApp202Test.java | 34 +++++++ .../test/resources/results/3.0.1/app202.json | 88 +++++++++++++++++++ 6 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/Example2Controller.java create mode 100644 springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/ExampleController.java create mode 100644 springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/MyExceptionHandler.java create mode 100644 springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/SpringDocApp202Test.java create mode 100644 springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app202.json diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericResponseService.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericResponseService.java index b39a5dc82..d9f68856d 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericResponseService.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericResponseService.java @@ -2,7 +2,7 @@ * * * * * * - * * * * Copyright 2019-2022 the original author or authors. + * * * * Copyright 2019-2023 the original author or authors. * * * * * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * * * you may not use this file except in compliance with the License. @@ -668,7 +668,12 @@ else if (returnType instanceof ParameterizedType) { */ private synchronized Map getGenericMapResponse(Class beanType) { List controllerAdviceInfosInThisBean = localExceptionHandlers.stream() - .filter(controllerInfo -> beanType.equals(controllerInfo.getControllerAdvice().getClass())) + .filter(controllerInfo -> { + Class objClz = controllerInfo.getControllerAdvice().getClass(); + if (org.springframework.aop.support.AopUtils.isAopProxy(controllerInfo.getControllerAdvice())) + objClz = org.springframework.aop.support.AopUtils.getTargetClass(controllerInfo.getControllerAdvice()); + return beanType.equals(objClz); + }) .collect(Collectors.toList()); Map genericApiResponseMap = controllerAdviceInfosInThisBean.stream() diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/Example2Controller.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/Example2Controller.java new file mode 100644 index 000000000..522df6183 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/Example2Controller.java @@ -0,0 +1,23 @@ +package test.org.springdoc.api.v30.app202; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/example2") +public class Example2Controller { + @GetMapping("/") + public void index() { + throw new IllegalArgumentException(); + } + + @ExceptionHandler(IllegalArgumentException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public String customControllerException() { + return "example"; + } +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/ExampleController.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/ExampleController.java new file mode 100644 index 000000000..930efbcb8 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/ExampleController.java @@ -0,0 +1,25 @@ +package test.org.springdoc.api.v30.app202; + +import org.springframework.http.HttpStatus; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/example") +@Validated +public class ExampleController { + @GetMapping("/") + public void index() { + throw new IllegalArgumentException(); + } + + @ExceptionHandler(IllegalArgumentException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + public String customControllerException() { + return "example"; + } +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/MyExceptionHandler.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/MyExceptionHandler.java new file mode 100644 index 000000000..240027d0f --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/MyExceptionHandler.java @@ -0,0 +1,23 @@ +package test.org.springdoc.api.v30.app202; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.http.HttpStatus; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ControllerAdvice +public class MyExceptionHandler { + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ExceptionHandler({ HttpRequestMethodNotSupportedException.class }) + @ResponseBody + public Map handleError() { + Map errorMap = new HashMap(); + errorMap.put("message", "error"); + return errorMap; + } +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/SpringDocApp202Test.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/SpringDocApp202Test.java new file mode 100644 index 000000000..1fb6239d5 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app202/SpringDocApp202Test.java @@ -0,0 +1,34 @@ +/* + * + * * + * * * + * * * * Copyright 2019-2023 the original author or authors. + * * * * + * * * * 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 + * * * * + * * * * https://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 test.org.springdoc.api.v30.app202; + +import test.org.springdoc.api.v30.AbstractSpringDocV30Test; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +public class SpringDocApp202Test extends AbstractSpringDocV30Test { + + @SpringBootApplication + static class SpringDocTestApp {} + +} diff --git a/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app202.json b/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app202.json new file mode 100644 index 000000000..0f5c99e89 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app202.json @@ -0,0 +1,88 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/example2/": { + "get": { + "tags": [ + "example-2-controller" + ], + "operationId": "index", + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "*/*": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + } + } + } + } + }, + "/example/": { + "get": { + "tags": [ + "example-controller" + ], + "operationId": "index_1", + "responses": { + "200": { + "description": "OK" + }, + "404": { + "description": "Not Found", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "*/*": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + } + } + } + } + } + }, + "components": {} +}