Hello!
Summary
Please add support for sending application/x-www-form-urlencoded
request bodies using the @HttpExchange
/ @PostExchange
abstraction.
Currently, it is not possible to annotate a method argument which is POJO in an @HttpExchange
interface to send form-encoded data in the request body.
Example
@HttpExchange("/token")
interface AuthClient {
@PostExchange(contentType = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
fun getToken(
@RequestBody form: MyForm
): TokenResponse
}
data class MyForm(
val login: String,
val password: String,
}
authClient.getToken(
MyForm("login", "pwd")
}
This code leads to org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/x-www-form-urlencoded' not supported for bodyType=MyForm
Currently to avoid limitations we can use custom org.springframework.http.codec.HttpMessageWriter
class FormUrlencodedWriter : HttpMessageWriter<Any> {
private val supportedMediaType = MediaType.APPLICATION_FORM_URLENCODED
override fun canWrite(elementType: ResolvableType, mediaType: MediaType?): Boolean {
return supportedMediaType == mediaType
}
override fun getWritableMediaTypes(): MutableList<MediaType> {
return mutableListOf(supportedMediaType)
}
override fun write(
inputStream: Publisher<out Any>,
elementType: ResolvableType,
mediaType: MediaType?,
message: ReactiveHttpOutputMessage,
hints: MutableMap<String, Any>
): Mono<Void> {
return Flux.from(inputStream).next().flatMap { value ->
val form = LinkedMultiValueMap<String, String>()
value.javaClass.declaredFields.forEach { field: Field ->
field.isAccessible = true
val fieldValue = field.get(value)
if (fieldValue != null) {
val annotation = field.getDeclaredAnnotation(FormProperty::class.java)
if (annotation != null) {
form.add(annotation.name, fieldValue.toString())
} else {
form.add(field.name, fieldValue.toString())
}
}
}
val encoded = UriComponentsBuilder.newInstance()
.queryParams(form)
.build()
.toUri()
.rawQuery
val bytes = encoded.toByteArray(UTF_8)
message.headers.contentType = supportedMediaType
message.headers.contentLength = bytes.size.toLong()
message.writeWith(Mono.just(message.bufferFactory().wrap(bytes)))
}
}
}
and add it to WebClient
@Bean
fun exchangeStrategies(): ExchangeStrategies = ExchangeStrategies.builder()
.codecs { config -> config.customCodecs().register(FormUrlencodedWriter()) }
.build()
@Bean
fun webClient(exchangeStrategies: ExchangeStrategies): WebClient = WebClient.builder()
.exchangeStrategies(exchangeStrategies)
.baseUrl("http://localhost:8081")
.build()
I understand that if use org.springframework.util.MultiValueMap
as @RequestBody
type then everything works fine but POJO support might be good enhancement
This request is similar to https://github.com/OpenFeign/feign-form/ which design was nice