Skip to content

Commit 1fccb25

Browse files
authored
JAX-RS missing required request body (#64)
Fix #63.
1 parent 215ae87 commit 1fccb25

File tree

5 files changed

+64
-29
lines changed

5 files changed

+64
-29
lines changed

belgif-rest-problem-it/belgif-rest-problem-it-common/src/main/java/io/github/belgif/rest/problem/AbstractRestProblemIT.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,11 @@ void constraintViolationMissingRequiredQueryParameter() {
106106
.body("issues[0].in", equalTo("query"))
107107
.body("issues[0].name", equalTo("param"))
108108
.body("issues[0].value", nullValue())
109-
.body("issues[0].detail", anyOf(equalTo("must not be null"), containsString("is not present")));
109+
.body("issues[0].detail", anyOf(
110+
// JEE
111+
equalTo("must not be null"),
112+
// Spring Boot
113+
containsString("is not present")));
110114
}
111115

112116
@Test
@@ -158,7 +162,11 @@ void constraintViolationMissingRequiredHeaderParameter() {
158162
.body("issues[0].in", equalTo("header"))
159163
.body("issues[0].name", equalTo("param"))
160164
.body("issues[0].value", nullValue())
161-
.body("issues[0].detail", anyOf(equalTo("must not be null"), containsString("is not present")));
165+
.body("issues[0].detail", anyOf(
166+
// JEE
167+
equalTo("must not be null"),
168+
// Spring Boot
169+
containsString("is not present")));
162170
}
163171

164172
@Test
@@ -214,6 +222,23 @@ void constraintViolationInvalidPathParameterOverridden() {
214222
.body("issues[0].detail", equalTo("must be greater than 0"));
215223
}
216224

225+
@Test
226+
void constraintViolationMissingRequiredBody() {
227+
getSpec().when()
228+
.contentType("application/json")
229+
.post("/beanValidation/body").then().assertThat()
230+
.statusCode(400)
231+
.body("type", equalTo("urn:problem-type:belgif:badRequest"))
232+
.body("issues[0].in", equalTo("body"))
233+
.body("issues[0].name", nullValue())
234+
.body("issues[0].value", nullValue())
235+
.body("issues[0].detail", anyOf(
236+
// JEE
237+
equalTo("must not be null"),
238+
// Spring Boot
239+
containsString("Required request body is missing")));
240+
}
241+
217242
@Test
218243
void constraintViolationBody() {
219244
getSpec().when().body("{" +

belgif-rest-problem-it/belgif-rest-problem-jakarta-ee-it/src/main/java/io/github/belgif/rest/problem/Frontend.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Response beanValidationQueryParameter(@QueryParam("param") @NotNull @Positive In
6464

6565
@POST
6666
@Path("/beanValidation/body")
67-
Response beanValidationBody(@Valid Model body);
67+
Response beanValidationBody(@Valid @NotNull Model body);
6868

6969
@POST
7070
@Path("/beanValidation/body/nested")

belgif-rest-problem-it/belgif-rest-problem-java-ee-it/src/main/java/io/github/belgif/rest/problem/Frontend.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Response beanValidationQueryParameter(@QueryParam("param") @NotNull @Positive In
6464

6565
@POST
6666
@Path("/beanValidation/body")
67-
Response beanValidationBody(@Valid Model body);
67+
Response beanValidationBody(@Valid @NotNull Model body);
6868

6969
@POST
7070
@Path("/beanValidation/body/nested")

belgif-rest-problem-java-ee/src/main/java/io/github/belgif/rest/problem/internal/ConstraintViolationUtil.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ public static InputValidationIssue convertToInputValidationIssue(ConstraintViola
6161
}
6262
InEnum in = determineSource(violation, propertyPath, methodNode);
6363
String name = propertyPath.stream().map(Node::toString).collect(Collectors.joining("."));
64+
if (in == InEnum.BODY && propertyPath.getLast().getKind() == ElementKind.PARAMETER) {
65+
name = null;
66+
}
6467
return InputValidationIssues.schemaViolation(in, name, violation.getInvalidValue(), violation.getMessage());
6568
}
6669

@@ -74,7 +77,7 @@ private static InEnum determineSource(ConstraintViolation<?> violation,
7477
methodNode.getParameterTypes().toArray(new Class[0]));
7578
return AnnotationUtil.findParamAnnotation(method, param.getParameterIndex(), ANNOTATIONS)
7679
.map(Annotation::annotationType).map(SOURCE_MAPPING::get)
77-
.orElse(InEnum.QUERY);
80+
.orElse(InEnum.BODY);
7881
} catch (NoSuchMethodException e) {
7982
throw new IllegalStateException(
8083
"Method " + methodNode.getName() + " not found on " + violation.getRootBeanClass(), e);

belgif-rest-problem-java-ee/src/test/java/io/github/belgif/rest/problem/internal/ConstraintViolationUtilTest.java

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,30 @@ class ConstraintViolationUtilTest {
2727
private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
2828

2929
@Test
30-
void bodyProperty() {
30+
void missingRequiredBody() throws Exception {
31+
Set<ConstraintViolation<Resource>> violations =
32+
validator.forExecutables().validateParameters(new Resource(),
33+
Resource.class.getMethod("bodyParam", Body.class), new Object[] { null });
34+
35+
assertThat(violations).hasSize(1);
36+
37+
InputValidationIssue issue =
38+
ConstraintViolationUtil.convertToInputValidationIssue(violations.iterator().next());
39+
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
40+
assertThat(issue.getName()).isNull();
41+
assertThat(issue.getValue()).isNull();
42+
assertThat(issue.getDetail()).isEqualTo("must not be null");
43+
}
44+
45+
@Test
46+
void bodyProperty() throws Exception {
3147
Body target = new Body();
3248
target.value = 10;
3349

34-
Set<ConstraintViolation<Body>> violations = validator.validate(target);
50+
Set<ConstraintViolation<Resource>> violations =
51+
validator.forExecutables().validateParameters(new Resource(),
52+
Resource.class.getMethod("bodyParam", Body.class), new Object[] { target });
53+
3554
assertThat(violations).hasSize(1);
3655

3756
InputValidationIssue issue =
@@ -43,12 +62,15 @@ void bodyProperty() {
4362
}
4463

4564
@Test
46-
void nestedBodyProperty() {
65+
void nestedBodyProperty() throws Exception {
4766
Body target = new Body();
4867
target.nested.add(new Nested("OK"));
4968
target.nested.add(new Nested(null));
5069

51-
Set<ConstraintViolation<Body>> violations = validator.validate(target);
70+
Set<ConstraintViolation<Resource>> violations =
71+
validator.forExecutables().validateParameters(new Resource(),
72+
Resource.class.getMethod("bodyParam", Body.class), new Object[] { target });
73+
5274
assertThat(violations).hasSize(1);
5375

5476
InputValidationIssue issue =
@@ -107,22 +129,6 @@ void headerParam() throws Exception {
107129
assertThat(issue.getDetail()).isEqualTo("must be less than or equal to 5");
108130
}
109131

110-
@Test
111-
void noAnnotationParam() throws Exception {
112-
Set<ConstraintViolation<Resource>> violations =
113-
validator.forExecutables().validateParameters(new Resource(),
114-
Resource.class.getMethod("noAnnotationParam", int.class), new Object[] { 10 });
115-
116-
assertThat(violations).hasSize(1);
117-
118-
InputValidationIssue issue =
119-
ConstraintViolationUtil.convertToInputValidationIssue(violations.iterator().next());
120-
assertThat(issue.getIn()).isEqualTo(InEnum.QUERY);
121-
assertThat(issue.getName()).isEqualTo("value");
122-
assertThat(issue.getValue()).isEqualTo(10);
123-
assertThat(issue.getDetail()).isEqualTo("must be less than or equal to 5");
124-
}
125-
126132
@Test
127133
void paramFromSuperClass() throws Exception {
128134
Set<ConstraintViolation<Resource>> violations =
@@ -166,19 +172,20 @@ public Response paramFromSuperClass(@QueryParam("value") @Max(5) int value) {
166172
}
167173

168174
static class Resource extends SuperClass implements Interface {
169-
public Response queryParam(@QueryParam("value") @Max(5) int value) {
175+
176+
public Response bodyParam(@Valid @NotNull Body body) {
170177
return null;
171178
}
172179

173-
public Response pathParam(@PathParam("value") @Max(5) int value) {
180+
public Response queryParam(@QueryParam("value") @Max(5) int value) {
174181
return null;
175182
}
176183

177-
public Response headerParam(@HeaderParam("value") @Max(5) int value) {
184+
public Response pathParam(@PathParam("value") @Max(5) int value) {
178185
return null;
179186
}
180187

181-
public Response noAnnotationParam(@Max(5) int value) {
188+
public Response headerParam(@HeaderParam("value") @Max(5) int value) {
182189
return null;
183190
}
184191

0 commit comments

Comments
 (0)