Search code examples
kotlinmicronautmicronaut-client

Client posting multipart form data


I'm trying to create a client which I use to test my controller

The controller

@Secured(SecurityRule.IS_AUTHENTICATED)
@Controller
class InjuryController(private val userService: UserService, private val injuryService: InjuryService) {
...
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Post("/injuries/{id}/images")
    fun postImage(id: Long, file: CompletedFileUpload, principal: Principal): HttpResponse<*>? {
        ...
        return HttpResponse.ok(imageReference)
    }
...
}

The client

@Client("/")
interface InjuryClient {
    @Post("/injuries/{id}/images", produces = [MULTIPART_FORM_DATA])
    fun postImage(id: Long, body: MultipartBody, @Header authorization: String): ImageReference
}

The test

@Test
fun `Post an image an injury`() {
    // Given
    val description = "description"
    val occurredAt = LocalDateTime.now()
    val id = createInjury(description, occurredAt).id

    val toWrite = "test file"
    val file = File.createTempFile("data", ".txt")
    FileWriter(file).apply {
        write(toWrite)
        close()
    }

    val requestBody = MultipartBody.builder()
            .addPart("data",
                    file.name,
                    MediaType.TEXT_PLAIN_TYPE,
                    file
            ).build()

    // When
    val response = injuryClient.postImage(id, requestBody, authorization)

    // Then
    assertEquals("$id:${file.name}", response.key)
}

The error

The type java.util.LinkedHashMap is not a supported type for a multipart request body
io.micronaut.http.multipart.MultipartException: The type java.util.LinkedHashMap is not a supported type for a multipart request body
    at io.micronaut.http.client.DefaultHttpClient.buildMultipartRequest(DefaultHttpClient.java:2063)
    at io.micronaut.http.client.DefaultHttpClient.buildNettyRequest(DefaultHttpClient.java:1480)
    at io.micronaut.http.client.DefaultHttpClient.sendRequestThroughChannel(DefaultHttpClient.java:1599)
    at io.micronaut.http.client.DefaultHttpClient.lambda$null$27(DefaultHttpClient.java:1035)
    at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:577)
    at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:570)
    at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:549)
    at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:490)
    at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:615)
    at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:604)
    at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:104)
    at io.netty.channel.DefaultChannelPromise.trySuccess(DefaultChannelPromise.java:84)
    at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.fulfillConnectPromise(AbstractNioChannel.java:300)
    at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:335)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:688)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:635)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:552)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:514)
    at io.netty.util.concurrent.SingleThreadEventExecutor$6.run(SingleThreadEventExecutor.java:1044)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:834)

Any clue about how to get rid of that error?


Solution

  • The client assumes method arguments are members of the body instead of each one representing the entire body. You can achieve the desired behavior by annotating the body argument in the client with @Body