openapi-spring-webflux-validator
A friendly kotlin library to validate API endpoints using an OpenApi 3 or Swagger 2 specification. Great with webflux functional. It works happily with any JVM language including Java >=8.
Supports specifications in YAML and JSON
See this complete Spring 5 Webflux example that uses openapi-spring-webflux-validator.
Prequisites
Java 8 or greater
Install
Maven
<dependency>
<groupId>io.github.cdimascio</groupId>
<artifactId>openapi-spring-webflux-validator</artifactId>
<version>3.3.0</version>
</dependency>
Gradle
compile 'io.github.cdimascio:openapi-spring-webflux-validator:3.3.0'
For sbt, grape, ivy and more, see here
Usage (Kotlin)
This section and the next describe usage with Kotlin and Java respectively.
Configure (Kotlin)
This one-time configuration requires you to provide the location of the openapi/swagger specification and an optional custom error handler.
Supports JSON
and YAML
import io.github.cdimascio.openapi.Validate
val validate = Validate.configure("static/api.yaml")
with custom error handler
data class MyError(val id: String, val messages: List<String>)
val validate = Validate.configure("static/api.json") { status, messages ->
Error(status.name, messages)
}
with custom ObjectMapper factory:
val validate = Validate.configure(
openApiSwaggerPath = "api.yaml",
errorHandler = { status, message -> ValidationError(status.value(), message[0]) },
objectMapperFactory = { ObjectMapper()
.registerKotlinModule()
.registerModule(JavaTimeModule())
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) }
)
Validate a request (Kotlin + Reactor)
You can now validate a request in a coroutine style, using the validate
instance created above:
without a body
validate.request(req) {
// Do stuff e.g. return a list of names
ok().body(Mono.just(listOf("carmine", "alex", "eliana")))
}
with body
validate.request(req).withBody(User::class.java) { body ->
// Note that body is deserialized as User!
// Now you can do stuff.
// For example, lets echo the request as the response
ok().body(Mono.just(body))
}
Validate a request (Kotlin + coroutines)
Or you can validate a request in a coroutine style, using the validate
instance created above:
without a body
validate.requestAndAwait(req) {
// Do stuff e.g. return a list of names
ok().bodyValueAndAwait(listOf("carmine", "alex", "eliana"))
}
with body
validate.request(req).awaitBody(User::class.java) { body: User ->
// Note that body is deserialized as User!
// Now you can do stuff.
// For example, lets echo the request as the response
ok().bodyValueAndAwait(body)
}
Usage (Java 8 or greater)
Configure (Java)
This one-time configuration requires you to provide the location of the openapi/swagger specification and an optional custom error handler.
import io.github.cdimascio.openapi.Validate;
Validate<ValidationError> validate = Validate.configure("static/api.json")
with custom error handler
class MyError {
private String id;
private String messages;
public MyError(String id, List<String> messages) {
this.id = id;
this.messages = messages;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<String> getMessages() {
return messages;
}
public void setMessages(List<String> messages) {
this.messages = messages;
}
}
Validate<ValidationError> validate = Validate.configure("static/api.json", (status, messages) ->
new MyError(status.getName(), messages)
);
Validate a request (Java)
Using the validate
instance created above, you can now validate a request:
without a body
ArrayList<String> users = new ArrayList<String>() {{
add("carmine");
add("alex");
add("eliana");
}};
validate.request(null, () -> {
// Do stuff e.g. return a list of user names
ServerResponse.ok().body(fromObject(users));
});
with body
validate
.request(null)
.withBody(User.class, user ->
// Note that body is deserialized as User!
// Now you can do stuff.
// For example, lets echo the request as the response
return ServerResponse.ok().body(fromObject(user))
);
Example Valiation Output
Let's assume a POST
request to create a user requires the following request body:
{
"firstname": "carmine",
"lastname": "dimasico"
}
Let's now assume an API user misspells lastname
as lastnam
curl -X POST http://localhost:8080/api/users -H "Content-Type: application/json" -d'{
"firstname": "c",
"lastnam": "d"
}'
openapi-spring-webflux-validator
automatically validates the request against a Swagger spect and returns:
{
"code": 400,
"messages":[
"Object instance has properties which are not allowed by the schema: [\"lastnam\"]",
"Object has missing required properties ([\"lastname\"])"
]
}
Woah! Cool!! :-D
Example
Let's say you have an endpoint /users
that supports both GET
and POST
operations.
You can create those routes and validate them like so:
Create the routes in a reactive or coroutine style:
package myproject.controllers
import org.springframework.core.io.ClassPathResource
import org.springframework.http.MediaType.*
import org.springframework.web.reactive.function.server.ServerResponse.permanentRedirect
import org.springframework.web.reactive.function.server.coRouter
import org.springframework.web.reactive.function.server.plus
import org.springframework.web.reactive.function.server.router
import java.net.URI
class Routes(private val userHandler: UserHandler) {
fun router() = router {
"/api".nest {
accept(APPLICATION_JSON).nest {
POST("/users", userHandler::create)
}
accept(TEXT_EVENT_STREAM).nest {
GET("/users", userHandler::findAll)
}
}
} + coRouter {
"/coApi".nest {
accept(APPLICATION_JSON).nest {
POST("/users", userHandler::coCreate)
}
accept(TEXT_EVENT_STREAM).nest {
GET("/users", userHandler::coFindAll)
}
}
}
}
package myproject
import io.github.cdimascio.openapi.Validate
val validate = Validate.configure("static/api.yaml")
Validate with openapi-spring-webflux-validator
package myproject.controllers
import myproject.models.User
import myproject.validate
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import org.springframework.web.reactive.function.server.ServerResponse.ok
import org.springframework.web.reactive.function.server.bodyValueAndAwait
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
class UserHandler {
fun findAll(req: ServerRequest): Mono<ServerResponse> {
return validate.request(req) {
ok().bodyValue(listOf("carmine", "alex", "eliana"))
}
}
fun create(req: ServerRequest): Mono<ServerResponse> {
return validate.request(req).withBody(User::class.java) {
// it is the request body deserialized as User
ok().bodyValue(it)
}
}
suspend fun coFindAll(req: ServerRequest): ServerResponse {
return validate.requestAndAwait(req) {
ok().bodyValueAndAwait(listOf("carmine", "alex", "eliana"))
}
}
suspend fun coCreate(req: ServerRequest): ServerResponse {
return validate.request(req).awaitBody(User::class.java) {
// it is the request body deserialized as User
ok().bodyValueAndAwait(it)
}
}
}
License
Contributors
✨
Thanks goes to these wonderful people (emoji key):
Carmine DiMascio |
Krzysiek Kruczyński |
Chejerla Karthik |
Katie Levy |
Ilya Builuk |
Simon Zambrovski |
This project follows the all-contributors specification. Contributions of any kind welcome!