Ktor 3.0.2 Help

Request validation

The RequestValidation plugin provides the ability to validate a body of incoming requests. You can validate a raw request body or specified request object properties if the ContentNegotiation plugin with a serializer is installed. If a request body validation fails, the plugin raises RequestValidationException, which can be handled using the StatusPages plugin.

Add dependencies

To use RequestValidation, you need to include the ktor-server-request-validation artifact in the build script:

implementation("io.ktor:ktor-server-request-validation:$ktor_version")
implementation "io.ktor:ktor-server-request-validation:$ktor_version"
<dependency> <groupId>io.ktor</groupId> <artifactId>ktor-server-request-validation-jvm</artifactId> <version>${ktor_version}</version> </dependency>

Install RequestValidation

To install the RequestValidation plugin to the application, pass it to the install function in the specified module. The code snippets below show how to install RequestValidation...

  • ... inside the embeddedServer function call.

  • ... inside the explicitly defined module, which is an extension function of the Application class.

import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.server.application.* import io.ktor.server.plugins.requestvalidation.* fun main() { embeddedServer(Netty, port = 8080) { install(RequestValidation) // ... }.start(wait = true) }
import io.ktor.server.application.* import io.ktor.server.plugins.requestvalidation.* // ... fun Application.module() { install(RequestValidation) // ... }

The RequestValidation plugin can also be installed to specific routes. This might be useful if you need different RequestValidation configurations for different application resources.

Configure RequestValidation

Configuring RequestValidation involves three main steps:

  1. Receiving body contents.

  2. Configuring a validation function.

  3. Handling validation exceptions.

1. Receive body

The RequestValidation plugin validates a body of a request if you call the receive function with a type parameter. For instance, the code snippet below shows how to receive a body as a String value:

routing { post("/text") { val body = call.receive<String>() call.respond(body) } }

2. Configure a validation function

To validate a request body, use the validate function. This function returns a ValidationResult object representing a successful or unsuccessful validation result. For an unsuccessful result, RequestValidationException is raised.

The validate function has two overloads allowing you to validate a request body in two ways:

  • The first validate overload allows you to access a request body as the object of the specified type. The example below shows how to validate a request body representing a String value:

    install(RequestValidation) { validate<String> { bodyText -> if (!bodyText.startsWith("Hello")) ValidationResult.Invalid("Body text should start with 'Hello'") else ValidationResult.Valid } }

    If you have the ContentNegotiation plugin installed configured with a specific serializer, you can validate object properties. Learn more from Example: Validating object properties.

  • The second validate overload accepts ValidatorBuilder and allows you to provide custom validation rules. You can learn more from Example: Validating byte arrays.

3. Handle validation exceptions

If request validation is failed, RequestValidation raises RequestValidationException. This exception allows you to access a request body and get reasons for all validation failures for this request.

You can handle RequestValidationException using the StatusPages plugin as follows:

install(StatusPages) { exception<RequestValidationException> { call, cause -> call.respond(HttpStatusCode.BadRequest, cause.reasons.joinToString()) } }

You can find the full example here: request-validation.

Example: Validating object properties

In this example, we'll look at how to validate object properties using the RequestValidation plugin. Suppose the server receives a POST request with the following JSON data:

POST http://0.0.0.0:8080/json Content-Type: application/json { "id": -1, "firstName": "Jet", "lastName": "Brains" }

To add validation of the id property, follow the steps below:

  1. Create the Customer data class that describes the JSON object above:

    @Serializable data class Customer(val id: Int, val firstName: String, val lastName: String)
  2. Install the ContentNegotiation plugin with the JSON serializer:

    install(ContentNegotiation) { json() }
  3. Receive the Customer object on the server side as follows:

    post("/json") { val customer = call.receive<Customer>() call.respond(customer) }
  4. In the RequestValidation plugin configuration, add validation of the id property to make sure it falls into a specified range:

    install(RequestValidation) { validate<Customer> { customer -> if (customer.id <= 0) ValidationResult.Invalid("A customer ID should be greater than 0") else ValidationResult.Valid } }

    In this case, RequestValidation will raise RequestValidationException if the id value is less or equals to 0.

Example: Validating byte arrays

In this example, we'll take a look at how to validate a request body received as a byte array. Suppose the server receives a POST request with the following text data:

POST http://localhost:8080/array Content-Type: text/plain -1

To receive data as a byte array and validate it, perform the following steps:

  1. Receive data on the server side as follows:

    post("/array") { val body = call.receive<ByteArray>() call.respond(String(body)) }
  2. To validate the received data, we'll use the second validate function overload that accepts ValidatorBuilder and allows you to provide custom validation rules:

    install(RequestValidation) { validate { filter { body -> body is ByteArray } validation { body -> check(body is ByteArray) val intValue = String(body).toInt() if (intValue <= 0) ValidationResult.Invalid("A value should be greater than 0") else ValidationResult.Valid } } }
Last modified: 02 April 2024