Skip to content

API リクエストにクエリパラメータで独自データを渡す ​

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

サーバースクリプトの context.QueryStrings.Data() は API リクエストでも使えます。API の URL にクエリパラメータを付ければ、テーブルに専用のフラグ項目を作らなくても、サーバースクリプトの処理を呼び出し側から切り替えられます。ただし使えるのは、コマンドボタンやスクリプトなどブラウザのセッションで呼び出す場合だけです。リクエストボディで ApiKey を指定して呼び出す場合は使えません。

使える条件 ​

呼び出し方認証context.QueryStrings
コマンドボタン・スクリプトなど、プリザンターの画面から呼び出すブラウザのセッション(Cookie 認証)使える
リクエストボディで ApiKey を指定し、認証ユーザを上書きして呼び出すセッションなし使えない(Data() は常に空文字列)

WARNING

リクエストボディで ApiKey を指定して認証ユーザを上書きした場合、context.QueryStrings.Data() は常に空文字列を返します。コマンドボタンやスクリプトからの呼び出しでは ApiKey を指定せずブラウザのセッションをそのまま使うため、この制約には当たりません。

正確には、分かれ目は ApiKey の有無ではなく、リクエストにログイン済みの Cookie が付いているか(User.Identity.IsAuthenticated)です。確認したソースでも API コントローラは同じ判定をしています(ItemsController.cs、Context.cs)。

仕組み ​

QueryStrings クラス ​

context.QueryStrings の実体は QueryStrings クラスで、Dictionary<string, string> を継承したシンプルな実装です。

csharp
public class QueryStrings : Dictionary<string, string>
{
    public string Data(string key)
    {
        return this
            .Where(o => o.Key.ToLower() == key.ToLower())
            .Select(o => o.Value)
            .FirstOrDefault()
                ?? string.Empty;
    }

    public bool Bool(string key)
    {
        return Data(key).ToBool();
    }

    public int Int(string key)
    {
        return Data(key).ToInt();
    }

    public long Long(string key)
    {
        return Data(key).ToLong();
    }
}

QueryStrings.cs

メソッド戻り値
Data(key)値の文字列。キーの大文字・小文字は区別しない。キーがなければ空文字列
Bool(key)Data(key) を bool に変換した値
Int(key)Data(key) を int に変換した値
Long(key)Data(key) を long に変換した値

値が入るタイミング ​

QueryStrings に値を入れるのは Context クラスの SetData() です。HTTP リクエストのクエリ文字列を & と = で分解し、URL デコードしてから格納します。そのため日本語やスペースを含む値も扱えます。

csharp
private void SetData()
{
    // ...
    var request = AspNetCoreHttpContext.Current.Request;
    foreach (var o in request.QueryString.Value?.PadLeft(1, '?').Substring(1).Split('&'))
    {
        var keyAndValue = o.Split('=');
        var key = HttpUtility.UrlDecode(keyAndValue.FirstOrDefault());
        var value = HttpUtility.UrlDecode(keyAndValue.Skip(1).FirstOrDefault());
        QueryStrings[key] = value;
    }
    // ...
}

Context.cs#L551-L558

サーバースクリプトの context には、この Context.QueryStrings への参照がそのまま渡されます。

csharp
public readonly QueryStrings QueryStrings;

public ServerScriptModelContext(Context context, ...)
{
    QueryStrings = context.QueryStrings;
    // ...
}

ServerScriptModelContext.cs#L21

API では認証方式で決まる ​

API コントローラは、sessionData に User?.Identity?.IsAuthenticated の結果を渡して Context を作ります。SetData() は sessionData が true のときに呼ばれるため、QueryStrings が使えるかどうかは認証方式で決まります。

csharp
[HttpPost("{id}/Update")]
public ContentResult Update(long id)
{
    var body = default(string);
    using (var reader = new StreamReader(Request.Body)) body = reader.ReadToEnd();
    var context = new Context(
        sessionStatus: User?.Identity?.IsAuthenticated == true,
        sessionData: User?.Identity?.IsAuthenticated == true,
        apiRequestBody: body,
        contentType: Request.ContentType,
        api: true);
    // ...
}

ItemsController.cs#L60-L70

図を読み込み中…

コマンドボタンやスクリプトからの呼び出しではブラウザのセッション Cookie が送られるので IsAuthenticated が true になり、クエリパラメータが QueryStrings に入ります。

使い方 ​

パスの前提

以降の /api/items/... はルート配置の場合のパスです。サブディレクトリ配置の環境ではパスが変わります。スクリプトから URL を組み立てるときは $('#ApplicationPath').val() や context.ApplicationPath を使ってください。

基本 ​

更新 API の URL にクエリパラメータを付け、サーバースクリプトで取得します。

text
POST /api/items/{レコードID}/update?mode=export
js
// サーバースクリプト(更新後)
var mode = context.QueryStrings.Data('mode');
if (mode === 'export') {
    // エクスポート処理
}

複数のパラメータを渡す ​

text
POST /api/items/{レコードID}/update?action=notify&target=slack&channel=general
js
// サーバースクリプト(更新後)
var action = context.QueryStrings.Data('action');
var target = context.QueryStrings.Data('target');
var channel = context.QueryStrings.Data('channel');

if (action === 'notify' && target === 'slack') {
    // Slack通知処理
}

コマンドボタンから処理を振り分ける ​

処理の種類をクエリパラメータで指定し、1 つのサーバースクリプトで複数の処理を振り分ける例です。switch で分岐できるので、サーバースクリプトの数が増えすぎるのを防げます。

js
// コマンドボタンのスクリプト
$.ajax({
    url: $('#ApplicationPath').val() + 'api/items/' + $p.id() + '/update?action=approve',
    method: 'POST',
    contentType: 'application/json',
    data: JSON.stringify({ ApiVersion: 1.1 }),
    success: function (data) {
        // API のレスポンスは { Id, StatusCode, Message } 形式
        console.log(data.StatusCode, data.Message);
        location.reload();
    }
});
js
// サーバースクリプト(更新後)
var action = context.QueryStrings.Data('action');
switch (action) {
    case 'approve':
        // 承認処理
        model.ClassA = '承認済';
        break;
    case 'reject':
        // 却下処理
        model.ClassA = '却下';
        break;
}

API の呼び出しでは AddResponse は画面に反映されない

context.AddResponse() で積んだ指示(ResponseCollection)が画面に反映されるのは、HtmlScripts.OnEditorLoad で HTML ページに埋め込まれる経路だけです。API(api/items/...)のレスポンスは ApiResponse(Id / StatusCode / Message など)しか返さないため、API の呼び出しで AddResponse を使っても画面には反映されません。処理結果を画面に出したいときは、呼び出し元のスクリプト側で表示を制御してください(AddResponse については context.AddResponse の Method と引数)。

型変換メソッドを使う ​

数値やフラグを渡すときは、型変換メソッドを使うとコードが簡潔になります。

text
POST /api/items/{レコードID}/update?copies=3&dryRun=true
js
// サーバースクリプト(更新後)
var copies = context.QueryStrings.Int('copies');   // 3
var dryRun = context.QueryStrings.Bool('dryRun');  // true

if (!dryRun) {
    for (var i = 0; i < copies; i++) {
        // レコードの複製処理
    }
}

注意点 ​

  • キーの大文字・小文字は区別されません。 Data() は内部で ToLower() で比較するため、action・Action・ACTION は同じキーとして扱われます。
  • キーだけを指定した場合(?flag)、値は null になります。Data() は null のとき空文字列を返すので、存在チェックには Bool() か空文字列との比較を使います。
js
// ?flag だけの場合
context.QueryStrings.Data('flag');  // ""(空文字列)
context.QueryStrings.Bool('flag');  // false
// ?flag=true の場合
context.QueryStrings.Data('flag');  // "true"
context.QueryStrings.Bool('flag');  // true

関連ページ ​

変更履歴

第4版記事の確認版を繰り返す表現を整理する
第3版「拡張機能」「API」を 1.5.8.1 のソースで検証して修正
第2版記事のファイル名に並び順の番号を付け、元記事リンクを frontmatter の sources に移行
第1版API にクエリパラメータで独自データを渡す方法を追加し、ファイルアップロードの流れを図で説明