Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/cdimascio/openapi-spring-webflux-validator
🌱 A friendly kotlin library to validate API endpoints using an OpenApi 3.0 and Swagger 2.0 specification
https://github.com/cdimascio/openapi-spring-webflux-validator
functional hacktoberfest java openapi openapi3 rest spring swagger swagger-validator validation webflux
Last synced: 4 days ago
JSON representation
🌱 A friendly kotlin library to validate API endpoints using an OpenApi 3.0 and Swagger 2.0 specification
- Host: GitHub
- URL: https://github.com/cdimascio/openapi-spring-webflux-validator
- Owner: cdimascio
- License: other
- Created: 2017-11-29T15:41:04.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2024-12-19T22:36:55.000Z (about 1 month ago)
- Last Synced: 2025-01-19T19:07:14.939Z (11 days ago)
- Topics: functional, hacktoberfest, java, openapi, openapi3, rest, spring, swagger, swagger-validator, validation, webflux
- Language: Kotlin
- Homepage:
- Size: 345 KB
- Stars: 96
- Watchers: 6
- Forks: 13
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# openapi-spring-webflux-validator
![](https://travis-ci.org/cdimascio/openapi-spring-webflux-validator.svg?branch=master)[![Maven Central](https://img.shields.io/maven-central/v/io.github.cdimascio/openapi-spring-webflux-validator.svg?label=Maven%20Central)](https://search.maven.org/artifact/io.github.cdimascio/openapi-spring-webflux-validator/4.2.0/jar) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/f78b72ca90104e42b111723a7720adf3)](https://www.codacy.com/app/cdimascio/openapi-spring-webflux-validator?utm_source=github.com&utm_medium=referral&utm_content=cdimascio/openapi-spring-webflux-validator&utm_campaign=Badge_Grade) ![](https://img.shields.io/badge/license-Apache%202.0-blue.svg)
[![All Contributors](https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square)](#contributors-)
A friendly kotlin library to validate API endpoints using an _OpenApi 3_ or _Swagger 2_ specification. Great with webflux functional.
It **works happily with Spring Webflux 6's baseline of Jakarta JVM runtime >=17**.
Supports specifications in _YAML_ and _JSON_
## Prequisites
and Spring Webflux 6 + Java >=17
_For use with Spring Boot 2 and Webflux 5, use `openapi-spring-webflux-validator` version `3.5.0`. Java 8 or greater is required._
## Install
### Maven
```xml
io.github.cdimascio
openapi-spring-webflux-validator
4.2.0```
### Gradle
```groovy
compile 'io.github.cdimascio:openapi-spring-webflux-validator:4.2.0'
```For sbt, grape, ivy and more, see [here](https://search.maven.org/#artifactdetails%7Cio.github.cdimascio%7Copenapi-spring-webflux-validator%7C2.0.0%7Cjar)
## Usage (Kotlin)
This section and the next describe usage with Kotlin and Java respectively.
See this [complete Spring Webflux example that uses openapi-spring-webflux-validator](https://github.com/cdimascio/kotlin-swagger-spring-functional-template).
### 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`
```kotlin
import io.github.cdimascio.openapi.Validateval validate = Validate.configure("static/api.yaml")
```with custom error handler
```kotlin
import org.springframework.web.reactive.function.server.ServerRequestdata class MyError(val request: ServerRequest, val code: String, val messages: List)
val validate = Validate.configure("static/api.json") { request, status, messages ->
MyError(request, status.name, messages)
}
```with custom ObjectMapper factory:
```kotlin
val validate = Validate.configure(
openApiSwaggerPath = "api.yaml",
errorHandler = { request, status, message -> ValidationError(request, 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](#configure-kotlin):without a body
```kotlin
validate.request(req) {
// Do stuff e.g. return a list of names
ok().body(Mono.just(listOf("carmine", "alex", "eliana")))
}
```with body
```kotlin
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))
}
```with body you want to process as string (e.g. for computing a request signature), or that you want to deserialize somehow specifically
```kotlin
val identity: (String) -> String = { it }
validate.request(req).withBody(String::class.java, readValue = identity) { body ->
ok().body(Mono.just("content length is ${body.length}"))
}
```### Validate a request (Kotlin + coroutines)
Or you can validate a request in a coroutine style,
using the `validate` instance created [above](#configure-kotlin):without a body
```kotlin
validate.requestAndAwait(req) {
// Do stuff e.g. return a list of names
ok().bodyValueAndAwait(listOf("carmine", "alex", "eliana"))
}
```with body
```kotlin
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)
}
```with body you want to process as string (e.g. for computing a request signature), or that you want to deserialize somehow specifically
```kotlin
val identity: (String) -> String = { it }
validate.request(req).awaitBody(String::class.java, identity) { body: String ->
ok().bodyValueAndAwait("content length is ${body.length}")
}
```## Usage
### Configure (Java)
This one-time configuration requires you to provide the _location of the openapi/swagger specification_ and an optional _custom error handler_.```java
import io.github.cdimascio.openapi.Validate;Validate validate = Validate.configure("static/api.json")
```with custom error handler
```java
import org.springframework.web.reactive.function.server.ServerRequest;class MyError {
private ServerRequest request;
private String id;
private String messages;
public MyError(ServerRequest request, String id, List messages) {
this.request = request;
this.id = id;
this.messages = messages;
}
public ServerRequest getRequest() {
return request;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List getMessages() {
return messages;
}
public void setMessages(List messages) {
this.messages = messages;
}
}
``````java
Validate validate = Validate.configure("static/api.json", (request, status, messages) ->
new MyError(request, status.getName(), messages)
);
```### Validate a request (Java)
Using the `validate` instance created above, you can now validate a request:
without a body
```java
ArrayList users = new ArrayList() {{
add("carmine");
add("alex");
add("eliana");
}};validate.request(req, () ->
// Do stuff e.g. return a list of user names
ServerResponse.ok().bodyValue(users)
);
```with body
```java
validate
.request(req)
.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
ServerResponse.ok().bodyValue(user)
);
```with body you want to process as string (e.g. for computing a request signature)
```java
validate
.request(req)
.withBody(String.class, s -> s, body ->
ServerResponse.ok().bodyValue("content length is " + body.length())
);
```## Example Validation Output
Let's assume a `POST` request to create a user requires the following request body:
```json
{
"firstname": "carmine",
"lastname": "dimasico"
}
```Let's now assume an API user misspells `lastname` as `lastnam`
```shell
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:
```json
{
"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:**
```kotlin
package myproject.controllersimport 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.URIclass 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)
}
}
}
}
``````kotlin
package myprojectimport io.github.cdimascio.openapi.Validate
val validate = Validate.configure("static/api.yaml")
```**Validate with openapi-spring-webflux-validator**
```kotlin
package myproject.controllersimport 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.Monoclass UserHandler {
fun findAll(req: ServerRequest): Mono {
return validate.request(req) {
ok().bodyValue(listOf("carmine", "alex", "eliana"))
}
}fun create(req: ServerRequest): Mono {
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
[Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0)
## Contributors ✨
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
Carmine DiMascio
💻 ⚠️ 📖
Krzysiek Kruczyński
💻 ⚠️ 📖
Chejerla Karthik
💻
Katie Levy
💻
Ilya Builuk
💻
Simon Zambrovski
💻 ⚠️
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!