Skip to content

サーバースクリプトの内部構造 ​

第2版作成 最終更新 (日本時間)
確認バージョン1.5.8.1

サーバースクリプトの保存場所、実行されるまでの流れ、JavaScript から見えるオブジェクトを、1.5.8.1 のソースコードをもとにまとめます。

  • スクリプトはサイト設定(Sites テーブルの SiteSettings 列の JSON)の ServerScripts に保存されます。
  • 実行時は、拡張サーバースクリプトとサイトのスクリプトをまとめ、同じ条件のスクリプトを 1 本の文字列に結合して V8 エンジン(ClearScript)で 1 回だけ実行します。
  • フォルダ(ReferenceType が Sites のサイト)で動くのは「サイト設定の読み込み時」(WhenloadingSiteSettings)だけです。

全体像 ​

図を読み込み中…

保存場所 ​

サイトのサーバースクリプトは ServerScript クラスのリストとして、サイト設定の ServerScripts に入ります。サイト設定の画面にある「すべて無効化」は ServerScriptsAllDisabled です(SiteSettings.cs#L218-L220)。

ServerScript クラスが持つ項目は次のとおりです(ServerScript.cs#L7-L35)。

項目内容
Id・Title・Name識別子・タイトル・名前。Name は //Include: での参照に使う
Bodyスクリプト本文
WhenloadingSiteSettings 〜 BeforeOpeningRow実行条件(15 種類)。条件と context.Condition の値の対応は context.Condition を参照
Shared共有スクリプト。ほかのスクリプトの前に連結される(後述)
Functionalize本文を即時実行関数 (()=>{ ... })(); で囲む
TryCatch本文を try { ... } catch (e) { logs.LogException(...) } で囲む
Disabled無効化
TimeOutタイムアウト(ミリ秒)。適用のされ方は タイムアウト を参照
Backgroundバックグラウンド実行の指定

バックグラウンドサーバースクリプトは ServerScript を継承した BackgroundServerScript クラスで、実行ユーザー(UserId)とスケジュールを追加で持ちます。保存先はサイト設定ではなくテナント設定(TenantSettings.BackgroundServerScripts)です(BackgroundServerScript.cs)。

スクリプトの集め方(GetServerScripts) ​

実行するスクリプトの一覧は SiteSettings.GetServerScripts が作ります(SiteSettings.cs#L6204-L6269)。

  1. 拡張サーバースクリプト(Parameters.ExtendedServerScripts)のうち、ユーザー・サイト・コントローラ・アクションなどの条件に合うものを ServerScript に変換して先に並べる(絞り込みの詳細は 拡張サーバースクリプトの適用条件)。
  2. その後ろに、サイトのスクリプトのうち Disabled でないものを並べる。
  3. 実行条件が 1 つでも付いているスクリプトについて、本文が //debug// で始まるかを調べてデバッグ指定にする(ServerScript.cs#L181-L184)。
  4. 各スクリプトの本文にある //Include:名前 の行を、その名前のスクリプトの本文に置き換える。

//Include: による取り込み ​

行頭が //Include: の行は、コロンの後ろに書いた名前(前後の空白は除く)と Name が一致するスクリプトの本文に置き換わります。一致するものが複数あれば改行でつないで全部入ります。取り込んだ本文の中の //Include: も再帰的に展開され、その深さの上限は Script.json の ServerScriptIncludeDepthLimit(既定 10)です(SiteSettings.cs#L6271-L6311)。

js
//Include:common-functions
// ↑ この行が Name = "common-functions" のスクリプトの本文に置き換わる
model.ClassA = formatCode(model.ClassA);

一覧には拡張サーバースクリプトも含まれるので、拡張サーバースクリプトの Name を指定して取り込むこともできます。

一覧はリクエスト内でキャッシュされる ​

作った一覧は SiteSettings の ServerScriptsAndExtended([NonSerialized])に保持され、2 回目以降はそのまま返されます(SiteSettings.cs#L158-L159)。キャッシュは SiteSettings のインスタンス単位なので、リクエストごとにサイト設定を読み直せば作り直されます。同じリクエストの中では、最初に作ったときのコントローラ・アクションで絞り込んだ結果が使われます。

実行の流れ ​

各タイミングの呼び出し元(SetByBeforeCreateServerScript など)は、条件を指定して ServerScriptUtilities.Execute を呼びます(ServerScriptUtilities.cs#L1324-L1372)。

図を読み込み中…

実行しない条件 ​

次のいずれかに当てはまると、スクリプトは実行されません(ServerScriptUtilities.cs#L1178-L1192)。

  • サイト設定で「すべて無効化」(ServerScriptsAllDisabled)にしている
  • Script.json の ServerScript が false、契約設定でサーバースクリプトが無効、またはリクエストでサーバースクリプトが無効化されている
  • サーバースクリプトの入れ子(items.Update などから別のサーバースクリプトが動く)の深さが 10 以上
  • 条件に合う(共有でない)スクリプトが 1 つもない

結合のしかた ​

実行するコードは次の順に改行でつないだ 1 本の文字列です(ServerScriptUtilities.cs#L1254-L1266)。

  1. 共有スクリプト(Shared のもの)の本文を全部
  2. 条件に合うスクリプトの本文を、一覧の順(拡張サーバースクリプト → サイトのスクリプト)に

Functionalize と TryCatch の囲みは 2. の各スクリプトに個別に付きます(ServerScriptUtilities.cs#L1285-L1303)。共有スクリプトには付きません。TryCatch のログには、スクリプトの Id・Title・Name を _ でつないだ文字列とスタックトレースが出ます。

同じ条件のスクリプトは同じスコープで動く

結合して 1 回で実行するため、Functionalize を付けていないスクリプトの let・const はほかのスクリプトと同じトップレベルに宣言されます。別々のスクリプトで同じ名前を let で宣言すると、後のスクリプトで構文エラーになり、その条件のスクリプトが全部動きません。スクリプトごとに Functionalize を付けるか、名前が重ならないようにしてください。共有スクリプトは関数化されないので、共有したい関数や変数はそのまま見えます。

デバッグ ​

条件に合うスクリプトのどれか 1 つでも本文が //debug// で始まっていれば、そのときの実行全体がデバッグモードになります。デバッグモードではエンジンが EnableDebugging と EnableRemoteDebugging を付けて作られ、実行開始時にデバッガーの接続を待って一時停止します(ScriptEngine.cs#L20-L54)。デバッグモードの間はタイムアウトが効きません(ServerScriptModel.cs#L185-L190)。

本番環境で //debug// を残さない

デバッガーが接続するまで処理が止まり、タイムアウトもかかりません。検証が終わったら先頭の //debug// を消してください。

JavaScript から見えるオブジェクト ​

エンジンに登録されるものは次のとおりです(ServerScriptUtilities.cs#L1221-L1253)。これ以外の .NET の型や OS の機能には触れません(理由は C# スクリプト・Python は使えるか)。

名前C# のクラス用途
JsonConvertNewtonsoft.Json.JsonConvert(型).NET 側の JSON 変換
contextServerScriptModelContextユーザー・サイト・リクエストの情報、Log・Error・AddResponse など
gridServerScriptModelGrid一覧の件数など
modelExpandoObject対象レコードの値(読み書き)
savedExpandoObject保存済みの値
depts・groups・usersServerScriptModelDepts など組織・グループ・ユーザーの取得
columnsExpandoObject項目ごとの表示・入力の制御(columns で変更できるプロパティ)
siteSettingsServerScriptModelSiteSettings既定のビュー・セクションなど
viewServerScriptModelViewフィルター・ソート(view.OnSelectingWhere とフィルター)
itemsServerScriptModelApiItemsレコード・サイトの取得・作成・更新・削除、集計
hidden・responses・elementsServerScriptModelHidden など隠し項目、レスポンス、画面要素の表示制御
extendedSqlServerScriptModelExtendedSql拡張 SQL の実行
notificationsServerScriptModelNotification通知
httpClientServerScriptModelHttpClientHTTP 通信。Script.json の DisableServerScriptHttpClient が true なら登録されない
utilities・logsServerScriptModelUtilities・ServerScriptModelLogsユーティリティ、ログ出力
_file_cs($ps.file)ServerScriptFileファイル操作。DisableServerScriptFile が false のときだけ登録される(既定は true で使えない)
_csv_cs($ps.CSV)ServerScriptCsvCSV の変換

$ps はエンジンの初期化時に JavaScript で定義されるオブジェクトで、$ps.file・$ps.CSV・$ps.JSON などは _file_cs・_csv_cs・JsonConvert を包んだ JavaScript の関数です(ServerScriptJsLibraries.cs)。$ps.file の制限は $ps.file と添付ファイル を参照してください。

model に入る値 ​

model と saved には、どのテーブルでも次の値が入ります(ServerScriptUtilities.cs#L191-L430)。

  • ReadOnly・SiteId・Title・Body・Ver・Creator・Updator・CreatedTime・UpdatedTime・Comments
  • 分類・数値・日付・説明・チェック・添付ファイルの各項目(ClassA など)

期限付きテーブルでは IssueId・StartTime・CompletionTime・WorkValue・ProgressRate・RemainingWorkValue・Status・Manager・Owner・Locked、記録テーブルでは ResultId・Status・Manager・Owner・Locked が加わります。

変更の反映 ​

model の値を書き換えると、その項目名が記録されます。実行後、記録された項目のうちそのユーザーが編集できる項目だけがレコードに反映されます(ServerScriptUtilities.cs#L1075-L1091、L680-L699)。

model には項目のほかに ExtendedRowCss と ExtendedRowData を書けます。行表示の前(BeforeOpeningRow)に設定すると、一覧の行(tr)の CSS クラスと data-extension 属性になります(ServerScriptUtilities.cs#L724-L737、HtmlGrids.cs#L369-L379)。

js
// 行表示の前: 日付A が過ぎている行に CSS クラスを付ける
if (model.DateA < new Date()) {
    model.ExtendedRowCss = 'row-overdue';
}

呼び出し元とコード自動生成 ​

期限付きテーブル・記録テーブルの作成・更新・削除などでサーバースクリプトを呼ぶコードは、CodeDefiner のテンプレート(App_Data/Definitions/Definition_Code/Model_OnCreating_ServerScript_Body.txt など)から IssueModel.cs・ResultModel.cs に生成されています(Definition_Code)。たとえば作成前のテンプレートは、SetByBeforeCreateServerScript を呼び、context.ErrorData にエラーが入っていればそこで作成を止めます。サーバースクリプトの context.Error は context.ErrorData にエラーを設定するメソッドなので(ServerScriptModelContext.cs#L152-L156)、作成前に呼ぶと作成を中止できます。CodeDefiner については CodeDefiner を参照してください。

フォルダ(Sites)で動く条件 ​

ReferenceType が Sites のサイト(フォルダ)では、サーバースクリプトはサイト設定の読み込み時(WhenloadingSiteSettings)だけ動きます。作成・更新・削除・画面表示の前などの条件は動きません。理由は 3 つあります。

箇所1.5.8.1 の実装
管理画面サーバースクリプトのダイアログで、Sites のときは実行条件のチェックボックス群に hidden が付き、条件を選べない(SiteUtilities.cs#L17937-L17947)。スタイル・スクリプト・HTML のダイアログも同じ
APIサイト設定の API でサーバースクリプトを登録できるのは ServerScriptRefTypes(Results・Issues)だけ(ApiSiteSetting.cs#L13-L17、SiteUtilities.cs#L2442)
モデルSiteModel の作成・更新・削除は SetByBeforeCreateServerScript などを呼ばない(SiteModel.cs に呼び出しがない)

WhenloadingSiteSettings は、サイト設定を読み込むとき(ItemModel.SetSite)にレコードなしで実行されます。そのため model にレコードの値は入りません。また、対象がそのサイト自身(context.Id がサイト ID と同じ)で、アクションが次のいずれかのときは実行されません(_BaseModel.cs#L758-L787)。

createbytemplate・edit・update・copy・delete・updatesitesettings・updatesmartdesign

つまり、サイト設定の編集画面を開いたり保存したりするときには動かず、一覧やレコードの画面を開くときなどに動きます。

サイトの操作は items から

フォルダ自体にスクリプトを付けられなくても、テーブルのサーバースクリプトから items でサイトを扱えます。items.GetSite(id)・GetSiteByTitle・GetSiteByName・GetSiteByGroupName・GetClosestSite で取得し、items.NewSite(referenceType) で新しいサイトのモデルを作れます(ServerScriptModelApiItems.cs#L33-L114)。取得したサイトのモデルでは SiteId・SiteName・SiteGroupName・ReferenceType・ParentId・InheritPermission・Publish・DisableCrossSearch・各ビューのガイド(GridGuide など)・SiteSettings などを読め、SiteId 以外は書き込めます(ServerScriptModelApiModel.cs#L107-L178)。

js
// サイト名でサイトを探し、サイト名を書き換える
const sites = items.GetSiteByName('master-table');
if (sites.length > 0) {
    context.Log('ParentId: ' + sites[0].ParentId);
    sites[0].SiteName = 'master-table-old';
    sites[0].Update();
}

フォルダでも作成・更新・削除のサーバースクリプトを動かせるようにする改修案は、フォルダでサーバースクリプトを動かす にまとめています。

計算式のスクリプトとの違い ​

拡張計算式も同じ ScriptEngine(V8)で評価しますが、別の実装(FormulaServerScriptUtilities)です。登録されるのは model・context と FormulaServerScriptUtilities 型だけで、items や httpClient などは使えません。詳しくは 拡張計算式の実装ロジック を参照してください。

関連ページ ​

変更履歴

第2版操作別の実行順と拡張SQLの併用・同期実行の注意点を追加
第1版サーバースクリプトの仕組み・項目の変更可否・拡張サーバースクリプトの解説と、関連する改修・設計メモを追加