Cies
06/02/2025, 2:53 PMwww-form-urlencoded
POST body received by a http4k server to a data class
, preferably with kotlinx.serialization
. I've done this before (when not using http4k) with Jackson, using quite some self written code, to (1) create a little language for adding structure to the form's name
values (this allowed me to have nested data structures, and list/arrays, like .user.permissions[5]
) and to parse the data directly to Jackson's internal JSON representation (in order to skip a JSON format step: Jackson was mainly used for it's "type coercion" since all values are received in one big www-form-urlencoded
string).
Is there anything in http4k to make this easier? I've seen the body lens feature, but I have not been able to use it for form submissions.dave
06/02/2025, 3:09 PMMap<String, Any>
whereas the form body would deserialise into Map<String, String>
. So - whatever you're using to deserialise needs to work around that conversion.
Assuming that you can work around that, you can use the httpBodyRoot()
function to create a custom lens creation method. Here's the standard webform version (your one would be much simpler):
fun Body.Companion.webForm(
validator: Validator,
contentNegotiation: ContentNegotiation,
vararg formFields: Lens<WebForm, *>
): BiDiBodyLensSpec<WebForm> =
httpBodyRoot(formFields.map { it.meta }, APPLICATION_FORM_URLENCODED, contentNegotiation)
.map({ it.payload.asString() }, { Body(it) })
.map(
{ WebForm(formParametersFrom(it), emptyList()) },
{ (fields) -> fields.flatMap { pair -> pair.value.map { pair.key to it } }.toUrlFormEncoded() })
.map(
{ it.copy(errors = validator(it, formFields.toList())) },
{ it.copy(errors = validator(it, formFields.toList())) })
For standard and multipart webforms, you use Body.webform().toLens()
for the form and then <http://FormField.int|FormField.int>().required("whatever")
etc.Cies
06/02/2025, 6:06 PM