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を作成します。
- アップロードされた画像を表GALLERYに保存する。
- 表GALLERYに保存されている画像の一覧を返す。
- 表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が割り当たったユーザーでサインインした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を実装しています。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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のインラインに、以下を記述します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
a img { border:none; } | |
#images ol { margin: 1em auto; width: 100%; } | |
#images li { display: inline; } | |
#images a { background: #fff; display: inline; float: left; | |
margin: 0 0 27px 30px; width: auto; padding: 10px 10px 15px; | |
textalign: center; text-decoration: none; color: #333; | |
font-size: 18px; -webkit-box-shadow: 0 3px 6px rgba(0,0,0,.25); | |
-moz-boxshadow: 0 3px 6px rgba(0,0,0,.25); } | |
#images img { display: block; width: 190px; margin-bottom: 12px; } | |
label {font-weight: bold; text-align: right;float: left; | |
width: 120px; margin-right: 0.625em; } | |
label :after {content(":")} | |
input { width: 250px; margin-bottom: 5px;textalign: left} | |
#images a:after { content: attr(title); } |
最後にファイル・アップロードのリージョンを開くボタンを作成します。
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のアプリケーション作成の参考になれば幸いです。
完