Skip to content

A few examples

Frédéric Nieto edited this page May 14, 2020 · 5 revisions

A few Examples

Setup

application.install(OpenAPIGen) { // basic info info { version = "0.0.1" title = "Test API" description = "The Test API" contact { name = "Support" email = "support@test.com" } } // describe the server, add as many as you want server("http://localhost:8080/") { description = "Test server" } //optional custom schema object namer replaceModule(DefaultSchemaNamer, object: SchemaNamer { val regex = Regex("[A-Za-z0-9_.]+") override fun get(type: KType): String { return type.toString().replace(regex) { it.value.split(".").last() }.replace(Regex(">|<|, "), "_") } }) } application.apiRouting { // routing goes here }

Expose the OpenAPI.json and swager-ui

application.routing { get("/openapi.json") { call.respond(application.openAPIGen.api.serialize()) } get("/") { call.respondRedirect("/swagger-ui/index.html?url=/openapi.json", true) } }

Routing

 get<ParameterType, ResponseType> { params -> // get request on '/' because no path is specified respond(ResponseType(...)) } route("someroute").get<ParameterType, ResponseType>( // get request on '/someroute' info("String Param Endpoint", "This is a String Param Endpoint"), // A Route module that adds a name and description to the OpenAPI data for the endpoint, it is optional example = ResponseType(...) // example for the OpenAPI data ) { params -> respond(ResponseType(...)) }

Routes can be written in a chained manner:

route("a").route("b")...

or in blocks if you want a hierarchy

route("a") { route("b") { ... } }

Parameters

You may have noticed the ParameterType or Params in the get and post handlers, these allow to configure the parameters.

Path Parameters:

@Path("{a}") data class LongParam(@PathParam("A simple Long Param") val a: Long) data class StringParam(@PathParam("A simple String Param") val str: Long) route("someroute") { get<LongParam, ResponseType> { params -> // get request on '/someroute/{a}' respond(ResponseType(...)) } } route("{str}") { get<StringParam, ResponseType> { params -> // get request on '/{str}', str will be the string  respond(ResponseType(...)) } }

Note that the name of the parameter's field must be the one in the brackets, if you have multiple ones it is undefined behavior.

Query Parameters:

data class LongParam(@QueryParam("A simple Long Param") val a: Long) route("someroute") { get<LongParam, ResponseType> { params -> // get request on '/someroute/?a=' respond(ResponseType(...)) } }

Header Parameters:

data class LongParam(@HeaderParam("A simple Long Param") val `A-HEADER`: Long) route("someroute") { get<LongParam, ResponseType> { params -> // get request on '/someroute', and expects a header 'A-HEADER' respond(ResponseType(...)) } }

Styles

All Parameter annotations have a style optional parameter that allows you to specify the style according to spec.

Exceptions and multiple responses

The options you have:

throws(HttpStatusCode.BadRequest, CustomException::class) { ... // no example, just the exception handling throws(HttpStatusCode.BadRequest, "example", CustomException::class) { ... // exception handling with example, will respond example throws(HttpStatusCode.BadRequest, "example", {ex: CustomException -> ex.toString()}) { ... // exception handling, will respond generated content

Now we want to respond a custom generic Error object when an exception is thrown:

data class Error<P>(val id: String, val payload: P) throws(HttpStatusCode.BadRequest, Error("bad.request", mapOf<String, String>()), {ex: CustomException -> Error(ex.id, ex.payload)}) { get<ParameterType, ResponseType> { params -> respond(ResponseType(...)) } }

You can also define multiple ones:

data class Error<P>(val id: String, val payload: P) throws(HttpStatusCode.BadRequest, Error("bad.request", mapOf<String, String>()), {ex: CustomException -> Error(ex.id, ex.payload)}) { throws(HttpStatusCode.InternalServerError, Error("internal.error", mapOf<String, String>()), {ex: OtherCustomException -> Error(ex.id, ex.payload)}) { get<ParameterType, ResponseType> { params -> respond(ResponseType(...)) } } }

And different response types:

data class Error<P>(val id: String, val payload: P) throws(HttpStatusCode.BadRequest, Error("bad.request", mapOf<String, String>()), {ex: CustomException -> Error(ex.id, ex.payload)}) { throws(HttpStatusCode.InternalServerError, "err", {ex: OtherCustomException -> ex.id}) { get<ParameterType, ResponseType> { params -> respond(ResponseType(...)) } } }

If you want to respond normally you can also do:

// null example means no example, you can of course also add one if you want throws(HttpStatusCode.OK, null, {ex: CustomException -> OtherResponseType(ex.response)}) { get<ParameterType, ResponseType> { params -> if (shouldRespondOther) throw CustomException(response) respond(ResponseType(...)) } }

Sealed Class Support / Jackson Polymorphic Deserialization

If you use polymorphic json, and you: You have Type A

{ "@type" : "a", "str": "Some String" }

You have Type B

{ "@type" : "b", "i": 1 }

You have Type C

{ "@type" : "c", "l": 0 }

You would define it like this

 @JsonTypeInfo(use = JsonTypeInfo.Id.NAME) @JsonSubTypes( JsonSubTypes.Type(Base.A::class, name = "a"), JsonSubTypes.Type(Base.B::class, name = "b"), JsonSubTypes.Type(Base.C::class, name = "c") ) sealed class Base { class A(val str: String) : Base() class B(val i: @Min(0) @Max(2) Int) : Base() // Min and Max constrain the value, it will be shown in the OpenAPI spec, you can also implement custom ones as the feature is not hard-coded, look at how they are defined @WithExample // provide an example in a subtype, the companion object and the annotation are required class C(val l: @Clamp(0, 10) Long) : Base() { companion object: ExampleProvider<C> { override val example: C? = C(5) } } } // and then use as always as request or response, here it just responds what it receives post<Params, Base, Base>( info("Sealed class Endpoint", "This is a Sealed class Endpoint"), exampleRequest = Base.A("Hi"), exampleResponse = Base.A("Hi") ) { params, base -> respond(base) }

Binary Request/Response

const val contentType = "image/png" @BinaryRequest([contentType]) // can be omitted if you don' t want to use it as request @BinaryResponse([contentType]) // can be omitted if you don' t want to use it as response data class RawPng(val stream: InputStream) // then in your route like usual post<Params, Response, RawPng> { params, body-> ... } post<Params, RawPng, Body> { params, body -> ... }

Multipart Request

@FormDataRequest data class PDFFileCreateDTO(@PartEncoding("application/pdf") val file: NamedFileInputStream, val name: String, val public: Boolean) // then in your route like usual post<Params, Response, PDFFileCreateDTO> { params, fileCreate -> ... }
Clone this wiki locally