手動による REST サービスの作成
この付録では、%CSP.RESTOpens in a new tab クラスのサブクラスを作成することによって、InterSystems IRIS® REST サービスを手動で作成する方法について説明します。この手順で作成した手動コーディングの REST サービスは、すべての API 管理ツールで機能するとは限りません。
“REST サービスの保護” の “REST サービスの認証の設定” も参照してください。
手動による REST サービスの作成の基本
REST サービスを手動で定義するには、以下の手順を実行します。
-
%CSP.RESTOpens in a new tab のサブクラスである REST サービス・クラスを作成します。サブクラスで以下を実行します。
-
REST URL と HTTP メソッドに対して実行される InterSystems IRIS のメソッドを指定する URL マップを定義します。
-
オプションで、UseSession パラメータを指定します。このパラメータは、各 REST 呼び出しを独自の Web セッションで実行するか、1 つのセッションを他の REST 呼び出しと共有するかを制御します。
-
必要な場合は、エラー処理のメソッドをオーバーライドできます。
実装コードをディスパッチ・コードから分離する場合、REST サービスを実装するメソッドを別のクラスで定義し、それらのメソッドを URL マップから呼び出すことができます。
-
REST サービス・クラスをディスパッチ・クラスとして使用する Web アプリケーションを定義します。
Web アプリケーションとそのセキュリティを定義するには、[Web アプリケーション] ページに移動します ([システム管理]→[セキュリティ]→[アプリケーション]→[Web アプリケーション] の順にクリックします)。
Web アプリケーションを定義する際には、[ディスパッチ・クラス] を、作成した REST サービス・クラスの名前に設定します。
また、アプリケーションの名前を REST 呼び出しの URL の最初の部分として指定します。名前の例としては /csp/mynamespace や /csp/myapp がありますが、URL で使用できる任意のテキストを指定できます。
1 つのネームスペース内に REST サービス・クラスを複数定義できます。独自のエントリ・ポイントを有する REST サービス・クラスにはそれぞれ、独自の Web アプリケーションが必要です。
URL マップの作成
REST サービス・クラスで、サービスを実装するメソッドに REST 呼び出しを関連付ける、UrlMap という名前の XData ブロックを定義します。URL のコンテンツに基づいて呼び出しをメソッドに直接送信することも、URL に基づいて呼び出しを別の REST サービス・クラスに転送することもできます。Web アプリケーションが処理している関連するサービスが少数の場合、実装メソッドに呼び出しを直接送信することができます。一方、Web アプリケーションが多数の異なるサービスを処理する場合は別個の REST サービス・クラスを定義し、そのそれぞれで関連する一連のサービスを処理できます。その後、REST 呼び出しを他の REST サービス・クラスに適宜転送する中央の REST サービス・クラスを使用するように Web アプリケーションを構成します。
%CSP.RESTOpens in a new tab のサブクラスが呼び出しをメソッドに直接送信する場合、UrlMap には、一連の <Route> 要素を含む <Routes> 定義が含まれます。それぞれの <Route> 要素で、指定した URL と HTTP 操作について呼び出すクラス・メソッドを指定します。一般的に、REST は GET、POST、PUT、または DELETE 操作を使用しますが、任意の HTTP 操作を指定することができます。URL には必要に応じてパラメータを含めることができます。このパラメータは、REST URL の一部として指定され、指定のメソッドにパラメータとして渡されます。
%CSP.RESTOpens in a new tab のサブクラスが %CSP.RESTOpens in a new tab の他のサブクラスに呼び出しを転送する場合、UrlMap には、一連の <Map> 要素を含む <Routes> 定義が含まれます。<Map> 要素は、指定された接頭語を持つすべての呼び出しを別の REST サービス・クラスに転送します。その後、そのクラスによって動作が実装されます。呼び出しをメソッドに直接送信する、または別のサブクラスに転送することで動作を実装できます。
Important:
InterSystems IRIS は、受信した REST URL を各 <Route> の URL プロパティおよび各 <Map> の Prefix プロパティと比較します。その際、URL マップの最初の項目から始めて、同じ HTTP 要求メソッドを使用する最初に一致した項目で停止します。そのため、<Routes> 内の要素の順序は重要です。受信した URL が URL マップの複数の要素と一致する可能性がある場合、InterSystems IRIS は、最初に一致した要素を使用し、一致する可能性があるそれ以降の要素は無視します。
<Route> 要素を使用した UrlMap
InterSystems IRIS は、受信した URL および HTTP 要求メソッドを URL マップの各 <Route> 要素と比較します。そして、最初に一致した <Route> 要素で指定されているメソッドを呼び出します。<Route> 要素は、以下の 3 つの部分で構成されます。
-
Url — REST サービスを呼び出す REST URL の末尾部分の書式を指定します。Url は : (コロン) で始まるテキスト要素とパラメータから構成されます。
-
Method — REST 呼び出しの HTTP 要求メソッドを指定します。一般的には GET、POST、PUT、または DELETE ですが、任意の HTTP 要求メソッドを使用できます。要求メソッドには、サービスで実行される機能に適したものを選択する必要がありますが、%CSP.RESTOpens in a new tab クラスはそれぞれのメソッドに関する特別な処理を実行しません。HTTP 要求メソッドはすべて大文字で指定する必要があります。
-
Call — REST サービスを実行するために呼び出すクラス・メソッドを指定します。既定では、このクラス・メソッドは REST サービス・クラスで定義されますが、任意のクラス・メソッドを明示的に指定することもできます。
例えば、以下の <Route> を考えてみましょう。
<Route Url="/echo" Method="POST" Call="Echo" Cors="false" />
これは、REST 呼び出しの最後が /echo になることと、POST メソッドを使用することを指定しています。これにより、REST サービスを定義する REST.DocServer クラスの Echo クラス・メソッドを呼び出します。Cors プロパティはオプションです。詳細は、“CORS を使用するための REST サービスの変更” を参照してください。
完全な REST URL は、以下の部分で構成されます。
-
InterSystems IRIS サーバのサーバ名とポート。この章の例では、サーバ名およびポート http://localhost:52773/ を使用しています。
-
[Web アプリケーション] ページ ([システム管理]→[セキュリティ]→[アプリケーション]→[Web アプリケーション] の順にクリック) で定義された Web アプリケーションの名前(例 : /csp/samples/docserver)。
-
<Route> 要素の Url プロパティ。Url プロパティのセグメントの先頭が : (コロン) の場合は、そのセグメントがパラメータであることを意味します。パラメータはその URL セグメントのどの値とも一致します。この値は、パラメータとしてメソッドに渡されます。
前述の例の場合、TCP トレース・ユーティリティで以下のように完全な REST 呼び出しが示されます。
POST /csp/samples/docserver/echo HTTP/1.1
Host: localhost:52773
REST サービスを実装するコードを %CSP.RESTOpens in a new tab ディスパッチ・コードと区別する場合、REST サービスを実装するメソッドを別のクラスで定義し、Call 要素内でクラスとメソッドを指定できます。
パラメータの指定
以下の <Route> 定義では、URL 内に 2 つのパラメータ (namespace と class) が定義されています。
<Route Url="/class/:namespace/:classname" Method="GET" Call="GetClass" />
REST 呼び出しの URL は /csp/samples/docserver/class/ で始まり、それに続く 2 つの URL の要素では 2 つのパラメータを指定します。GetClass() メソッドは、クエリするネームスペースおよびクラス名としてこれらのパラメータを使用します。例えば、以下の REST 呼び出しを考えてみましょう。
http://localhost:52773/csp/samples/docserver/class/samples/Cinema.Review
この REST 呼び出しは GetClass() メソッドを呼び出し、文字列 "samples" および "Cinema.Review" をパラメータ値として渡します。GetClass() メソッドには、以下のシグニチャがあります。
/// This method returns the class text for the named class
ClassMethod GetClass(pNamespace As %String,
pClassname As %String) As %Status
{
1 つの URL に対する複数のルートの指定
指定された URL について、異なる HTTP 要求メソッドをサポートできます。そのためには、HTTP 要求ごとに別個の ObjectScript メソッドを定義するか、要求を検証する 1 つの ObjectScript メソッドを使用します。
以下の例では、1 つの URL について HTTP 要求メソッドごとに異なるメソッドを使用しています。
<Route Url="/request" Method="GET" Call="GetRequest" />
<Route Url="/request" Method="POST" Call="PostRequest" />
このようなルーティングにより、HTTP GET メソッドで URL /csp/samples/docserver/request を呼び出したときには、GetRequest() メソッドが起動されます。HTTP POST メソッドで呼び出すと、PostRequest() メソッドが起動されます。
一方、以下の <Route> 定義を使用することもできます。
<Route Url="/request" Method="GET" Call="Request" />
<Route Url="/request" Method="POST" Call="Request" />
この場合は、Request() メソッドにより、GET 操作または POST 操作の呼び出しが処理されます。このメソッドにより、%CSP.RequestOpens in a new tab のインスタンスである %request オブジェクトが検証されます。このオブジェクトでは、URL プロパティに URL のテキストが含まれています。
ルート・マップでの正規表現
ルート・マップ内で正規表現を使用できます。これは、ニーズを満たすために REST サービスを定義する方法が他にない場合にのみ行うことをお勧めします。このセクションで詳細に説明します (ObjectScript での正規表現の詳細は、"ObjectScript の使用法" の “正規表現” を参照してください)。
内部的には、URL 内でのパラメータの定義に使用する :parameter-name 構文は、正規表現を使用して実装されます。:parameter-name として指定された各セグメントは、繰り返す一致グループが含まれる正規表現、具体的には ([^/]+) 正規表現に変換されます。この構文は、文字列 (長さがゼロ以外) に / (スラッシュ) 文字が含まれない限り、あらゆる文字列と一致します。したがって、GetClass() サンプル Url="/class/:namespace/:classname" は、以下と同等です。
<Route Url="/class/([^/]+)/([^/]+)" Method="GET" Call="GetClass" />
ここでは、2 つのパラメータを指定する 2 つの一致グループがあります。
ほとんどの場合、この形式で REST URL の指定に十分な柔軟性を提供しますが、上級ユーザはルート定義に正規表現形式を直接使用することができます。この URL は、正規表現およびそれぞれの一致グループに合致している必要があります。これは、1 組の括弧によって指定され、メソッドに渡されるパラメータを定義します。
例えば、以下のルート・マップを考えてみます。
<Routes>
<Route Url="/Move/:direction" Method="GET" Call="Move" />
<Route Url="/Move2/(east|west|north|south)" Method="GET" Call="Move" />
</Routes>
最初のルートでは、パラメータに任意の値を指定できます。パラメータの値にかかわらず、Move() メソッドが呼び出されます。2 つ目のルートでは、パラメータの値は east、west、north、south のいずれかである必要があります。これら以外の値を指定してこの 2 つ目のルートを呼び出した場合、Move() メソッドは呼び出されず、REST サービスから 404 エラーが返されます。これは、リソースが見つからないからです。
この簡単な例は、通常のパラメータ構文と正規表現との違いを示すことだけを目的としています。この例の場合、正規表現は必要ありません。Move() メソッドがパラメータの値を確認して適切に応答できるからです (またその必要があるからです)。ただし、以下の場合は、正規表現が役立ちます。
-
パラメータがオプションの場合。この場合、:parameter-name 構文の代わりに、正規表現 ([^/]*) を使用します。次に、例を示します。
<Route Url="/Test3/([^/]*)" Method="GET" Call="Test"/>
もちろん、ここで呼び出されるメソッドもパラメータに NULL 値が含まれる場合はこれを処理できる必要があります。
-
パラメータが最後のパラメータで、値にスラッシュが含まれる場合。この場合、そのパラメータが必須であれば、:parameter-name 構文の代わりに正規表現 ((?s).+) を使用します。次に、例を示します。
<Route Url="/Test4/((?s).+)" Method="GET" Call="Test"/>
または、そのパラメータがオプションであれば、:parameter-name 構文の代わりに正規表現 ((?s).*) を使用します。次に、例を示します。
<Route Url="/Test5/((?s).*)" Method="GET" Call="Test"/>
<Map> 要素を使用した UrlMap
InterSystems IRIS は、受信した URL を URL マップの各 <Map> 要素の接頭語と比較します。そして、最初に一致した <Map> 要素で指定されている REST サービス・クラスに、受信した REST 呼び出しを転送します。そのクラスは URL の残りの部分を処理し、サービスを実装するメソッドを通常呼び出します。<Map> 要素には以下の 2 つの属性があります。
3 つの <Map> 要素を含む以下の UrlMap を考えてみましょう。
XData UrlMap
{
<Routes>
<Map Prefix="/coffee/sales" Forward="MyLib.coffee.SalesREST"/>
<Map Prefix="/coffee/repairs" Forward="MyLib.coffee.RepairsREST"/>
<Map Prefix="/coffee" Forward="MyLib.coffee.MiscREST"/>
</Routes>
}
この UrlMap は、3 つの REST サービス・クラス (MyLib.coffee.SalesREST、MyLib.coffee.RepairsREST、または MyLib.coffee.MiscREST) のいずれかに REST 呼び出しを転送します。
これらの REST サービスの 1 つを呼び出す完全な REST URL は、以下の部分で構成されます。
-
InterSystems IRIS サーバのサーバ名とポート (例えば、http://localhost:52773/)。
-
[Web アプリケーション] ページ ([システム管理]→[セキュリティ]→[アプリケーション]→[Web アプリケーション] の順にクリック) で定義された Web アプリケーションの名前。例えば、これらの REST 呼び出しの Web アプリケーションの名前としては /coffeeRESTSvr などが考えられます。
-
<Map> 要素の Prefix。
-
REST URL の残りの部分。これは、転送された REST 要求を受け取る REST サービス・クラスによって処理される URL です。
例えば、以下の REST 呼び出しがあるとします。
http://localhost:52773/coffeeRESTSvr/coffee/sales/reports/id/875
これは、/coffee/sales という接頭語を持つ 1 つ目の <Map> と一致し、REST 呼び出しを MyLib.coffee.SalesREST クラスに転送します。そのクラスは URL の残りの部分 "/reports/id/875" で一致するものを探します。
別の例として、以下の REST 呼び出しがあるとします。
http://localhost:52773/coffeeRESTSvr/coffee/inventory/machinetype/drip
これは、/coffee という接頭語を持つ 3 つ目の <Map> と一致し、REST 呼び出しを MyLib.coffee.MiscREST クラスに転送します。そのクラスは URL の残りの部分 "/inventory/machinetype/drip" で一致するものを探します。
Note:
この UrlMap の例で、prefix="/coffee" の <Map> が 1 つ目のマップであった場合、/coffee を含む REST 呼び出しはすべて、それ以降の <Map> 要素のいずれかと一致した場合でも、MyLib.coffee.MiscREST クラスに転送されます。<Routes> 内の <Map> 要素の順序は重要です。
REST サービスのローカライズ
REST サービスから返される文字列はすべてローカライズできるため、サーバはさまざまな言語で複数バージョンの文字列を格納できます。HTTP Accept-Language ヘッダが含まれる HTTP 要求をサービスが受け取ると、サービスは適切なバージョンの文字列で応答します。
REST サービスをローカライズするには、以下の操作を行います。
-
実装コード内で、ハードコードされたリテラル文字列を含めるのではなく、$$$Text マクロのインスタンスを使用し、マクロ引数の値を以下のように指定します。
例えば、以下を指定するのではなく、
set returnvalue="Hello world"
以下を指定します。
set returnvalue=$$$TEXT("Hello world","sampledomain","en-us")
-
$$$Text マクロに対するドメイン引数を省略する場合は、REST サービス・クラス内に DOMAIN クラス・パラメータも指定します。次に、例を示します。
Parameter DOMAIN = "sampledomain"
-
コードをコンパイルします。このとき、コンパイラによって、$$$Text マクロの一意のインスタンスごとに、メッセージ・ディクショナリにエントリが生成されます。
メッセージ・ディクショナリはグローバルであるため、(例えば) 管理ポータルで簡単に確認できます。一般的なタスクに役立つクラス・メソッドがいくつかあります。
-
開発が完了したら、対象のドメインまたはすべてのドメインのメッセージ・ディクショナリをエクスポートします。
結果は、元の言語でのテキスト文字列を含む 1 つ以上の XML メッセージ・ファイルになります。
-
これらのファイルをトランスレータに送信し、変換されたバージョンを要求します。
-
変換された XML メッセージ・ファイルを受け取ったら、元のファイルのエクスポート元である同じネームスペースにインポートします。
メッセージ・ディクショナリに、変換されたテキストと元のテキストが共存します。
-
実行時に REST サービスは、HTTP Accept-Language ヘッダに基づいて、返すテキストを選択します。
詳細は、技術文書 "文字列のローカライズとメッセージ・ディクショナリ" を参照してください。
REST での Web セッションの使用
概要については、このドキュメントで前述した “REST での Web セッションの使用” を参照してください。
REST サービスで複数の REST 呼び出しにわたって 1 つの Web セッションを使用できるようにするには、REST サービス・クラスで UseSession パラメータを 1 に設定します。
Parameter UseSession As Integer = 1;
CORS のサポート
概要については、このドキュメントで前述した “CORS の概要” を参照してください。この付録の説明に従って Web サービスを手動で作成する場合、CORS のサポートの詳細は多少異なります。
CORS ヘッダの処理のオーバーライド
Important:
既定の CORS ヘッダの処理は、機密データを扱う REST サービスには適していません。
既定の CORS ヘッダの処理ではフィルタ処理は実行されず、CORS ヘッダが外部サーバに渡され、応答が返されるだけです。ドメインの許可リスト内の起源へのアクセスを制限するか、許可される要求メソッドを制限することをお勧めします。そのためには、REST サービス・クラスの OnHandleCorsRequest() メソッドをオーバーライドします。
OnHandleCorsRequest() メソッドの実装の詳細は、このドキュメントで前述した “OnHandleCorsRequest() の定義” を参照してください。
UrlMap 内の <Route> 要素と一致する URL 要求はすべて、クラス内で定義された 1 つの OnHandleCorsRequest() メソッドで処理されます。異なる REST URL 要求に対して OnHandleCorsRequest() メソッドの異なる実装が必要な場合は、Forward を使用して他の REST サービス・クラスに要求を送信する必要があります。
バリエーション : クエリ・パラメータへのアクセス
パラメータを REST サービスに渡す際には、サービスを呼び出すために使用される URL パスの一部として渡すことをお勧めします (例 : /myapi/someresource/parametervalue)。ただし、パラメータをクエリ・パラメータとして渡す方が便利な場合もあります (例 : /myapi/someresource?parameter=value)。そのような場合は、%request 変数を使用してパラメータ値を取得することができます。REST サービス内で、%request 変数は、URL クエリ全体を保持する %CSP.RequestOpens in a new tab のインスタンスです。指定されたクエリ・パラメータの値を取得するには、以下の構文を使用します。
$GET(%request.Data(name,1),default)
name は、クエリ・パラメータの名前です。default は、返す既定値です。また、同じ URL が同じクエリ・パラメータの複数のコピーを保持する場合は、以下の構文を使用します。
$GET(%request.Data(name,index),default)
index は、取得するコピーの数値インデックスです。詳細は、%CSP.RESTOpens in a new tab のクラスリファレンスを参照してください。
例 : Hello World!
以下のコードは、きわめて簡単な REST サービスのサンプルです。helloWorld.disp、helloWorld.impl、helloWorld.hwObj の 3 つのクラスがあります。helloWorld.disp および helloWorld.impl は %CSP.RESTOpens in a new tab を拡張して REST サービスを確立しますが、helloWorld.hwobj は %PersistentOpens in a new tab と %JSON.AdaptorOpens in a new tab の両方を拡張します。POST メソッドを使用してオブジェクトを作成するときに、この動作が便利です。
Class helloWorld.disp Extends %CSP.REST
{
Parameter HandleCorsRequest = 0;
XData UrlMap [ XMLNamespace = "https://www.intersystems.com/urlmap" ]
{
<Routes>
<Route Url="/hello" Method="GET" Call="Hello" />
<Route Url="/hello" Method="POST" Call="PostHello" />
</Routes>
}
ClassMethod Hello() As %Status
{
Try {
Do ##class(%REST.Impl).%SetContentType("application/json")
If '##class(%REST.Impl).%CheckAccepts("application/json") Do ##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit
Set response=##class(helloWorld.impl).Hello()
Do ##class(%REST.Impl).%WriteResponse(response)
} Catch (ex) {
Do ##class(%REST.Impl).%SetStatusCode("400")
return {"errormessage": "Client error"}
}
Quit $$$OK
}
ClassMethod PostHello() As %Status
{
Try {
Do ##class(%REST.Impl).%SetContentType("application/json")
If '##class(%REST.Impl).%CheckAccepts("application/json") Do ##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit
Set response=##class(helloWorld.impl).PostHello()
Do ##class(%REST.Impl).%WriteResponse(response)
} Catch (ex) {
Do ##class(%REST.Impl).%SetStatusCode("400")
return {"errormessage": "Client error"}
}
Quit $$$OK
}
上記のディスパッチ・クラスの構造に注意してください。さまざまなメソッドでさまざまなエンドポイントに URLMap が定義されていて、これらのルートを実装クラスにディスパッチするクラス・メソッドがあります。その過程で、このクラスによってエラー処理が実行されます。エラーの報告およびステータス・コードの設定の詳細は、%REST.ImplOpens in a new tab のドキュメントを参照してください。POST メソッドまたは PUT メソッドのエンドポイントであるクラス・メソッドは、特殊な %requestOpens in a new tab パラメータを使用して、そのパラメータから実装メソッドに情報を渡す必要があります。
Class helloWorld.impl Extends %CSP.REST
{
ClassMethod Hello() As %DynamicObject
{
Try {
return {"Hello":"World"}.%ToJSON()
} Catch (ex) {
Do ##class(%REST.Impl).%SetStatusCode("500")
return {"errormessage": "Server error"}
}
}
ClassMethod PostHello(body As %DynamicObject) As %DynamicObject
{
Try {
set temp = ##class(helloWorld.hwobj).%New()
Do temp.%JSONImport(body)
Do temp.%Save()
Do temp.%JSONExportToString(.ret)
return ret
} Catch (ex) {
Do ##class(%REST.Impl).%SetStatusCode("500")
return {"errormessage": "Server error"}
}
}
}
上記のサンプルの実装クラスには 2 つのメソッドがあります。1 つは、JSON 形式の "Hello World" メッセージを返すだけのメソッドです。もう 1 つは、入力内容に基づいてオブジェクトを作成し、そのオブジェクトのコンテンツを返すメソッドです。%New() と %JSONImport() のようなメソッドの使用法の詳細は、それぞれ %PersistentOpens in a new tab および %JSON.AdapterOpens in a new tab の各ドキュメントを参照してください。
helloWorld.hwobj クラスには多数のプロパティを置くことができますが、簡略化のために、この例では 1 つのみとしています。
Class helloWorld.hwobj Extends (%Persistent, %JSON.Adaptor)
{
Property Hello As %String;
Storage Default
{
<Data name="hwobjDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>Hello</Value>
</Value>
</Data>
<DataLocation>^helloWorld.hwobjD</DataLocation>
<DefaultData>hwobjDefaultData</DefaultData>
<IdLocation>^helloWorld.hwobjD</IdLocation>
<IndexLocation>^helloWorld.hwobjI</IndexLocation>
<StreamLocation>^helloWorld.hwobjS</StreamLocation>
<Type>%Storage.Persistent</Type>
}
}
"手動による REST サービスの作成の基本Opens in a new tab" に記載されている手順に従って管理ポータルで Web アプリケーションを構成した後、Postman などの REST クライアントを使用してその Web アプリケーションに要求を送信し、応答を確認します。