Skip to content

Commit d5a7a0b

Browse files
author
bnasslahsen
committed
When using RestControllerAdvice, one of the responses description is missing and replaced with a default. Fixes #440
1 parent 56ab4d9 commit d5a7a0b

File tree

7 files changed

+307
-2
lines changed

7 files changed

+307
-2
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public ApiResponses build(Components components, HandlerMethod handlerMethod, Op
7777
genericMapResponse.forEach(apiResponses::addApiResponse);
7878
//Then use the apiResponses from documentation
7979
ApiResponses apiResponsesFromDoc = operation.getResponses();
80-
if (apiResponsesFromDoc != null)
80+
if (!CollectionUtils.isEmpty(apiResponsesFromDoc))
8181
apiResponsesFromDoc.forEach(apiResponses::addApiResponse);
8282
// for each one build ApiResponse and add it to existing responses
8383
// Fill api Responses
@@ -172,7 +172,7 @@ private void buildContentFromDoc(Components components, ApiResponses apiResponse
172172

173173
private void buildApiResponses(Components components, Method method, ApiResponses apiResponsesOp,
174174
MethodAttributes methodAttributes, boolean isGeneric) {
175-
if (!CollectionUtils.isEmpty(apiResponsesOp) && (apiResponsesOp.size() != genericMapResponse.size())) {
175+
if (!CollectionUtils.isEmpty(apiResponsesOp) && ((apiResponsesOp.size() != genericMapResponse.size() || isGeneric))) {
176176
// API Responses at operation and @ApiResponse annotation
177177
for (Map.Entry<String, ApiResponse> entry : apiResponsesOp.entrySet()) {
178178
String httpCode = entry.getKey();
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package test.org.springdoc.api.app91;
2+
3+
import javax.servlet.http.HttpServletRequest;
4+
5+
import io.swagger.v3.oas.annotations.media.Content;
6+
import io.swagger.v3.oas.annotations.media.ExampleObject;
7+
import io.swagger.v3.oas.annotations.media.Schema;
8+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
9+
10+
import org.springframework.beans.TypeMismatchException;
11+
import org.springframework.http.HttpStatus;
12+
import org.springframework.http.MediaType;
13+
import org.springframework.http.ResponseEntity;
14+
import org.springframework.web.bind.annotation.ExceptionHandler;
15+
import org.springframework.web.bind.annotation.ResponseStatus;
16+
import org.springframework.web.bind.annotation.RestControllerAdvice;
17+
18+
@RestControllerAdvice
19+
public class Advice {
20+
21+
@ExceptionHandler(TypeMismatchException.class)
22+
@ResponseStatus(HttpStatus.BAD_REQUEST)
23+
@ApiResponse(
24+
responseCode = "400",
25+
description = "Bad Request",
26+
content =
27+
@Content(
28+
mediaType = MediaType.APPLICATION_JSON_VALUE,
29+
schema = @Schema(implementation = ApiError.class),
30+
examples = {
31+
@ExampleObject(
32+
name = "Service-400",
33+
summary = "400 from the service directly",
34+
value =
35+
"{\"status\": 400,"
36+
+ "\"errorCode\": \"ERROR_001\","
37+
+ "\"message\": \"An example message...\""
38+
+ "}")
39+
}))
40+
public ResponseEntity<ApiError> badRequest(HttpServletRequest req, Exception exception) {
41+
ApiError erroObj = new ApiError(400, "A code", "A message");
42+
return new ResponseEntity<>(erroObj, HttpStatus.BAD_REQUEST);
43+
}
44+
45+
@ExceptionHandler(Exception.class)
46+
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
47+
@ApiResponse(
48+
responseCode = "500",
49+
description = "Internal Server Error",
50+
content =
51+
@Content(
52+
mediaType = MediaType.APPLICATION_JSON_VALUE,
53+
schema = @Schema(implementation = ApiError.class),
54+
examples = {
55+
@ExampleObject(
56+
name = "Service-500",
57+
summary = "500 from the service directly",
58+
value =
59+
"{\"status\": 500,"
60+
+ "\"errorCode\": \"ERROR_002\","
61+
+ "\"message\": \"Another example message...\""
62+
+ "}")
63+
}))
64+
public ResponseEntity<ApiError> internalServerError(HttpServletRequest req, Exception exception) {
65+
ApiError erroObj = new ApiError(500, "A different code", "A different message");
66+
return new ResponseEntity<>(erroObj, HttpStatus.INTERNAL_SERVER_ERROR);
67+
}
68+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package test.org.springdoc.api.app91;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
6+
@Schema(
7+
type = "object",
8+
name = "ApiError",
9+
title = "ApiError",
10+
description = "A consistent response object for sending errors over the wire.")
11+
public class ApiError {
12+
13+
@Schema(name = "status", description = "The Http Status value", type = "int", nullable = true)
14+
@JsonProperty("status")
15+
private int status;
16+
17+
@Schema(
18+
name = "errorCode",
19+
description = "An Error Code which can help with identifying issues.",
20+
type = "string",
21+
nullable = true)
22+
@JsonProperty("errorCode")
23+
private String errorCode;
24+
25+
@Schema(name = "message", description = "The Error Message.", type = "string", nullable = false)
26+
@JsonProperty("message")
27+
private String message;
28+
29+
public ApiError(int status, String errorCode, String message) {
30+
this.status = status;
31+
this.errorCode = errorCode;
32+
this.message = message;
33+
}
34+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package test.org.springdoc.api.app91;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
6+
@Schema(
7+
type = "object",
8+
name = "Greeting",
9+
title = "Greeting",
10+
description = "An object containing a greeting message")
11+
public class Greeting {
12+
13+
14+
@Schema(
15+
name = "payload",
16+
description = "The greeting value",
17+
type = "string",
18+
nullable = false,
19+
example = "sdfsdfs")
20+
@JsonProperty("payload")
21+
private String payload;
22+
23+
public Greeting(String payload) {
24+
this.payload = payload;
25+
}
26+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package test.org.springdoc.api.app91;
2+
3+
import io.swagger.v3.oas.annotations.Operation;
4+
import io.swagger.v3.oas.annotations.tags.Tag;
5+
import org.apache.commons.lang3.RandomStringUtils;
6+
7+
import org.springframework.http.ResponseEntity;
8+
import org.springframework.web.bind.annotation.GetMapping;
9+
import org.springframework.web.bind.annotation.RequestMapping;
10+
import org.springframework.web.bind.annotation.RestController;
11+
12+
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
13+
14+
@RestController
15+
@RequestMapping(produces = APPLICATION_JSON_VALUE)
16+
@Tag(name = "Demo", description = "The Demo API")
17+
public class GreetingController {
18+
19+
@GetMapping(produces = APPLICATION_JSON_VALUE)
20+
@Operation(summary = "This API will return a random greeting.")
21+
public ResponseEntity<Greeting> sayHello() {
22+
return ResponseEntity.ok(new Greeting(RandomStringUtils.randomAlphanumeric(10)));
23+
}
24+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
*
3+
* * Copyright 2019-2020 the original author or authors.
4+
* *
5+
* * Licensed under the Apache License, Version 2.0 (the "License");
6+
* * you may not use this file except in compliance with the License.
7+
* * You may obtain a copy of the License at
8+
* *
9+
* * https://www.apache.org/licenses/LICENSE-2.0
10+
* *
11+
* * Unless required by applicable law or agreed to in writing, software
12+
* * distributed under the License is distributed on an "AS IS" BASIS,
13+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* * See the License for the specific language governing permissions and
15+
* * limitations under the License.
16+
*
17+
*/
18+
19+
package test.org.springdoc.api.app91;
20+
21+
22+
import test.org.springdoc.api.AbstractSpringDocTest;
23+
24+
import org.springframework.boot.autoconfigure.SpringBootApplication;
25+
26+
public class SpringDocApp91Test extends AbstractSpringDocTest {
27+
28+
@SpringBootApplication
29+
static class SpringDocTestApp {}
30+
31+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
{
2+
"openapi": "3.0.1",
3+
"info": {
4+
"title": "OpenAPI definition",
5+
"version": "v0"
6+
},
7+
"servers": [
8+
{
9+
"url": "http://localhost",
10+
"description": "Generated server url"
11+
}
12+
],
13+
"tags": [
14+
{
15+
"name": "Demo",
16+
"description": "The Demo API"
17+
}
18+
],
19+
"paths": {
20+
"/": {
21+
"get": {
22+
"tags": [
23+
"Demo"
24+
],
25+
"summary": "This API will return a random greeting.",
26+
"operationId": "sayHello",
27+
"responses": {
28+
"500": {
29+
"description": "Internal Server Error",
30+
"content": {
31+
"application/json": {
32+
"schema": {
33+
"$ref": "#/components/schemas/ApiError"
34+
},
35+
"examples": {
36+
"Service-500": {
37+
"summary": "500 from the service directly",
38+
"description": "Service-500",
39+
"value": {
40+
"status": 500,
41+
"errorCode": "ERROR_002",
42+
"message": "Another example message..."
43+
}
44+
}
45+
}
46+
}
47+
}
48+
},
49+
"400": {
50+
"description": "Bad Request",
51+
"content": {
52+
"application/json": {
53+
"schema": {
54+
"$ref": "#/components/schemas/ApiError"
55+
},
56+
"examples": {
57+
"Service-400": {
58+
"summary": "400 from the service directly",
59+
"description": "Service-400",
60+
"value": {
61+
"status": 400,
62+
"errorCode": "ERROR_001",
63+
"message": "An example message..."
64+
}
65+
}
66+
}
67+
}
68+
}
69+
},
70+
"200": {
71+
"description": "default response",
72+
"content": {
73+
"application/json": {
74+
"schema": {
75+
"$ref": "#/components/schemas/Greeting"
76+
}
77+
}
78+
}
79+
}
80+
}
81+
}
82+
}
83+
},
84+
"components": {
85+
"schemas": {
86+
"ApiError": {
87+
"title": "ApiError",
88+
"type": "object",
89+
"properties": {
90+
"status": {
91+
"type": "integer",
92+
"description": "The Http Status value",
93+
"format": "int32",
94+
"nullable": true
95+
},
96+
"errorCode": {
97+
"type": "string",
98+
"description": "An Error Code which can help with identifying issues.",
99+
"nullable": true
100+
},
101+
"message": {
102+
"type": "string",
103+
"description": "The Error Message."
104+
}
105+
},
106+
"description": "A consistent response object for sending errors over the wire."
107+
},
108+
"Greeting": {
109+
"title": "Greeting",
110+
"type": "object",
111+
"properties": {
112+
"payload": {
113+
"type": "string",
114+
"description": "The greeting value",
115+
"example": "sdfsdfs"
116+
}
117+
},
118+
"description": "An object containing a greeting message"
119+
}
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)