前提・実現したいこと
Kotlin+SpringBoot でサーバ内で生成したファイルを連携したチャットルームに投稿する機能を実装しています。
対象チャットツールはChatworkです。
ChatWorkAPIを叩く際のHTTPクライアントはSpring RestTemplateを利用しています。
実現したいことは以下のエンドポイントへのファイルアップロードです。
http://developer.chatwork.com/ja/endpoint_rooms.html#POST-rooms-room_id-files
発生している問題・エラーメッセージ
【問題点】
エンドポイントが要求している形でリクエストボディが作成できていないのか、ステータスコード400(Bad Request)でレスポンスされてしまいます。
何が不足しているのかわからず、、、確認すべき点や修正すべき箇所があればご指摘いただきたいです。
該当のソースコード
Kotlin
1package com.***.***.domain.service.api.chatwork.impl 2 3import com.***.***.domain.service.api.chatwork.ChatWorkRoomID 4import com.***.***.domain.service.api.chatwork.ChatWorkToken 5import com.***.***.domain.service.api.chatwork.ChatworkService 6import org.springframework.core.io.ByteArrayResource 7import org.springframework.core.io.Resource 8import org.springframework.http.ContentDisposition 9import org.springframework.http.HttpEntity 10import org.springframework.http.HttpHeaders 11import org.springframework.http.MediaType 12import org.springframework.stereotype.Service 13import org.springframework.util.LinkedMultiValueMap 14import org.springframework.util.MultiValueMap 15import org.springframework.web.client.RestTemplate 16import java.io.File 17import java.nio.file.Files 18import java.nio.file.Path 19 20 21@Service 22class ChatworkServiceImpl(private val restTemplate: RestTemplate) : ChatworkService { 23 24 /** 25 * ファイル + メッセージ 投稿. 26 */ 27 override fun post(message: String, filePath: Path, roomID: ChatWorkRoomID, token: ChatWorkToken): Boolean { 28 // エンドポイント 29 val url = "https://api.chatwork.com/v2/rooms/${roomID}/files" 30 // アップロードファイル 31 val file: File = filePath.toFile() 32 // アップロードファイルのコンテンツタイプ 33 val contentType: String = Files.probeContentType(filePath) 34 35 // HTTPリクエストヘッダ作成 36 val headers = HttpHeaders() 37 headers.contentType = MediaType.MULTIPART_FORM_DATA 38 headers.set("X-ChatWorkToken", token) 39 40 // HTTPリクエストボディ作成 41 val fileHeaders = HttpHeaders() 42 fileHeaders.contentType = MediaType.parseMediaType(contentType) 43 val uploadFileData: Resource = object : ByteArrayResource(file.readBytes()) { 44 override fun getFilename(): String { 45 return file.name 46 } 47 48 override fun contentLength(): Long { 49 return file.length() 50 } 51 } 52 val fileEntity = HttpEntity(uploadFileData, fileHeaders) 53 val body: MultiValueMap<String, Object> = LinkedMultiValueMap<String, Object>() 54 body.add("file", fileEntity as Object) 55 body.add("message", "テスト" as Object) 56 57 // リクエスト & レスポンス 58 val request = HttpEntity(body, headers) 59 val response = restTemplate.postForEntity(url, request, String::class.java) 60 return response.statusCode.is2xxSuccessful 61 } 62}
試したこと
ファイルアップロードで使用したAPIトークンやRoomIDは以下のメッセージ送信処理では有効でしたので必要なパラメータは用意できていると判断しています。
Chatworkのサポートにアドバイスを求めるもAPIについてはサポート対象外とのこと...
HTTPリクエストのログ
[API:Request(5c1b57c0-228e-4c05-a605-92e019721ac5)] Request=[POST:https://api.chatwork.com/v2/rooms/00000000/files], Headers=[[Accept:"text/plain, application/json, application/*+json, */*", Content-Type:"multipart/form-data;charset=UTF-8;boundary=tQc3hQBjk3FyTcoV9i2q5WDRKyraUHhMY_1WPY", X-ChatWorkToken:"***********", Content-Length:"9287"]], Body=[--tQc3hQBjk3FyTcoV9i2q5WDRKyraUHhMY_1WPY Content-Disposition: form-data; name="file"; filename="template.xlsx" Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet Content-Length: 8861 �k��q�:&zw����`Ⱥ%�e9v�7;�[f��nĠh]�����8x���X�V�� �u�2���b��ޒ��*���'�p�^ՉRI-����Y�������JҺ�*�W�˟��h�صF-��ߤ[�~��PK!��GI{docProps/core.xml . . 省略 . ��GI{�docProps/core.xmlPK-!EO�U�#-docProps/app.xmlPK � --tQc3hQBjk3FyTcoV9i2q5WDRKyraUHhMY_1WPY Content-Disposition: form-data; name="message" Content-Type: text/plain;charset=UTF-8 Content-Length: 9 テスト --tQc3hQBjk3FyTcoV9i2q5WDRKyraUHhMY_1WPY-- ]
HTTPレスポンスのログ
[API:Response(5c1b57c0-228e-4c05-a605-92e019721ac5)] Status=[400:Bad Request], Headers=[[Content-Type:"application/json", Content-Length:"64", Connection:"keep-alive", Date:"Wed, 11 Dec 2019 10:25:45 GMT", x-amzn-RequestId:"01c2efde-74d2-46c2-ab03-3f3478abf844", x-amzn-ErrorType:"AccessDeniedException", x-amz-apigw-id:"EiOWeEQiNjMFnkg=", X-Cache:"Error from cloudfront", Via:"1.1 c6a9836e54cf7bc2bb59e3fb86f871e1.cloudfront.net (CloudFront)", X-Amz-Cf-Pop:"NRT20-C1", X-Amz-Cf-Id:"LgV5C-NNYcwt52lodk8KhLRct-35xCyLYPqG71tNqIQpoSxWGkHtCw=="]], Body=[{"errors":["Please upload the file with `multipart/form-data`"]}]
あなたの回答
tips
プレビュー