JunshuO
ServiceNow Employee

Beyond JSON Working with Binary Files and Alternative Content Types in ServiceNow REST APIs.png

C3シリーズの他の記事もご覧ください

 

ほとんどの ServiceNow 開発者は、REST API を JSON 入力、JSON 出力の操作だと考えています。または、詳細度を好む場合は XML です。
 
C3 を構築したとき、ServiceNow の REST 機能は JSON にとどまらず、バイナリファイルの処理、画像の直接提供、CSV データのエクスポート、基本的に必要なコンテンツタイプでの作業ができることがわかりました。
 
これは単なる理論ではありません。実際、何千もの画像を処理し、カスタムRESTエンドポイントを介して安全に提供し、印刷ベンダー向けにCSVエクスポートを作成しました。ServiceNow の REST API は、私が当初考えていたよりもはるかに柔軟であることがわかりました。データストリームの操作方法を一度理解すれば、まったく新しい可能性が広がります。
 
これまでに採用した 3 つの主なアプローチと、ServiceNow での JSON 以外のコンテンツの処理について学んだ内容をご紹介します。

 

問題:JSON では不十分な場合

 

従来の ServiceNow REST API は JSON を適切に処理しますが、C3 にはさらに多くのものが必要でした。

 

  • イメージストレージ:Replicate AI で AI が生成したアバターが、添付ファイルとして保存する必要があるバイナリ画像 URL として返されました
  • 安全なイメージサービング:レコード ACL を変更せずに画像にアクセスできるユーザーをきめ細かく制御する必要がありました
  • データエクスポート:当社の印刷ベンダーは、プロフェッショナルなカード印刷プロセスのために、画像 URL を含む CSV エクスポートを必要としていました

 

これらはそれぞれ、一般的な ServiceNow REST API パスではカバーされていない方法で、バイナリデータまたは代替の添付ファイルスタイルのコンテンツタイプを操作する必要がありました。

 

テクニック 1: バイナリ ファイルを取得して添付ファイルとして保存する

 

最初の課題は、Replicate AI から返された画像を保存することでした。APIは生成された画像へのURLを提供します。画像をフェッチしてそれらのバイナリファイルをServiceNowに保存するには、GETリクエストを送信する必要があります。

 

ReplicateAI スクリプトインクルードでの処理方法は次のとおりです。

 

saveImageAsAttachment: function ({ imageUrl, tableName, recordSysId, fieldOrFileName }) {
    const request = new sn_ws.RESTMessageV2();
    request.setHttpMethod('GET');
    request.setEndpoint(imageUrl);
    request.saveResponseBodyAsAttachment(tableName, recordSysId, fieldOrFileName);
    const attachmentId = request.execute().getResponseAttachmentSysid();
    return attachmentId;
}

 

この機能とは何ですか?

 

この saveResponseBodyAsAttachment() メソッドは、バイナリファイル処理に非常に効果的です。

 

  1. バイナリ画像データをフェッチするためのGETリクエストを実行します。
  2. base64エンコーディングを扱うことなく、バイナリストリームを自動的に処理します。
  3. sys_attachmentテーブルに添付ファイルレコードを作成します。
  4. レコードで参照できるように、添付ファイルのsys_idを返します。

 

その利点

 

  • base64 オーバーヘッドなし:直接バイナリ転送は、画像データのbase64エンコーディングよりも約33〜37%小さくなります
  • 1 行のソリューション:バイナリデータ転送と添付ファイル作成のすべての複雑さを処理します
  • コンテンツタイプの自動検出:ServiceNow はファイルコンテンツから MIME タイプを特定します

 

テクニック 2: バイナリファイルを提供する

 

ServiceNow の組み込み添付ファイルサービングは、基本的に ACL チェックのみがあり、他の処理はありません。しかし、一部の自動化されたサードパーティ処理のために、より柔軟な制御が必要でした。

 

画像を提供するためのスクリプト化された REST API を次に示します。

 

(function process(request, response) {
    const parentId = request.queryParams.parentId;
    const type = request.queryParams.type;

    var attachmentGr = new GlideRecord('sys_attachment');

    if (type == 'avatar_without_bg') {
        let qc = attachmentGr.addQuery('table_name', 'x_snc_cctcg_photo_profile');
        qc.addOrCondition('table_name', 'ZZ_YYx_snc_cctcg_photo_profile');
        attachmentGr.addQuery('file_name', 'avatar_without_bg');
    }
    else if (type == 'qr_code') {
        let qc = attachmentGr.addQuery('table_name', 'x_snc_cctcg_photo_qr_code');
        qc.addOrCondition('table_name', 'ZZ_YYx_snc_cctcg_photo_qr_code');
        attachmentGr.addQuery('file_name', 'qr_code');
    }
    // ... additional type handling

    attachmentGr.addQuery('table_sys_id', parentId);
    attachmentGr.query();

    if (attachmentGr.next()) {
        const attachment = new GlideSysAttachment();
        const attachmentContentStream = attachment.getContentStream(attachmentGr.sys_id.toString());
        response.setHeader('Content-Disposition', `filename="${parentId}_${type}"`);
        response.setContentType('image/png');
        response.setStatus(200);
        const writer = response.getStreamWriter();
        writer.writeStream(attachmentContentStream);
    }

})(request, response);

 

バイナリストリームプロセスの分解

 

1. 添付ファイルのクエリ

 

var attachmentGr = new GlideRecord('sys_attachment');
// Custom logic to find the right attachment based on type and parent ID

 

2. コンテンツストリームを取得する

 

この getContentStream() メソッドは、バイナリファイルデータを表すJavaのGlideScriptableInputStreamを返します。

 

const attachment = new GlideSysAttachment();
const attachmentContentStream = attachment.getContentStream(attachmentGr.sys_id.toString());

 

3. 応答ヘッダーを設定する

 

応答ヘッダーは、ブラウザーまたは他の HTTP クライアントが、転送中のファイルに関する追加データを取得できるようにする HTTP ヘッダーです。この Content-Disposition ヘッダーは、ダウンロードしたファイルのデフォルトファイル名を設定するために使用されます。これにより、ブラウザーから REST エンドポイントの URL に移動すると、ファイルは Content-Disposition ヘッダーで指定された名前で自動的にダウンロードされます。

 

response.setHeader('Content-Disposition', `filename="${parentId}_${type}"`);
response.setContentType('image/png');
response.setStatus(200);

 

4. バイナリデータをストリームに書き込みます

 

ここでは、添付ファイルの入力ストリームをREST API応答の出力ストリームに接続します。その結果、REST API は JSON ペイロードではなく添付ファイルのバイナリデータで応答します。

 

const writer = response.getStreamWriter();
writer.writeStream(attachmentContentStream);

 

カスタムファイルサービングが役立つ理由

 

  • カスタムロジック:レコードの状態に応じて異なる画像を表示するなどのカスタムロジックを実装し、そのロジックをサーバーに格納して一貫性を保つことができます。
  • パブリックアクセスオプション:必要に応じて、テーブル ACL を変更することなく、認証なしで特定の画像を公開できます。
  • カスタムヘッダー:キャッシュの動作、ファイル名、およびコンテンツタイプを希望どおりに制御します
  • 使用状況のトラッキング:必要に応じて、特定の画像やファイルへのアクセスをログまたは監視します。

 

テクニック 3: 文字列ベースのファイル (CSV など) を作成する

 

3つ目の課題は、印刷ベンダー向けのCSVエクスポートを作成することでした。これは、ServiceNow REST API が JSON だけでなく、テキストベースのコンテンツタイプ (PowerPoint、Excel、Word などのファイルも含む) をどのように処理できるかを示しています。明確にしておきますが、これらはダウンロード可能なファイルであり、応答内のJSONの代わりに単なるテキストデータが返されるわけではありません。

 

(function process(request, response) {
    var encodedQuery = request.queryParams.query || 'avatar_without_bg!=';
    
    var profileGR = new GlideRecord('x_snc_cctcg_photo_profile');
    profileGR.addEncodedQuery(encodedQuery);
    profileGR.setLimit(10);
    profileGR.query();

    var csvContent = '"Name","Email","Alternate Email","Special Card","Badge 1","Badge 2","Badge 3","First ServiceNow Release","Flavored persona","Is MVP","Rank ID","Technical Superpower","Team","Avatar","Avatar without Bg","Physical Card","Digital Card","Physical QR Code","Digital QR Code"\n';

    while (profileGR.next()) {
        var qrGR = new GlideRecord('x_snc_cctcg_photo_qr_code');
        var physicalQR = '';
        var digitalQR = '';

        // Get associated QR codes
        qrGR.addQuery('profile', profileGR.getUniqueValue());
        qrGR.query();
        while (qrGR.next()) {
            if (qrGR.qr_type.toString() == 'de165405476cee50e52659db416d436d') {
                physicalQR = qrGR.sys_id.toString();
            } else if (qrGR.qr_type.toString() == 'c4265405476cee50e52659db416d4371') {
                digitalQR = qrGR.sys_id.toString();
            }
        }

        // Build CSV row with image URLs
        var row = [
            '"' + profileGR.name.toString() + '"',
            '"' + profileGR.email.toString() + '"',
            // ... other fields
            (!profileGR.avatar_without_bg.nil() ? '"' + gs.getProperty('glide.servlet.uri') + '/api/x_snc_cctcg_photo/creatorcon_c3_webhooks/image?parentId=' + profileGR.getUniqueValue() + '&type=avatar_without_bg"' : '""'),
            (!profileGR.card_with_qr.nil() ? '"' + gs.getProperty('glide.servlet.uri') + '/api/x_snc_cctcg_photo/creatorcon_c3_webhooks/image?parentId=' + profileGR.getUniqueValue() + '&type=card_physical"' : '""'),
            // ... more image URL fields
        ].join(',');

        csvContent += row + '\n';
    }

    // Output CSV content
    response.setHeader('Content-Type', 'text/csv');
    response.setHeader('Content-Disposition', 'attachment; filename=card_export.csv');
    response.setStatus(200);
    response.getStreamWriter().writeString(csvContent);

})(request, response);

 

CSV エクスポートの内訳

 

1. カスタムコンテンツタイプヘッダー

 

これは、応答を表示しようとするのではなく、CSV ファイルとしてダウンロードするようにブラウザに指示します。

 

response.setHeader('Content-Type', 'text/csv');
response.setHeader('Content-Disposition', 'attachment; filename=card_export.csv');

 

2. CSV 行を作成する

 

CSV の各行は文字列の配列として構築され、カンマで連結されます (カンマ区切りの値などがあるため)。

 

// Build CSV row with image URLs
var row = [
    '"' + profileGR.name.toString() + '"',
    '"' + profileGR.email.toString() + '"',
    // ... other fields
    (!profileGR.avatar_without_bg.nil() ? '"' + gs.getProperty('glide.servlet.uri') + '/api/x_snc_cctcg_photo/creatorcon_c3_webhooks/image?parentId=' + profileGR.getUniqueValue() + '&type=avatar_without_bg"' : '""'),
    (!profileGR.card_with_qr.nil() ? '"' + gs.getProperty('glide.servlet.uri') + '/api/x_snc_cctcg_photo/creatorcon_c3_webhooks/image?parentId=' + profileGR.getUniqueValue() + '&type=card_physical"' : '""'),
    // ... more image URL fields
].join(',');

 

 

3. 文字列値を構築する

 

ファイルのコンテンツとして機能する文字列値を構築します。そこで、最初は CSV ヘッダーを使用して設定し、次に各行を文字列に段階的に追加します。

 

var csvContent = '"Name","Email","Alternate Email","Special Card","Badge 1","Badge 2","Badge 3","First ServiceNow Release","Flavored persona","Is MVP","Rank ID","Technical Superpower","Team","Avatar","Avatar without Bg","Physical Card","Digital Card","Physical QR Code","Digital QR Code"\n';

// Other logic

// For each row
csvContent += row + '\n';

 

4. 画像URLを含める

 

CSV には、画像提供 REST API への完全な URL が含まれているため、外部システムは実際の画像ファイルをダウンロードできます...せっかくなので、気の利いたファイル処理REST API同士を連携させない手はありませんよね?

 

'"' + gs.getProperty('glide.servlet.uri') + '/api/x_snc_cctcg_photo/creatorcon_c3_webhooks/image?parentId=' + profileGR.getUniqueValue() + '&type=avatar_without_bg"'

 

5. JSON の代わりに文字列出力

 

JSONオブジェクトでresponse.setBody()を使用する代わりに、CSV文字列を応答ストリームに直接書き込みます。

 

response.getStreamWriter().writeString(csvContent);

 

まとめ

 

ServiceNow でバイナリファイルや代替コンテンツタイプを操作したところ、このプラットフォームの REST 機能は、多くの人が思っているよりもはるかに柔軟であることがわかりました。JSON API に限定されません...スクリプト化された REST API は、必要なほぼすべてのコンテンツタイプを処理できます。

 

これにより、次の可能性が開かれます。

 

  • バイナリコンテンツをフェッチ、変換、提供するファイル処理ワークフロー (これには MID サーバーのヘルプが必要な場合があります)
  • 特定の外部システム要件に合わせて調整されたカスタムエクスポート形式
  • 従来のレコードベースの権限を超えた柔軟なセキュリティモデル
  • 希望するデータ形式に関係なく、任意の外部システムで動作する統合パターン

 

重要なインサイトは、ServiceNow のスクリプト化されたREST APIが標準のHTTP基盤上に構築されていることです。ストリーム、ヘッダー、およびコンテンツタイプの操作方法を理解したら、必要なほぼすべての統合パターンを実装できます。

 

確かに、JSON はほとんどの API 作業に適していますが、JSON を超える必要がある場合には、統合に必要なあらゆるコンテンツタイプを処理するツールが ServiceNow に用意されています。正直に言うと、それはかなりクールだと思いました。


この記事は機械翻訳されております。最新は元となる記事をご覧ください: https://www.servicenow.com/community/developer-advocate-blog/beyond-json-working-with-binary-files-a...。

免責事項: 一部の日本語は、翻訳ソフトウェアを使用してお客様の便宜のために翻訳されています。正確な翻訳をご提供できるよう相当な努力を払っておりますが、いかなる自動翻訳も人間の翻訳者に代わすることはなく、そのようなことは意図されておりません。翻訳は「現状のまま」提供されています。他言語への翻訳の的確性、信頼性または正確性については、明示または黙示を問わず、いかなる保証も行われません。翻訳ソフトには限界があるため、一部のコンテンツが正確に翻訳されていない場合があります。これらの資料の公用言語は英語です。翻訳の際に生じる相違または不一致は、コンプライアンスまたは履行の目的においては拘束力を有さず、法的効力はないものとします。

This article was machine translated and replicated from the original source: https://www.servicenow.com/community/developer-advocate-blog/beyond-json-working-with-binary-files-a...。