invalidField

Builds a ValidationResult.Invalid that requestValidation decodes into one errors[] entry carrying both pointer and detail.

A plain ValidationResult.Invalid("text"), written without this helper, still works. It just decodes to a detail-only entry, with no pointer.

Parameters

pointer

a JSON Pointer (RFC 6901), typically its URI fragment form, e.g. "#/age", identifying the invalid member of the request body. Written to the wire exactly as given.

detail

human-readable explanation of what is wrong with pointer.

See also

Throws


inline fun <T> invalidField(property: KProperty1<T, *>, detail: String): ValidationResult.Invalid(source)

Builds a ValidationResult.Invalid for property of the request body, deriving its pointer with jsonPointer rather than taking one written by hand. Renaming the property is then a compile error, not a pointer that quietly names a member that is gone.

For a nested member, or one that @SerialName renames, pass jsonPointer<T>(...)'s result to the String overload instead — jsonPointer explains why the property reference cannot cover those.

Parameters

property

the invalid member of the request body, referenced on T.

detail

human-readable explanation of what is wrong with property.

Throws

if T's serialized form has no member under the property's name, or if detail contains U+0000.

Samples

install(RequestValidation) {
    validate<Customer> { customer ->
        val errors =
            buildList {
                if (customer.age <= 0) {
                    add(jsonPointer(Customer::age) to "must be a positive integer")
                }
                // No property reference reaches a nested member, but every segment of the path is
                // still checked against the descriptor it belongs to.
                if (customer.profile.color !in setOf("green", "red", "blue")) {
                    add(jsonPointer<Customer>("profile", "color") to "must be 'green', 'red' or 'blue'")
                }
                // `@SerialName` renames this one, so the pointer has to name what the wire
                // carries. `jsonPointer(Customer::emailAddress)` would throw rather than guess.
                if ("@" !in customer.emailAddress) {
                    add(jsonPointer<Customer>("email_address") to "must be an email address")
                }
            }

        if (errors.isEmpty()) ValidationResult.Valid else invalidFields(errors)
    }
}

install(StatusPages) {
    problemDetails {
        requestValidation(ValidationError)
    }
}

routing {
    post("/customers") {
        call.respond(call.receive<Customer>())
    }
}