Skip to content

Commit 7469c72

Browse files
authored
Merge pull request #3519 from SentryMan/avaje-validator
Add Avaje Validator Module
2 parents 22a0f5a + 77f5825 commit 7469c72

22 files changed

Lines changed: 1154 additions & 55 deletions

File tree

docs/asciidoc/modules/avaje-inject.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<path>
2222
<groupId>io.avaje</groupId>
2323
<artifactId>avaje-inject-generator</artifactId>
24-
<version>10.0</version>
24+
<version>10.3</version>
2525
</path>
2626
</annotationProcessorPaths>
2727
</configuration>
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
== Avaje Validator
2+
3+
Bean validation via https://avaje.io/validator/[Avaje Validator].
4+
5+
=== Usage
6+
7+
1) Add the dependency:
8+
9+
[dependency, artifactId="jooby-avaje-validator"]
10+
.
11+
12+
2) Configure annotation processor
13+
14+
.Maven
15+
[source, xml, role = "primary"]
16+
----
17+
<build>
18+
<plugins>
19+
<plugin>
20+
<groupId>org.apache.maven.plugins</groupId>
21+
<artifactId>maven-compiler-plugin</artifactId>
22+
<version>...</version>
23+
<configuration>
24+
<annotationProcessorPaths>
25+
<path>
26+
<groupId>io.avaje</groupId>
27+
<artifactId>avaje-validator-generator</artifactId>
28+
<version>2.1</version>
29+
</path>
30+
</annotationProcessorPaths>
31+
</configuration>
32+
</plugin>
33+
</plugins>
34+
</build>
35+
----
36+
37+
.Gradle
38+
[source, kotlin, role = "secondary"]
39+
----
40+
plugins {
41+
id "org.jetbrains.kotlin.kapt" version "1.9.10"
42+
}
43+
44+
dependencies {
45+
kapt 'io.avaje:avaje-validator-generator:2.1'
46+
}
47+
----
48+
49+
3) Install
50+
51+
.Java
52+
[source, java, role="primary"]
53+
----
54+
import io.jooby.avaje.validator.AvajeValidatorModule;
55+
56+
{
57+
install(new AvajeValidatorModule());
58+
}
59+
----
60+
61+
.Kotlin
62+
[source, kt, role="secondary"]
63+
----
64+
import io.jooby.avaje.validator.AvajeValidatorModule
65+
66+
{
67+
install(new AvajeValidatorModule())
68+
}
69+
----
70+
71+
4) Usage in MVC routes
72+
73+
.Java
74+
[source,java,role="primary"]
75+
----
76+
import io.jooby.annotation.*;
77+
import jakarta.validation.Valid;
78+
79+
@Path("/mvc")
80+
public class Controller {
81+
82+
@POST("/validate-body")
83+
public void validateBody(@Valid Bean bean) { // <1>
84+
...
85+
}
86+
87+
@POST("/validate-query")
88+
public void validateQuery(@Valid @QueryParam Bean bean) { // <2>
89+
...
90+
}
91+
92+
@POST("/validate-list")
93+
public void validateList(@Valid List<Bean> beans) { // <3>
94+
...
95+
}
96+
97+
@POST("/validate-map")
98+
public void validateMap(@Valid Map<String, Bean> beans) { // <4>
99+
...
100+
}
101+
}
102+
----
103+
104+
.Kotlin
105+
[source, kt, role="secondary"]
106+
----
107+
import io.jooby.annotation.*;
108+
import jakarta.validation.Valid
109+
110+
@Path("/mvc")
111+
class Controller {
112+
113+
@POST("/validate-body")
114+
fun validateBody(@Valid bean: Bean) : Unit { // <1>
115+
...
116+
}
117+
118+
@POST("/validate-query")
119+
fun validateQuery(@Valid @QueryParam bean: Bean) : Unit { // <2>
120+
...
121+
}
122+
123+
@POST("/validate-list")
124+
fun validateList(@Valid beans: List<Bean>) : Unit { // <3>
125+
...
126+
}
127+
128+
@POST("/validate-map")
129+
fun validateMap(@Valid beans: Map<String, Bean>) : Unit { // <4>
130+
...
131+
}
132+
}
133+
----
134+
135+
<1> Validate a bean decoded from the request body
136+
<2> Validate a bean parsed from query parameters. This works the same for `@FormParam` or `@BindParam`
137+
<3> Validate a list of beans. This also applies to arrays `@Valid Bean[] beans`
138+
<4> Validate a map of beans
139+
140+
4) Usage in in script/lambda routes
141+
142+
Jooby doesn't provide fully native bean validation in script/lambda at the moment,
143+
but you can use a helper that we utilize under the hood in MVC routes:
144+
145+
.Java
146+
[source, java, role="primary"]
147+
----
148+
import io.jooby.validation.BeanValidator;
149+
150+
{
151+
post("/validate", ctx -> {
152+
Bean bean = BeanValidator.validate(ctx, ctx.body(Bean.class));
153+
...
154+
});
155+
}
156+
----
157+
158+
.Kotlin
159+
[source, kt, role="secondary"]
160+
----
161+
import io.jooby.validation.BeanValidator
162+
163+
{
164+
post("/validate") {
165+
val bean = BeanValidator.validate(ctx, ctx.body(Bean.class))
166+
...
167+
}
168+
}
169+
----
170+
171+
`BeanValidator.validate()` behaves identically to validation in MVC routes.
172+
It also supports validating list, array, and map of beans
173+
174+
=== Constraint Violations Rendering
175+
176+
`AvajeValidatorModule` provides default built-in error handler that
177+
catches `ConstraintViolationException` and transforms it into the following response:
178+
179+
.JSON:
180+
----
181+
{
182+
"title": "Validation failed",
183+
"status": 422,
184+
"errors": [
185+
{
186+
"field": "firstName",
187+
"messages": [
188+
"must not be empty",
189+
"must not be null"
190+
],
191+
"type": "FIELD"
192+
},
193+
{
194+
"field": null,
195+
"messages": [
196+
"passwords are not the same"
197+
],
198+
"type": "GLOBAL"
199+
}
200+
]
201+
}
202+
----
203+
204+
It is possible to override the `title` and `status` code of the response above:
205+
206+
[source, java]
207+
----
208+
209+
{
210+
install(new AvajeJsonbModule());
211+
install(new AvajeValidatorModule()
212+
.statusCode(StatusCode.BAD_REQUEST)
213+
.validationTitle("Incorrect input data")
214+
);
215+
}
216+
----
217+
218+
If the default error handler doesn't fully meet your needs, you can always disable it and provide your own:
219+
220+
[source, java]
221+
----
222+
223+
{
224+
install(new AvajeJsonbModule());
225+
install(new AvajeValidatorModule().disableViolationHandler());
226+
227+
error(ConstraintViolationException.class, new MyConstraintViolationHandler());
228+
}
229+
----
230+
231+
=== Manual Validation
232+
233+
The module exposes `Validator` as a service, allowing you to run validation manually at any time.
234+
235+
==== Script/lambda:
236+
237+
[source, java]
238+
----
239+
import io.avaje.validation.Validator;
240+
241+
{
242+
post("/validate", ctx -> {
243+
Validator validator = require(Validator.class);
244+
validator.validate(ctx.body(Bean.class));
245+
...
246+
});
247+
}
248+
----
249+
250+
==== MVC routes with dependency injection:
251+
252+
1) Install DI framework at first.
253+
254+
[source, java]
255+
----
256+
import io.jooby.avaje.validator.AvajeValidatorModule;
257+
258+
{
259+
install(AvajeInjectModule.of()); // <1>
260+
install(new AvajeValidatorModule());
261+
}
262+
----
263+
264+
<1> `Avaje` is just an example, you can achieve the same with `Dagger` or `Guice`
265+
266+
2) Inject `Validator` in controller, service etc.
267+
268+
[source, java]
269+
----
270+
import io.avaje.validation.Validator;
271+
import jakarta.inject.Inject;
272+
273+
@Path("/mvc")
274+
public class Controller {
275+
276+
private final Validator validator;
277+
278+
@Inject
279+
public Controller(Validator validator) {
280+
this.validator = validator;
281+
}
282+
283+
@POST("/validate")
284+
public void validate(Bean bean) {
285+
Set<ConstraintViolation<Bean>> violations = validator.validate(bean);
286+
...
287+
}
288+
}
289+
----
290+
291+
=== Configuration
292+
Any property defined at `validation` will be added automatically:
293+
294+
.application.conf
295+
[source, properties]
296+
----
297+
validation.fail_fast = true
298+
----
299+
300+
Or programmatically:
301+
302+
[source, java]
303+
----
304+
import io.jooby.avaje.validator.AvajeValidatorModule;
305+
306+
{
307+
install(new AvajeValidatorModule().doWith(cfg -> {
308+
cfg.failFast(true);
309+
}));
310+
}
311+
----

docs/asciidoc/modules/modules.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Available modules are listed next.
2727
* link:/modules/redis[Redis]: Redis module.
2828

2929
=== Validation
30+
* link:/modules/avaje-validator[Avaje Validator]: Avaje Validator module.
3031
* link:/modules/hibernate-validator[Hibernate Validator]: Hibernate Validator module.
3132

3233
=== Development Tools

modules/jooby-avaje-inject/pom.xml

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111

1212
<modelVersion>4.0.0</modelVersion>
1313
<artifactId>jooby-avaje-inject</artifactId>
14-
14+
15+
<properties>
16+
<maven.compiler.proc>full</maven.compiler.proc>
17+
</properties>
18+
1519
<dependencies>
1620
<dependency>
1721
<groupId>com.github.spotbugs</groupId>
@@ -21,7 +25,6 @@
2125
<dependency>
2226
<groupId>io.jooby</groupId>
2327
<artifactId>jooby</artifactId>
24-
<version>${jooby.version}</version>
2528
</dependency>
2629

2730
<!-- Avaje Inject -->
@@ -33,7 +36,7 @@
3336
<dependency>
3437
<groupId>io.avaje</groupId>
3538
<artifactId>avaje-inject-generator</artifactId>
36-
<scope>provided</scope>
39+
<scope>test</scope>
3740
</dependency>
3841

3942
<!-- Test dependencies -->
@@ -74,30 +77,4 @@
7477
<scope>test</scope>
7578
</dependency>
7679
</dependencies>
77-
78-
<build>
79-
<plugins>
80-
<plugin>
81-
<groupId>org.apache.maven.plugins</groupId>
82-
<artifactId>maven-compiler-plugin</artifactId>
83-
<executions>
84-
<execution>
85-
<id>test</id>
86-
<phase>test-compile</phase>
87-
</execution>
88-
</executions>
89-
<configuration>
90-
<compilerArgs>
91-
<arg>-parameters</arg>
92-
</compilerArgs>
93-
<annotationProcessorPaths>
94-
<path>
95-
<groupId>io.avaje</groupId>
96-
<artifactId>avaje-inject-generator</artifactId>
97-
</path>
98-
</annotationProcessorPaths>
99-
</configuration>
100-
</plugin>
101-
</plugins>
102-
</build>
10380
</project>

0 commit comments

Comments
 (0)