kotlin - 文件上传导致 HTTP 415 Unsupported Media Type with RouterFunction 和 HandlerFunction
问题描述
尽管将内容类型设置为 MULTIPART_FORM_DATA,但测试请求将被 HTTP 415 拒绝:
路由器功能:
@Configuration
class Router(
private val uploadHandler: UploadHandler
) {
@ExperimentalPathApi
@Bean
fun route() = coRouter {
POST("/upload", accept(MediaType.MULTIPART_FORM_DATA), uploadHandler::upload)
}
}
处理函数:
@Component
class UploadHandler(
private val uploadVerifier: UploadVerifier,
private val excelTypeResolver: ExcelTypeResolver
) {
@ExperimentalPathApi
suspend fun upload(serverRequest: ServerRequest): ServerResponse {
val now = LocalDateTime.now()
val tmpDir = createTempDirectory("smp-excel-import-$now")
val result = serverRequest.bodyToFlux(FilePart::class.java)
.flatMap { filePart ->
val path = "${tmpDir.name}/${filePart.name()}"
filePart.transferTo(Path.of(path))
.map { excelTypeResolver.resolve(path) }
}
.collect(Collectors.toUnmodifiableSet())
.flatMap { Mono.just(uploadVerifier.verify(it)) }
return if (result.awaitSingle()) {
ServerResponse.ok().buildAndAwait()
} else {
ServerResponse.badRequest().buildAndAwait()
}
}
}
测试:
@IntegrationTest
class UploadHandlerTest {
@Inject
private lateinit var webTestClient: WebTestClient
@Test
fun upload() {
val builder = MultipartBodyBuilder()
builder.part("files", ClassPathResource("CN42N.xlsx")).contentType(MULTIPART_FORM_DATA)
webTestClient.post()
.uri("/upload")
.accept(ALL)
.contentType(MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData(builder.build()))
.exchange()
.expectStatus()
.is2xxSuccessful()
}
}
输出:
2021-02-10 12:07:05.729 [main] ERROR o.s.t.w.r.server.ExchangeResult - Request details for assertion failure:
> POST http://localhost:51485/upload
> WebTestClient-Request-Id: [1]
> Accept: [*/*]
> Content-Type: [multipart/form-data;boundary=Q1BTQgiOIvoHF6eTGJMNuKrS7oOxo1nL8M1X]
1022477 bytes of content.
< 415 UNSUPPORTED_MEDIA_TYPE Unsupported Media Type
< Vary: [Origin, Access-Control-Request-Method, Access-Control-Request-Headers]
< Content-Type: [application/json]
< Content-Length: [146]
< Cache-Control: [no-cache, no-store, max-age=0, must-revalidate]
< Pragma: [no-cache]
< Expires: [0]
< X-Content-Type-Options: [nosniff]
< X-Frame-Options: [DENY]
< X-XSS-Protection: [1 ; mode=block]
< Referrer-Policy: [no-referrer]
{"timestamp":"2021-02-10T11:07:05.650+00:00","path":"/upload","status":415,"error":"Unsupported Media Type","message":"","requestId":"6d0c12f4-1"}
所以客户端请求是正确的,但 Content-Type 配置似乎缺少某处。如果我手动向正在运行的服务器发布请求,行为是相同的。此外,将媒体类型更改为 ALL 没有效果。
解决方案
原因是:
val result = serverRequest.bodyToFlux(FilePart::class.java)
它适用于:
val result = serverRequest.bodyToFlux(Part::class.java)
推荐阅读
- c - 如何在没有 100% CPU 使用率的情况下继续从命名管道读取数据?
- java - Java中算术运算期间的文字假设
- python - 为了分别运行 3 个函数,我创建了 3 个附加线程,但为什么主线程仍在运行这些函数?
- java - Firebase admin sdk setValue 和 setValueAsync 从未完成
- pandas - 寻找相似的阶段
- qt - QML Layout.alignment 未按预期运行
- python - 将路径参数/变量从批处理文件传递给 python,sys.argv[] 只给出最后一个目录而不是整个字符串
- python - PyTorch Transformer 模型,某些值始终为 0
- python - 在函数内部调用函数但终端将函数调用视为全局名称
- android - Flutter:在后台调用函数或只是杀死后台进程