Requests and Responses
An example focusing on documenting requests and responses.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140 | package io.github.smiley4.ktoropenapi.examples
import com.fasterxml.jackson.core.util.DefaultIndenter
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter
import com.fasterxml.jackson.databind.SerializationFeature
import io.github.smiley4.ktoropenapi.OpenApi
import io.github.smiley4.ktoropenapi.post
import io.github.smiley4.ktoropenapi.openApi
import io.github.smiley4.ktorredoc.redoc
import io.github.smiley4.ktorswaggerui.swaggerUI
import io.ktor.http.HttpStatusCode
import io.ktor.serialization.jackson.jackson
import io.ktor.server.application.Application
import io.ktor.server.application.install
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.request.receive
import io.ktor.server.response.respond
import io.ktor.server.routing.route
import io.ktor.server.routing.routing
fun main() {
embeddedServer(Netty, port = 8080, host = "localhost", module = Application::myModule).start(wait = true)
}
private fun Application.myModule() {
// Install the OpenApi plugin and use the default configuration
install(OpenApi)
install(ContentNegotiation) {
jackson {
configure(SerializationFeature.INDENT_OUTPUT, true)
setDefaultPrettyPrinter(DefaultPrettyPrinter().apply {
indentArraysWith(DefaultPrettyPrinter.FixedSpaceIndenter.instance)
indentObjectsWith(DefaultIndenter(" ", "\n"))
})
}
}
routing {
// add the routes for OpenAPI spec, Swagger UI and ReDoc
route("swagger") {
swaggerUI("/api.json")
}
route("api.json") {
openApi()
}
route("redoc") {
redoc("/api.json")
}
// a documented route
post("calculate", {
// information about the request
request {
// specify the schema of the request body and some additional information
body<Calculation> {
description = "the requested operation and values to perform the operation on"
required = true
}
}
// information the possible responses
response {
// document the "200 OK" response
code(HttpStatusCode.OK) {
description = "Calculation was performed successfully."
// specify the schema of the response body and some additional information
body<CalculationResult> {
description = "the result of an operation together with the original request"
}
}
// document the "422 Unprocessable Entity" response
code(HttpStatusCode.UnprocessableEntity) {
description = "The requested calculation could not be performed, e.g. due to division by zero."
}
}
}) {
call.receive<Calculation>().let { calculation ->
when (calculation.operation) {
OperationType.ADD -> {
call.respond(
HttpStatusCode.OK, CalculationResult(
calculation = calculation,
result = calculation.a + calculation.b
)
)
}
OperationType.SUB -> {
call.respond(
HttpStatusCode.OK, CalculationResult(
calculation = calculation,
result = calculation.a - calculation.b
)
)
}
OperationType.MUL -> {
call.respond(
HttpStatusCode.OK, CalculationResult(
calculation = calculation,
result = calculation.a * calculation.b
)
)
}
OperationType.DIV -> {
if (calculation.b == 0f) {
call.respond(HttpStatusCode.UnprocessableEntity)
} else {
call.respond(
HttpStatusCode.OK, CalculationResult(
calculation = calculation,
result = calculation.a / calculation.b
)
)
}
}
}
}
}
}
}
private enum class OperationType {
ADD, SUB, MUL, DIV
}
private data class Calculation(
val operation: OperationType,
val a: Float,
val b: Float
)
private data class CalculationResult(
val calculation: Calculation,
val result: Float
)
|