外部システムへのAPI実行時、data部分の記載について

お世話になっております。

kintoneとクラウドサインという外部システムの連携を考えております。

kintoneに登録している文字列(1列)フォームの値をクラウドサインの書類のタイトルとして登録したのですが、うまくいきません。

 

現在のコードは以下の通りです。

=======================================================

~抜粋~

// リクエストURL
var url_doc = ‘https://api.cloudsign.jp/documents’;

// リクエストヘッダ
var headers_doc = {
‘accepts’ : ‘application/json’,
‘Authorization’ : <アクセストークン>,
};

// リクエストデータ
var data_doc = {
‘title’: ‘testAPI’
};

// kintone.proxy()によるAPI実行
kintone.proxy(url_doc, ‘POST’, headers_doc, data_doc, function(body, status, headers_doc) {

//success
var resp = JSON.parse(body);
console.log(status, JSON.parse(body), headers_doc);

}, function(error) {
//error
console.log(error); //proxy APIのレスポンスボディ(文字列)を表示

});

=======================================================

 

クラウドサインのAPI仕様書には以下のように記載されております。

=======================================================

/**
* 書類の作成
* @param {Object} opts Optional parameters
* @param {String} opts.title
* @param {String} opts.note
* @param {String} opts.templateId
* @param {module:api/DefaultApi~documentsPostCallback} callback The callback function, accepting three arguments: error, data, response
* data is of type: {@link module:model/DocumentModel}
*/
this.documentsPost = function(opts, callback) {
opts = opts || {};
var postBody = null;

var pathParams = {
};
var queryParams = {
};
var headerParams = {
};
var formParams = {
‘title’: opts[‘title’],
‘note’: opts[‘note’],
‘template_id’: opts[‘templateId’]
};

var authNames = [‘accessTokenAuth’];
var contentTypes = [];
var accepts = [‘application/json’];
var returnType = DocumentModel;

return this.apiClient.callApi(
‘/documents’, ‘POST’,
pathParams, queryParams, headerParams, formParams, postBody,
authNames, contentTypes, accepts, returnType, callback
);
}

=======================================================

 

コンソール上で確認したところ、ステータスは200で正常終了しており、クラウドサイン上で書類は作成されておりますが、

タイトルが空白となってしまいます。

 

現在のコードで記載している

// リクエストデータ
var data_doc = {
‘title’: ‘testAPI’
};

の部分の記載方法が間違ってるのだと思うのですが、どのように記載すればよろしいのでしょうか。

 

ご教示いただけると有難いです。

 

以上、よろしくお願いいたします。

 

クラウドサインのAPIの取扱の問題かと思いますが、書いてもらっている仕様と仰ってる部分は仕様というより、何らかのライブラリを利用したサンプルだと思います。仕様はこの辺でしょうか。

 

このようなケース(kintone.proxy()で外部APIリクエストがうまくいかない)では、まずcURLコマンドやPOSTMAN等別の方法で成功させると良いかと思いますが、試行済みでしょうか(比べながら進めるだけで効率が全く異なります)。特にトークンを使ったREST/JSONのAPIであればcURLコマンドは基本的に万能なので、成功させるステップを踏めるか、大切だと思います。ここに成功したら、あとは cURLコマンドとkintone.proxy()の対応 にそって確認すると良いと思います。

 

なお、kintone.proxy() と cURLコマンド等で比べながら作業して、cURLはうまくいくけど、kintone.proxy() ではうまくいかないというケースに遭遇した時には、やはり双方のリクエスト内容が一致していないのが原因ですので、一致するように調整していきます。ただ、cURLとkintone.proxy()が最終的にエンドポイントにどう渡っているかの確認が必要になってきますが、そのような時にはRequestBinのようなサービスを利用する(エンドポイントを切り分け用に差し替える)ことでリクエスト内容を確認することができますので、お試し頂くと良いかと思います。

Ryu Yamashita 様

ご返事が遅れてしまい申し訳ございません。

 

cURLにて検証作業を進めた結果、クラウドサインへの書類作成・pdfファイル添付まで出来ました。

ただ、ローカルのpdfファイルのパスしか指定できず、webサイトのpdfを指定するとエラーになります。

 

-F “uploadfile=@C:\curl\bin\161027soukatu.pdf”

 

×

-F “uploadfile=@http://www.city.osaka.lg.jp/seisakukikakushitsu/cmsfiles/contents/0000190/190830/20120801.pdf

kintone.proxyで実行する際はレコードの添付ファイルを指定したいので、試しに以下のように指定しても

「500  "internal_server_error"message: “failed to parse uploaded file”」というようなエラーとなりました。

=============================================================================

var appid = kintone.app.getId();
var recid = kintone.app.record.getId();

//対象のレコードを取得
kintone.api(‘/k/v1/record’, ‘GET’, { app: appid, id: recid}, function (response) {
//fileKeyの取得
var filekey = response[‘record’][‘File’][‘value’][0].fileKey;

var url = ‘/k/v1/file.json?fileKey=’ + filekey;

var data_file = “uploadfile=@” + url;

=============================================================================

Curlにしてもkintone.proxyにしてもweb上のpdfファイルを指定することはできないのでしょうか?

質問が漠然としており申し訳ありませんが、

手がかりとなるようなアドバイスを頂ければ幸いです。

 

-F “uploadfile=@C:\curl\bin\161027soukatu.pdf”

 

ということは、kintoneの添付ファイルのアップロードAPIと同じくmultipart/form-data形式ですね。ご推察の通り、kintoneからダウンロードして(別ドメインの)kintoneへアップロードできれば、同様に対応可能そうです。ただ、結論からいくと、サーバーサイドでないとちょっと難しい処理です。

 

手順としては、kinotneの file.json でblobもしくはFile形式でダウンロードして、これをmultipart/form-data形式でクラウドサインへアップロードする感じですが、クライアントJSでこれをやりきるには後者のドメイン越えでmultipart/form-data形式でクラウドサインへアップロードするが不可能に近いです。ファイルアップロードですので、kintone.proxy.upload() での対応が考えられますが、ちょっと難しいです。ですので、サーバーサイドの力が必要かと思います。

 

kintone.proxy.upload()に関する考察はこの辺です。

https://www.joyzo.co.jp/blog/2455

https://www.joyzo.co.jp/blog/2474

 

ちなみに、テキストファイルであればその特性上 kintone.proxy() でも対応が可能だったりします。

https://www.joyzo.co.jp/blog/1842

Ryu Yamashita 様

お世話になります。

 

kintone.proxy.upload()、kintone.proxy()では実現が難しいのですね・・・

クラウドサインへアップロードするファイルはpdfなので今回は使えませんが、kintone.proxy()でテキストファイルを送付できるのは何かに使えそうですね!

 

サーバーサイドというのは、「XMLHttpRequest」を使用してファイルをアップロードするという認識でよろしいでしょうか?

> サーバーサイドというのは、「XMLHttpRequest」を使用してファイルをアップロードするという認識でよろしいでしょうか?

 

サーバーサイドは、kintoneとは別に外部に準備したサーバーから(Javaでも何でも良いですが)APIをコールするという意図です。kintoneに入れるJavaScriptカスタマイズから外部に向けてAPIをコールするには、kintone.proxy()、kintone.proxy.upload()からしか手段はありません。

 

もちろん、クラウドサインのAPIがCORS対応していれば、XMLHttpRequest を使ってコールすることが可能ですが、そこは一応確認の余地ありですね。

Ryu Yamashita 様

お世話になります。

 

返信が遅くれてしまい申し訳ありません。

何とかkintone.proxy.upload()で実現できないものかと考えましたが、参照URLのまとめにありますように、

curl -fとの対応が見出されていないとのことなので、断念しました。

(対応するオプションは、もしかすると探せばでてくるかもしれないのでしょうか?)

===================================================================================

・「curl –upload-file」とkintone.proxy.upload()のPUTメソッドが対応している
※「curl -F」、「curl -d」との対応は現状見出されていない
・以下の様なRAWデータ形式のファイルアップロード仕様のAPIを有するサービスで利用できる(ヘッダやクエリで認証情報をのせるのにも対応できそう)

===================================================================================

 

外部にサーバーを準備するのはコスト的に厳しく、クラウドサインのAPIはCORS対応に対応しておらず、

八方ふさがりだったのですが、

確認したところ、Access-Control-Allow-Originに弊社ドメインを設定することができるこかもしれないということでしたので、返答待ちとなっております。

 

XMLHttpRequest を使って別ドメインにファイルをアップロードするのは初の試みになりますので、

つまづいた所は再度質問させていただきたいと思います。

 

> (対応するオプションは、もしかすると探せばでてくるかもしれないのでしょうか?)

 

kintone.proxy.upload() も私もだいぶ調べてみましたが、現状の結論かなぁと思っています。また、探せば出るという類でもないかと考えています(Request.binから逆引き的に見ていますので、それなりに調べがつきます)。

 

Access-Control-Allow-Origin に対応はやや想定外でしたが、それならXMLHttpRequestでも可能ですね。XMLHttpRequestを利用したmultipart/form-data形式のファイルアップロードは、ググっても出てきますが、やはりkintoneのfile.jsonに関する記事が参考になりますので、これらと同じようにやってみるのが良いかと思います。

あとは、cURLで成功させて、やはりRequest.binで成功ケースを確認して、差分を見たり合うように試行錯誤するというプロセスになると思います。

 

Ryu Yamashita 様

お世話になります。

 

kintone.proxy.upload()については、「curl -F」、「curl -d」に対応できるようになったという記事がでるのを期待して待つことにします。

参考URLを教えていただきありがとうございます。XMLHttpRequestでのファイルアップロードが可能になった際は、

今回学んだ、curlで実行→kintoneで実行の流れで作業していきたいと思います。

お世話になります。

 

クラウドサイン側でAccess-Control-Allow-Originへの対応をしていただき、

XMLHttpRequestでのファイルアップロード作業が可能になりました。

 

以下のコードでファイルのアップロードを試みたのですが、エラーが出てしまいます。

============================================================================

//======================
//ファイル添付
//======================

//「docid」は正常に取得できていることを確認しています

var url_file = ‘https://api.cloudsign.jp/documents/’ + docid + ‘/files’;

 

//手順1で取得したfileKeyをurlに設定します。

//「filekey」も正常に取得できていることを確認しています

//「filename」も設定済です

var url = ‘/k/v1/file.json?fileKey=’ + filekey;
var xhr = new XMLHttpRequest();
xhr.open(‘GET’, url, true);
xhr.responseType = ‘blob’;
xhr.setRequestHeader(‘X-Requested-With’ , ‘XMLHttpRequest’);

xhr.onreadystatechange = function(event){
if ((xhr.readyState == 4) && (xhr.status == 200)){
//ファイルがレスポンスとして返却されます

//FormDataにファイルを格納
var formData = new FormData();
formData.append(“__REQUEST_TOKEN__”, kintone.getRequestToken());
formData.append(“file”, xhr.response, filename);

console.log(xhr.response);

//httpRequestの送信

//「Authorization」も正常に認証されていることを確認しています

var xmlHttp = new XMLHttpRequest();
xmlHttp.open(“POST”, encodeURI(url_file), false);
xmlHttp.setRequestHeader(‘X-Requested-With’, ‘XMLHttpRequest’);
xmlHttp.setRequestHeader(‘Authorization’, 'Bearer ’ + response.access_token);
xmlHttp.responseType = ‘multipart/form-data’;
xmlHttp.send(formData);
}
};

xhr.send();

============================================================================

 

kintone上のコンソールでは「xmlHttp.send(formData);」の部分にエラーが出ており、

エラーコードは500 (Internal Server Error)で

メッセージ内容は{“error”:“internal_server_error”,“message”:“failed to parse uploaded file”}です。

 

また、サーバー側のエラーを確認いただいたところ「“no such file”」といったエラーがでているそうです。

ファイルの取得までは問題ないかと思うのですが、このような場合はどこを確認すべきなのでしょうか?

 

cURLでは、url_fileとaccess_tokenの指定は問題なく通っております。

 

以上、お手数ですがアドバイスなどいただければ幸いです。

こちらはkintoneへのアクセス時に要するものですので、不要かと思います。

 

> formData.append(“__REQUEST_TOKEN__”, kintone.getRequestToken());

 

あと、kintoneからのファイルダウンロードのレスポンスである xhr.response は

var blob = new Blob([xhr.response]);

で、formData.append(“file”, blob, filename); かもしれません。

 

更に

> encodeURI(url_file)

については、encodeURIの必要性があるのか?です。

 

こういう時もurlをRequestBinに差し替えて、成功ケースと失敗ケースで見比べるのが良いかと思います。blobが入ってくると、見づらくはなりますが。

 

また、cURLはあくまで外部からリクエストで、xmlhttprequestはJSからという違いは残っています(blobとローカルファイルの違いが影響するのかがちょっと不明ですが)が、基本的な確認方法は、失敗ケースを成功ケースに合うようにしていくという感じだと思います。

Ryu Yamashita 様

お世話になります。

 

ご指摘いただきありがとうございます。

該当箇所を削除し、成功時のログと失敗時のログを比較し修正したところ、

無事にファイルアップロードできました。

 

ファイルアップロードのコードは最終的に以下のようになりました。

=====================================================================

※ファイルキー、ファイル名、アクセストークンは取得済み

//======================
//ファイルアップロード
//======================

var url_file = ‘https://api.cloudsign.jp/documents/’ + docid + ‘/files’;

//取得したfileKeyをurlに設定します。
var url = ‘/k/v1/file.json?fileKey=’ + filekey;
var xhr = new XMLHttpRequest();
xhr.open(‘GET’, url, true);
xhr.setRequestHeader(‘X-Requested-With’ , ‘XMLHttpRequest’);
xhr.setRequestHeader(‘Content-Type’, ‘application/pdf’);
xhr.responseType = ‘blob’;

xhr.onreadystatechange = function(event){
if ((xhr.readyState == 4) && (xhr.status == 200)){
//ファイルがレスポンスとして返却されます

//FormDataにファイルを格納
var blob = new Blob([xhr.response]);
var formData = new FormData();
//formData.append(“file”, blob, filename);
formData.append(“uploadfile”, blob, filename);

//httpRequestの送信
var xmlHttp = new XMLHttpRequest();
xmlHttp.open(“POST”, url_file, false);
xmlHttp.setRequestHeader(‘Accept’, ‘application/json’);
xmlHttp.setRequestHeader(‘Accept’, ‘text/javascript’);
xmlHttp.setRequestHeader(‘Accept’, ‘*/*’);
xmlHttp.setRequestHeader(‘Authorization’, 'Bearer ’ + response.access_token);
xmlHttp.responseType = ‘multipart/form-data’;
xmlHttp.send(formData);

console.log(xmlHttp.response);
//ファイルIDを取得

var fileid = JSON.parse(xmlHttp.responseText).files[0].id;

}
};

xhr.send();

=====================================================================

 

ご指摘いただいた箇所の修正と、「formData.append(“uploadfile”, blob, filename);」の部分で、

「“file”」⇒「“uploadfile”」とすることでファイルアップロードできました。

 

非常に助かりました。ありがとうございます。

2ヶ月弱越し、ベンダーさんとの調整も経て解決に至れてよかったですね。