2022年4月14日木曜日

Oracle REST Data ServicesのマニュアルにあるImage Galleryのサンプル・アプリを作成する

 Oracle REST Data Servicesのマニュアルの付録Dとして、イメージ・ギャラリの作成というセクションがあります。

Oracle APEXで作成したアプリケーションと、ORDSのREST APIの連携を実装しています。サーバー側のPL/SQLよりORDS REST APIを呼び出す例は多いのですが、このサンプルはブラウザのJavaScriptからORDSのREST APIを呼び出しています。

ただし、古い環境を元にしていて、書かれている通りに手順を追うことができません。Always FreeのAutonomous DatabaseのAPEX 21.2の環境上で、このサンプル・アプリケーションを作成してみました。

作成したアプリケーションは以下のようになります。


これより、実施した作業を紹介します。


ギャラリ・データベース表の作成


アップロードした画像を保存する表GALLERYを作成します。

SQLワークショップユーティリティクイックSQLを開きます。以下のモデルを使って、表GALLERYを作成します。

gallery
    title vc1000 /nn
    content_type vc1000 /nn
    image blob /nn

SQLの生成SQLスクリプトを保存レビューおよび実行を順次クリックします。

表GALLERYが作成されたら作業は完了です。アプリケーションの作成は行いません。


ギャラリRESTfulサービス・モジュールの作成


以下の3つのREST APIを作成します。

  1. アップロードされた画像を表GALLERYに保存する。
  2. 表GALLERYに保存されている画像の一覧を返す。
  3. 表GALLERYに保存されている画像を取り出す。
ORDSのREST APIを作成する方法はいくつかありますが、今回は、Oracle APEXが提供しているSQLワークショップRESTfulサービスを使います。


初回のアクセスの場合、ORDSに登録されていないスキーマとメッセージが表示されることがあります。このようなときは、ORDSにスキーマを登録を実行します。


ダイアログが開きます。

RESTfulアクセスの有効化ONになっていることを確認して、スキーマ属性の保存を実行します。


以上で、APEXのワークスペース・スキーマでREST APIを作成できるようになりました。

最初にモジュールを作成します。

左ペインよりモジュールを選択し、モジュールの作成を実行します。


モジュール名gallery.exampleベース・パスgallery/公開ONとして、モジュールの作成を実行します。


モジュールが作成されたら、続いてテンプレートの作成をクリックします。


URIテンプレートgallery/とします。

テンプレートの作成を実行します。


作成されたURIテンプレートに、ハンドラを作成します。

ハンドラの作成をクリックします。


メソッドとしてPOSTを選択します。ソースには以下を記述します。
declare
    image_id gallery.id%type;
begin
    insert into gallery (title,content_type,image) 
        values  (:title,:content_type,:body)
        returning id into image_id;
    :status_code := 201;
    :forward_location := './' || image_id;
end;
パラメータ行の追加をクリックします。アップロードされるファイル名をHTTPのヘッダーSlugとして受け付け、バインド変数titleに渡されるようにします。

追加した行の名前Slugバインド変数titleアクセス・メソッドINソース・タイプHTTP HEADERデータ型にはSTRINGを指定します。

以上を設定し、ハンドラの作成を実行します。


ハンドラのソースで使用しているstatus_codeおよびfoward_locationは、暗黙的パラメータとしてORDSが定義しているバインド変数です。status_codeはHTTPのレスポンス・コード、forward_locationはLocationヘッダーとして解釈されます。

テンプレートimages/に、メソッドGETのハンドラを作成します。

ソースは以下のSELECT文になります。
select id,title,content_type from gallery order by id desc
メソッドGETソース・タイプとしてCollection Queryを選択します。

以上を設定して、ハンドラの作成を実行します。


それぞれの画像を取り出すREST APIのテンプレートを作成します。

モジュールに戻り、テンプレートの作成をクリックします。テンプレートimages/を作成したときと、同じ手順になります。

URIテンプレートとしてimages/:idを設定し、テンプレートの作成を実行します。


作成したテンプレートimages/:idに、画像を取り出すGETハンドラを作成します。

ソースとして、以下のSELECT文を記述します。バインド変数:idは、URIテンプレートから取得されます。
select content_type, image from gallery where id = :id
メソッドGETソース・タイプとしてMedia Resourceを選択し、ハンドラの作成を実行します。


以上で、今回作成するアプリケーションで使用するREST APIが完成しました。


ギャラリRESTfulサービスの保護



作成したREST APIを、APEXで認証されたセッションからのみ呼び出しができるように保護します。マニュアルでは、ファースト・パーティ認証として説明がされています。

今回はロールは新規作成せず、デフォルトで作成されているRESTful Servicesを使います。


左ペインにて権限を選択します。権限の作成をクリックします。


名前をモジュール名と同じgallery.exampleタイトルギャラリRESTfulサービスとします。ロールとしてRESTful Servicesモジュールとしてgallery.exampleを選択します。

以上で、権限の作成を実行します。


モジュールgallery.exampleのREST APIの保護が設定されました。

ユーザーとグループの管理を開き、gallery.exampleを呼び出すユーザーにRESTful Servicesのロールを割り当てます。


ロールを割り当てるユーザーの設定を開き、グループ割当てのセクションでRESTful Servicesを割り当てます。

変更の適用を実行します。


これで、グループ割当て(ロール)としてRESTful Servicesが割り当たったユーザーでサインインしたAPEXアプリケーションより、作成したギャラリRESTfulサービスを呼び出すことができるようになりました。


ギャラリ・アプリケーションの作成



ギャラリRESTfulサービスを使用するAPEXアプリケーションを作成します。

アプリケーション作成ウィザードを起動します。名前Image Galleryとし、それ以外は指定せず空のアプリケーションを作成します。

アプリケーションの作成を実行します。


アプリケーションが作成されます。

Image Galleryのアプリケーションは、すべてホーム・ページに実装します。

ページ・デザイナにて、ホーム・ページを開きます。


最初にファイルをアップロードするダイアログを作成します。APEXが標準で提供している機能を使用せずファイルをアップロードするため、ファイルを選択するアイテムはHTMLを直接記述して生成します。また、ファイルのアップロード処理は、JavaScriptでコーディングします。

Bodyにリージョンを作成します。

識別タイトルファイル・アップロードタイプとして静的コンテンツを選択します。外観テンプレートとして、Inline Dialogを選択し、このリージョンがダイアログとして表示されるようにします。


テンプレート・オプションをクリックして開き、Auto Hieightチェックを入れます。OKをクリックして変更を保存します。


作成したダイアログのリージョンファイル・アップロードに、サブリージョンを作成します。

リージョンファイル・アップロード上でコンテキスト・メニューを表示させ、サブリージョンの作成を実行します。


識別タイトルファイルの選択タイプ静的コンテンツとします。ソースHTMLコードとして以下を記述し、ファイルを選択するHTML要素を記述します。

<label for="file">File</label>
<input type="file" name="file" id="file" class="text ui-widget-content ui-corner-all"/>

レイアウト親リージョンファイル・アップロードであることを確認し、位置をSub RegionsからRegion Bodyに変更します。

位置がSub Regionsだと、サブリージョンは必ず画面の底部に配置されます。Region Bodyに変更することにより、画面上の配置を自由に決めることができます。これは、APEX 21.2から利用できる新機能です。

外観テンプレートとしてBlank with Attributesを選び、リージョンのタイトルや枠の表示を省きます。


リージョンファイル・アップロードに、ファイルのアップロードを実行するボタンを作成します。

識別ボタン名B_UPLOADラベルアップロードとします。動作アクションとして、動的アクションで定義を選択します。


ボタンB_UPLOADに動的アクションを作成します。ボタンB_UPLOAD上でコンテキスト・メニューを開き、動的アクションの作成を実行します。

識別名前ファイルのアップロードとします。タイミングはデフォルトで、イベントクリック選択タイプボタンボタンB_UPLOADとなります。


デフォルトで作成されるTrueアクションを選択し、識別アクションJavaScriptコードの実行に変更します。

設定コードとして、以下を記述します。
var file = document.querySelector('input[type="file"]');
uploadFiles(gallery_url,file.files[0],function() {
    doFetch();
});
ファンクションuploadFilesおよびdoFetchは、この後にページ・プロパティJavaScriptのセクションに定義します。


ファイルのアップロードを開始したら、リージョンを閉じるようTrueアクションを追加します。JavaScriptコードの実行の下にTrueアクションを作成します。

作成したTrueアクションの識別アクションとして、リージョンを閉じるを選択します。影響を受ける要素選択タイプリージョンリージョンとしてファイル・アップロードを選択します。


ファイルをアップロードするボタンの作成は、以上で完了です。

ファイルのアップロードをせずダイアログを閉じるためのボタン、キャンセルを作成します。

Region Body上でコンテキスト・メニューを表示させ、ボタンの作成を実行します。

識別ボタン名B_CANCELラベルキャンセルとします。アップロード・ボタンの右隣に配置されるよう、レイアウト新規行の開始OFFにします。動作アクションとして、動的アクションで定義を選択します。


ボタンB_CANCELに動的アクションを作成します。

識別名前アップロードのキャンセルとします。タイミングデフォルトのまま変更しません。


デフォルトで作成されたTrueアクションを選択し、リージョンファイル・アップロードを閉じるアクションに変更します。ボタンB_UPLOADに作成したアクションリージョンを閉じると同じです。


以上で、ファイルのアップロードを実行するダイアログは完成です。

画像の一覧を表示するリージョンを作成します。

新規にBodyにリージョンを作成します。

識別タイトルImagesタイプとして静的コンテンツを選択します。外観テンプレートとしてBlank with Attributesを選択します。詳細静的IDとしてimagesを設定します。


APEXのカード・リージョンを使用した方が、簡単に画像の一覧を表示することができます。このアプリケーションの目的は、ORDSのREST APIを呼び出す例を提供することなので、あえてJavaScriptでコードを書いて画像の取得と描画をしています。

ページ・プロパティJavaScriptファンクションおよびグローバル変数の宣言として、以下を記述します。ORDS REST APIを呼び出して画像ファイルをアップロードするuploadFilesと、REST APIを呼び出して画像を画面に表示するdoFetchを実装しています。

var workspace_path_prefix = 'apexdev';
var gallery_url = './' + workspace_path_prefix + '/gallery/images/';
var cred = apex.env.APP_ID + "," + apex.env.APP_SESSION;
/*
* ORDS REST APIを呼び出して、ファイルをアップロードする。
*
* APEXでのアップロードは指定した表のBLOB列に書き込むか、APEX_APPLICATION_TEMP_FILESに
* アップロードした内容を保存した後にデータの操作を行う。
*
* ORDS REST APIを使うと、ORDSのRESTサービスではBLOBのデータを受け取り、
* 表に保存するかどうかは、ハンドラ内で決定する。
*/
function uploadFiles(url, fileOrBlob, onload ) {
var name = 'unspecified';
if ( fileOrBlob['name'] ) {
name = fileOrBlob.name;
}
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.setRequestHeader('Apex-Session', cred);
xhr.setRequestHeader('Slug', encodeURI(name));
xhr.onload = onload;
xhr.send(fileOrBlob);
}
/*
* ORDSのREST APIを呼び出して、画像を取り出す。
* APEXのセッションをfetchで渡す方法と、URIに渡す方法のサンプル。
*/
const doFetch = async () => {
// 保存されている画像の一覧をfetchで取得する。
const request = await fetch(gallery_url, {
method: "GET",
headers: { 'Apex-Session': cred }
});
// 画像の一覧はJSON形式。
const response = await request.text();
const data = JSON.parse(response);
// それぞれの画像を取り出して表示する。
$('#image-list').remove();
var $images = $('<ol id="image-list"></ol>');
for ( i in data.items ) {
var item = data.items[i];
var uri = gallery_url + item.id + "?_apex_session=" + cred;
var $image = $('<li></li>')
.append('<a href="' + uri + '" + title="' +
decodeURI(item.title) + '"><img src="'+ uri +
'"></a>');
$images.append($image);
}
$('#images').append($images);
};



ページがロードされたときに画像が表示されるよう、JavaScriptページ・ロード時に実行に、以下を記述します。

doFetch();


ファイルを選択するHTML要素や画像の表示は、APEXの機能を使わずに生成しています。APEXの外観テンプレート・オプションは適用できないため、CSSを記述します。

ページ・プロパティのCSSのインラインに、以下を記述します。




最後にファイル・アップロードのリージョンを開くボタンを作成します。

Body上でコンテキスト・メニューを開き、ボタンの作成を実行します。

Bodyの先頭に配置します。識別ボタン名B_OPEN_UPLOADラベルアップロードとします。動作アクションとして、動的アクションで定義を選択します。


ボタンB_OPEN_UPLOADに動的アクションを作成します。

ボタンB_OPEN_UPLOAD上でコンテスト・メニューを開き、動的アクションの作成を実行します。

識別名前ファイル・アップロードを開くとします。タイミングデフォルトから変更しません。


すでに作成されているTrueアクションを選択し、識別アクションリージョンを開くに変更します。影響を受ける要素選択タイプリージョンリージョンとしてファイル・アップロードを選択します。


ボタンB_OPEN_UPLOADをクリックすると、アップロードするファイルを選択するダイアログが表示されます。

以上でアプリケーションは完成です。アプリケーションを実行すると、記事の最初のGIF動画のような動作をします。

マニュアルにはこの他に、サード・パーティ・アプリケーションからのRESTfulサービスへのアクセスとして、OAuth2による保護について説明されています。こちらはAPEX以外のアプリケーションから呼び出すことが想定されています。今回の説明には含めません。OAuth2による保護については、以前に記事が参考になるかと思います。

今回作成したアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/ords-gallery-images-sample.sql

Oracle APEXのアプリケーション作成の参考になれば幸いです。