Skip to content

Commit 313214e

Browse files
committed
Response schema for overridden methods of ResponseEntityExceptionHandler is not generated. Fixes #1208
1 parent 7d129dc commit 313214e

File tree

6 files changed

+183
-4
lines changed

6 files changed

+183
-4
lines changed

springdoc-openapi-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ protected synchronized OpenAPI getOpenApi() {
298298
// run the optional customisers
299299
List<Server> servers = openApi.getServers();
300300
openApiCustomisers.ifPresent(apiCustomisers -> apiCustomisers.forEach(openApiCustomiser -> openApiCustomiser.customise(openApi)));
301-
if(!servers.equals(openApi.getServers()))
301+
if (servers != null && !servers.equals(openApi.getServers()))
302302
openAPIService.setServersPresent(true);
303303

304304
openAPIService.setCachedOpenAPI(openApi);

springdoc-openapi-common/src/main/java/org/springdoc/core/GenericResponseService.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,16 @@
4646
import io.swagger.v3.oas.models.responses.ApiResponses;
4747
import org.apache.commons.lang3.ArrayUtils;
4848
import org.apache.commons.lang3.StringUtils;
49+
import org.slf4j.Logger;
50+
import org.slf4j.LoggerFactory;
4951

5052
import org.springframework.core.MethodParameter;
5153
import org.springframework.core.ResolvableType;
5254
import org.springframework.core.annotation.AnnotatedElementUtils;
5355
import org.springframework.http.HttpStatus;
5456
import org.springframework.util.CollectionUtils;
5557
import org.springframework.util.ReflectionUtils;
58+
import org.springframework.web.bind.annotation.ControllerAdvice;
5659
import org.springframework.web.bind.annotation.ExceptionHandler;
5760
import org.springframework.web.bind.annotation.RequestMapping;
5861
import org.springframework.web.bind.annotation.ResponseStatus;
@@ -96,6 +99,11 @@ public class GenericResponseService {
9699
*/
97100
private List<ControllerAdviceInfo> controllerAdviceInfos = new ArrayList<>();
98101

102+
/**
103+
* The constant LOGGER.
104+
*/
105+
private static final Logger LOGGER = LoggerFactory.getLogger(GenericResponseService.class);
106+
99107
/**
100108
* Instantiates a new Generic response builder.
101109
*
@@ -153,7 +161,11 @@ public void buildGenericResponse(Components components, Map<String, Object> find
153161
if (org.springframework.aop.support.AopUtils.isAopProxy(controllerAdvice))
154162
objClz = org.springframework.aop.support.AopUtils.getTargetClass(controllerAdvice);
155163
ControllerAdviceInfo controllerAdviceInfo = new ControllerAdviceInfo(controllerAdvice);
156-
Arrays.stream(ReflectionUtils.getAllDeclaredMethods(objClz)).filter(m -> m.isAnnotationPresent(ExceptionHandler.class)).forEach(methods::add);
164+
Class<?> finalObjClz = objClz;
165+
Arrays.stream(ReflectionUtils.getAllDeclaredMethods(objClz))
166+
.filter(m -> m.isAnnotationPresent(ExceptionHandler.class)
167+
|| isResponseEntityExceptionHandlerMethod(m)
168+
).forEach(methods::add);
157169
// for each one build ApiResponse and add it to existing responses
158170
for (Method method : methods) {
159171
if (!operationService.isHidden(method)) {
@@ -182,6 +194,26 @@ public void buildGenericResponse(Components components, Map<String, Object> find
182194
}
183195
}
184196

197+
/**
198+
* Is response entity exception handler method boolean.
199+
*
200+
* @param m the m
201+
* @return the boolean
202+
*/
203+
private boolean isResponseEntityExceptionHandlerMethod(Method m) {
204+
if (AnnotatedElementUtils.hasAnnotation(m.getDeclaringClass(), ControllerAdvice.class)) {
205+
try {
206+
Class aClass = Class.forName("org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler");
207+
if (aClass.isAssignableFrom(m.getDeclaringClass()) && ReflectionUtils.findMethod(aClass, m.getName(), m.getParameterTypes()) != null)
208+
return true;
209+
}
210+
catch (ClassNotFoundException e) {
211+
LOGGER.trace(e.getMessage());
212+
}
213+
}
214+
return false;
215+
}
216+
185217
/**
186218
* Compute response from doc map.
187219
*
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package test.org.springdoc.api.app59;
2+
3+
import javax.validation.constraints.NotNull;
4+
5+
import com.fasterxml.jackson.annotation.JsonProperty;
6+
7+
public class HelloBody {
8+
9+
@NotNull
10+
@JsonProperty
11+
private String helloValue;
12+
13+
14+
}

springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app59/HelloController.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@
1818

1919
package test.org.springdoc.api.app59;
2020

21+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
22+
23+
import org.springframework.validation.annotation.Validated;
2124
import org.springframework.web.bind.annotation.GetMapping;
25+
import org.springframework.web.bind.annotation.PostMapping;
26+
import org.springframework.web.bind.annotation.RequestBody;
2227
import org.springframework.web.bind.annotation.RestController;
2328

2429
@RestController
@@ -27,4 +32,10 @@ public class HelloController {
2732
@GetMapping("/example")
2833
public void test() {
2934
}
35+
36+
@PostMapping("/hello")
37+
@ApiResponse(responseCode = "200", description = "The server accepted your hello.")
38+
String hello(@Validated @RequestBody final HelloBody helloBody) {
39+
return "World!";
40+
}
3041
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package test.org.springdoc.api.app59;
2+
3+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
4+
5+
import org.springframework.core.Ordered;
6+
import org.springframework.core.annotation.Order;
7+
import org.springframework.http.HttpHeaders;
8+
import org.springframework.http.HttpStatus;
9+
import org.springframework.http.ResponseEntity;
10+
import org.springframework.web.bind.MethodArgumentNotValidException;
11+
import org.springframework.web.bind.annotation.ExceptionHandler;
12+
import org.springframework.web.bind.annotation.ResponseBody;
13+
import org.springframework.web.bind.annotation.RestControllerAdvice;
14+
import org.springframework.web.context.request.WebRequest;
15+
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
16+
17+
@Order(Ordered.HIGHEST_PRECEDENCE)
18+
@RestControllerAdvice
19+
public class HelloExceptionHandler extends ResponseEntityExceptionHandler {
20+
21+
@ExceptionHandler(Exception.class)
22+
@ResponseBody
23+
@ApiResponse(responseCode = "500",
24+
description = "An unknown error occurred"
25+
)
26+
protected ResponseEntity<Object> handleException() {
27+
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
28+
}
29+
30+
@Override
31+
@ApiResponse(responseCode = "400",
32+
description = "The request is malformed or information is missing."
33+
)
34+
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
35+
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
36+
}
37+
38+
}

springdoc-openapi-webmvc-core/src/test/resources/results/app59.json

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,83 @@
1111
}
1212
],
1313
"paths": {
14+
"/hello": {
15+
"post": {
16+
"tags": [
17+
"hello-controller"
18+
],
19+
"operationId": "hello",
20+
"requestBody": {
21+
"content": {
22+
"application/json": {
23+
"schema": {
24+
"$ref": "#/components/schemas/HelloBody"
25+
}
26+
}
27+
},
28+
"required": true
29+
},
30+
"responses": {
31+
"500": {
32+
"description": "An unknown error occurred",
33+
"content": {
34+
"*/*": {
35+
"schema": {
36+
"type": "object"
37+
}
38+
}
39+
}
40+
},
41+
"400": {
42+
"description": "The request is malformed or information is missing.",
43+
"content": {
44+
"*/*": {
45+
"schema": {
46+
"type": "object"
47+
}
48+
}
49+
}
50+
},
51+
"200": {
52+
"description": "The server accepted your hello.",
53+
"content": {
54+
"*/*": {
55+
"schema": {
56+
"type": "string"
57+
}
58+
}
59+
}
60+
}
61+
}
62+
}
63+
},
1464
"/example": {
1565
"get": {
1666
"tags": [
1767
"hello-controller"
1868
],
1969
"operationId": "test",
2070
"responses": {
71+
"500": {
72+
"description": "An unknown error occurred",
73+
"content": {
74+
"*/*": {
75+
"schema": {
76+
"type": "object"
77+
}
78+
}
79+
}
80+
},
81+
"400": {
82+
"description": "The request is malformed or information is missing.",
83+
"content": {
84+
"*/*": {
85+
"schema": {
86+
"type": "object"
87+
}
88+
}
89+
}
90+
},
2191
"200": {
2292
"description": "OK"
2393
}
@@ -26,5 +96,19 @@
2696
}
2797
}
2898
},
29-
"components": {}
30-
}
99+
"components": {
100+
"schemas": {
101+
"HelloBody": {
102+
"required": [
103+
"helloValue"
104+
],
105+
"type": "object",
106+
"properties": {
107+
"helloValue": {
108+
"type": "string"
109+
}
110+
}
111+
}
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)