vte.cx(ブイテックス)はReactなどのJavaScriptフレームワークを利用してWebサービスを作成することができるバックエンドサービス(BaaS)です。
vte.cxのアプリケーションは基本的にREST APIを利⽤するSPA(Single Page Application)になります。APIを介すため、サーバとクライアントは基本的に疎結合となり、可搬性の高いアプリケーションを作成できます。
このドキュメントは主にvte.cxの仕様について説明したものです。サービスの作成やデプロイ方法など実践的な内容については
ファンダメンタルズやQiitaの記事を参照してください。
サービスとはユーザが作成するWebアプリケーション単位に割り当てられる領域(ドメイン)のことで、サービス一つにつきvte.cxのサブドメインが一つ割り当てられます。いわゆる、マルチテナントになっており、サービスは完全に独立して動作するため他のサービスと干渉することはありません。
サービスは、管理画面より作成してください。サービス作成に成功すると以下のようにサービス名のサブドメインが割り当てられます。
https://{サービス名}.vte.cx
サービス名は、対象のサービスにログインしてGET /d/?_serviceを実行することでも取得できます。
サービスには以下のような状態(ステータス)があります。
作成されたサービスはひとまず「サービス非公開(開発中):staging」となります。
この状態のサービスは、JavaScriptのローカル開発環境で使うことを想定しており、http通信しかできません。また無料で使えます。
管理画面から「サービス公開中:production」に変更すると、今度はhttps通信しかできなくなります。この状態になると課金されるようになります。
creating : サービス新規作成中 staging : サービス非公開(開発中) production : サービス公開中 blocked : システム管理者による強制停止 resetting : リセット中 deleting : 削除中 deleted : 削除済み failure : 登録失敗
vte.cxにはリソースを階層で管理するドキュメント型データベースがあります。
リソースは/dから始まるエンドポイントURLにマッピングされ、REST APIによりエンドポイントURLで指定されたリソースに対して読み書きできます。 例えば、GET /d/fooを実行するとfooというリソースを取得できます。リソースはHTMLやJavaScriptなどのテキスト情報の他、写真や動画などのBlobコンテンツなどを扱えます。
また、/d/fooをフォルダに見立てて、その配下にリソースを格納することもできます。例えば、/d/foo/barは、fooの配下にあるbarというリソースを意味します。GET /d/foo?fというように、fパラメータをつけることで、配下にあるリソースの一覧を取得することができます。エントリが存在しない場合、HTTPステータスコード204を返却します。
例えば、PUT /d/foo?_content
により、fooに対して、テキストデータやblobデータを格納することができます。PUTの際、ペイロード(リクエストのBody)にはデータをraw形式で入れてください。最大サイズは100MiBです。また、Content-Typeには、multipart/form-dataではなく、image/pngやtext/htmlなど実際に格納したコンテンツに合ったタイプを指定してください。
?_contentパラメータを付けない場合、メタ情報の登録更新になります。(メタ情報については「メタ情報とATOM表現」を参照してください。
また、
FormDataオブジェクトを使って画像やCSVファイルをアップロードすることができます。詳しくは、「ファイルアップロード」を参照してください。
PUTはCSRF対策のためXHR通信(XMLHttpRequest)からのみ受け付けるようにしています。つまり、リクエストヘッダに「X-Requested-With: XMLHttpRequest」が設定されていなければエラーを返却します。(ステータス417)
GET /d/foo?e
とeパラメータを付与することで、fooのメタ情報を取得できます。エントリが存在しない場合、HTTPステータスコード204を返却します。
以下はメタ情報をJSONで取得したものです。(XMLの属性であるhrefやrelには___href
のように___
が先頭に付くので注意してください。)
http://vtecxblank.vte.cx/d/foo?e
[ { "sample": "テスト内容", "author": [ { "uri": "urn:vte.cx:created:209" } ], "id": "/foo,1", "link": [ { "___href": "/foo", "___rel": "self" } ], "published": "2018-06-26T12:29:37.947+09:00", "updated": "2018-06-26T12:29:37.947+09:00" } ]
メタ情報はATOM(RFC4287)によって表現されます。ATOMは少々古いものではありますが、拡張性が高く、メタ情報の流通ためのフォーマットとして広く使われていることから、vte.cxでも採用しています。
vte.cxにおけるATOMの各項目は以下のような意味をもちます。
yyyy-MM-dd'T'hh:mm:ss.SSS+99:99
」(ISO8601拡張)形式で代入されるまた、以下のように、エントリの基本構造としてidとキーがある点に注意してください。
<id>
タグを持ちます。これは、システム全体で一意となるように、キーとリビジョンを組み合わせた形をしています。(上記エントリのidは、/foo,1
) また、リビジョンはエントリの更新回数です。
<id>
タグに値を自動的に登録します。<link>
タグのrel="self"属性のhref属性がエントリの参照を表すキーです。
上記のATOM項目以外の項目をエントリスキーマに記述することでユーザ独自の項目を定義することができます。上記の例では、sampleがユーザ定義項目になります。
詳細は、「エントリスキーマとユーザ定義項目」で説明します。
エントリにlink rel=alternate
タグを付与することで別のURLにマッピングすることが可能です。これを別名(alias)と呼んでいます。LinuxのシンボリックリンクやWindowsのショートカットフォルダのような機能です。
例えば、ECサイトのカテゴリ表示において、家電/パソコン/アンドロイドや、家電/スマートフォン/アンドロイドといったように、「アンドロイド」を異なるカテゴリに関連付けしたい場合があります。
これを実現するのが別名(alias)で、「アンドロイド」は一つのエントリであるにもかかわらず複数のカテゴリのパスから参照できるようになります。
これは、上位階層が変わったときでも、aliasの書き換えだけで下位のエントリは変更しないで済むという利点があります。
また、子要素が存在するフォルダにaliasを設定するとaliasのパスからもその子要素にアクセスできるようになります。
以下のエントリは実体が /foo/android で別名(alias)が /bar/android になります。
[ { "id": "/foo/android,1", "link": [ { "___href": "/foo/android", "___rel": "self" }, { "___href": "/bar/android", "___rel": "alternate" } ] } ]
以下のようにリクエストすることでaliasを追加できます。
aliasは1つのエントリにつき最大100個まで追加することができます。
PUT /d?_addalias
<feed> <entry> <link rel="self" href="{キー}" /> <link rel="alternate" href="{alias}" /> </entry> </feed>
また、PUT /d?_removealias
で指定したaliasを削除できます。
メタ情報は、JSONの他にもXMLやMessagePackなどの様々な形式に変換して取りだすことができます。例えば、GET /d/foo?e&x
とxパラメータを付与することでXMLとして取得でき、GET /d/foo?e&m
などmパラメータでMessagePackとして取得できます。どのような形式に変換されても項目の構造は同じ形になります。
パラメータの指定がない場合、デフォルトはJSON形式です。
ブラウザに表示する際は、JSONやMessagePackではなくXMLをおすすめします。なお、JSONについてはセキュリティー上の理由でブラウザに直接表示できないようにしています。(JSONPの禁止)
また、POSTやPUTのペイロード(リクエストのBody)はraw形式のみ許可しています。送信する際はXHR通信(XMLHttpRequest)である必要があります。フォームデータ(HTML Form)送信はCSRF対策のため受け付けられないようにしています。
リクエストパラメータの種類とContent-Typeは以下の通りです。
リクエストパラメータの予約語は1文字の英字、もしくは先頭
_
(アンダースコア)で始まる文字列になります。これ以外のもの、つまり2文字以上で_
で始まらない文字列についてはユーザのアプリケーションが自由に使えるパラメータです。
以下は、/foo配下をxmlで取得した例です。
http://vtecxblank.vte.cx/d/foo?f&x
<?xml version="1.0" encoding="UTF-8" ?> <feed> <entry> <sample>テスト内容</sample> <author> <uri>urn:vte.cx:created:209</uri> </author> <id>/foo/bar,1</id> <link href="/foo/bar" rel="self"/> <published>2018-06-26T12:29:37.947+09:00</published> <updated>2018-06-26T12:29:37.947+09:00</updated> </entry> </feed>
ATOM項目以外の項目をエントリスキーマに記述することでユーザ独自の項目を定義することができます。実際に業務アプリケーションを作成する場合にはこのユーザ定義項目をいかに定義していくかが鍵になります。
エントリスキーマにユーザ項目を定義するには、管理画面の「エントリスキーマ管理」で行うか、プロジェクトの/setup/_settings/template.xml
ファイルを直接編集し、項目名などを記述することでできます。(管理画面で編集を行った後は、必ずプロジェクトのtemplate.xmlファイルも更新するようにしてください)
システム運用中でもファイルの更新は可能であり、項目を追加して更新すると直ぐにシステムに反映されます。ただし、追加項目は常に要素の最後尾に追記する必要があります。途中の階層であっても同列の最後であれば項目を追加できます。
エントリスキーマには以下の記述ルールが適用されます。ルールを適用できるものは、項目の有無や親子関係、繰り返しといった構造の定義、数値や文字列、日付などの型の指定、必須チェックや最大/最小チェック、正規表現によるバリデーションチェックなどがあり、データ登録更新時にルールに合致しなければエラーとなります。
yyyy-MM-dd
yyyy-MM-dd HH
yyyy-MM-dd HH:mm
yyyy-MM-dd HH:mm:ss
yyyy-MM-dd HH:mm:ss.SSS
yyyyMMdd
yyyyMMddHH
yyyyMMddHHmm
yyyyMMddHHmmss
yyyyMMddHHmmssSSS
{降順ソートしたい項目名}_desc(desc)
と指定してください。
以下はエントリスキーマのサンプルです。/_settings/template
エントリの<content>
タグに以下を記述することでエントリスキーマを設定できます。
idx email verified_email(Boolean) // Boolean型 name given_name family_name error errors{2} // インデントでerrorの子要素。Mapで多重度は2 domain reason message locationType location code(int){1~100} // 1~100の範囲 message subInfo favorite $attribute // $で始まる項目はXMLの属性となる(項目の先頭に記述する) food!=^.{3}$ // !で必須項目を示す。もし{}があればそれよりも後に記述する。 food{}!=xxx など music=^.{5}$ favorite2 food food1 favorite3 food hobby{} $$text // $$textはXMLのテキストノードになる
上記エントリスキーマを利用するサンプルアプリケーションでは以下のようなリクエストになります。リクエストでは、スキーマに定義されている項目のうち実際に必要なものだけが使用され、また、リクエストの第一階層の項目に値が代入されていないものは出現しない項目となります。
例えば、このエンドポイントへのリクエストには、emailやfamily_name、subinfoなどの項目だけが存在する一方で、errorなどの項目はレスポンスだけに存在します。
このように、アプリケーションを設計する際、どのエンドポイントでどの項目を使うかについてグルーピングの定義が必要です。vte.cxでは管理画面の「エンドポイント管理」を利用してこれらを定義することができます。
POST /d/registration
[ { "email": "email1", "family_name": "管理者Y", "given_name": "X", "name": "管理者", "subInfo": { "favorite": { "food": "カレー", "music": "ポップス1" } }, "verified_email": false } ]
レスポンス
[
{
"error": {
"code": 400,
"errors": [
{
"domain": "vte.cx",
"location": "Authorization",
"locationType": "header",
"message": "invalid header",
"reason": "invalidAuthentication"
}
],
"message": "Syntax Error"
}
}
]
GET /d/foo?f
というように、fパラメータをつけることで、配下にあるエントリの一覧を取得することができます。取得可能なエントリの最大件数は?l={件数}
パラメータで指定できます。(デフォルトは100件)
?l=*
ですべてのエントリを取得できます。また、?cで配下のエントリ件数を取得できます。
検索対象のデータが大量にある場合、ページネーションのように、1回のGETで取得するより複数のページにわけて検索する方がユーザ体験がよくなることがあります。ページネーションでは、取得可能なエントリの最大件数を絞って複数回検索することになります。続きのデータが存在する場合、レスポンスヘッダのx-vtecx-nextpageに次のページのカーソル文字列が自動的に設定されますので、?pにそのカーソル文字列を指定することで次ページを検索することができます。
カーソル文字列が設定されるケースは、lパラメータで指定した件数を超えた場合とフェッチリミッター機能の上限に達した場合の2通りあります。
フェッチリミッター機能とは、サーバ負荷軽減のため検索条件検索(含む件数取得)や検索条件付きページネションにおいて一定の件数(50,000件)を検索(fetch)後に一旦終了するようにしている制御機能です。
検索件数上限に達すると、戻り値に検索したところまでのカーソル文字列を設定し、ステータス206(Partial Content)を返却します。
クライアントにおいて「p=カーソル文字列」の形でクエリ文字列に付加してリクエストすると、続きのページを取得できます。
なお、?l=*で検索した場合はフェッチリミッター機能は無視され、すべての検索が完了するまで実行されます。これを使用すると、データ件数が多い場合にサーバに大きな負荷がかかるため、通常は件数を指定するようにしてください。
?lパラメータで指定した件数だけ取得した後、さらに次に続くデータを検索したい場合にページ数を指定して検索できると便利です。これをページネーション機能といいます。
ページネーション機能では、
GET {Key}?{検索条件}&n={ページ数}
をリクエストすることで、指定されたページ数のデータを取得できます。また、l={件数}パラメータを指定することで、1ページあたりの件数を指定できます。
ただし、ページネーション機能を使うにはあらかじめカーソル一覧(pageindex)を作成しておく必要があります。以下のリクエストを実行することでカーソル一覧がセッションに保持されます。検索時、指定したページ数のカーソル一覧がセッションに存在しない場合は400エラーとなり、「Please make a pagination index in advance. 」のメッセージが返ります。また、検索した結果、最終ページ数に満たない場合は実際に張った最終ページ数が返ります。
大量のカーソル一覧(pageindex)を張るのは時間がかかるため、最初のリクエストは最終ページ数を50ぐらいにとどめておくのがおすすめです。(フェッチの最大実行回数である5万件を超える件数を検索した場合には206(Partial Content)が返ります)
まず50ページ分をカーソル一覧に登録し、一旦画面に表示させたうえで、さらに続きを表示させる必要があれば、次の50〜100ページを張るようにします。ただし、開始ページ数を指定する場合、セッションに「開始ページ数-1ページ」のカーソルが登録されていなければエラーになりますので、開始ページ数には必ず前回実行した最終ページを指定するようにしてください。
以下のように、URLパラメータに条件を指定することで絞り込み検索ができます。条件は「項目名=値」の形、もしくは、「項目名-記号-値」の形で指定します。項目名には、テンプレートの階層を"."でつないだ名称を指定します。
値はJavaScriptのencodeURIComponent()関数などで必ずエンコードしてください。値にもし&が含まれていると区切り文字と認識され、値が分割されて誤動作してしまいますのでご注意ください。具体的な条件検索の例についてはvtecxblankのチュートリアル(tutorial_2.html)に載せていますのでこちらもご覧ください。
文法
https://{サービス名}.vte.cx/{Key}?f&{name}{=|-eq-|-lt-|-le-|-gt-|-ge-|-ne-|-rg-|-fm-|-bm-|-ft-}{value}&{name}{=|-eq-|-lt-|-le-|-gt-|-ge-|-ne-|-rg-|-fm-|-bm-|-ft-}{value}&...&l={n}&p={カーソル文字列}&{ソート項目名}-desc|asc
例:好きな食べ物がeggで価格が5000以下を昇順で最大件数20件取得
https://{サービス名}.vte.cx/{Key}?f&subInfo.favorite.food-eq-egg&price-lt-5000&l=20&price-asc
記号の種類と意味は以下の通りです。
前方一致では、指定した文字の先頭文字が一致する条件となり、後方一致では、末尾文字が一致する条件になります。SQLで例えると、-fm-町
は、LIKE '町%'
に相当し、-bm-田
は、LIKE '%田'
に相当します。
あいまい検索を実行するには正規表現を使います。例えば、
-rg-.*田町.*
は、`LIKE '%田町%'
に相当します。
また、Keyに*
を付けることで前方一致検索が可能です。例えば、"GET /men/to*"を実行すると、"/men/tokyo"、"/men/tops" などのエントリを検索できます。
&|(と&)でOR条件を囲むことでOR検索ができます。括弧を省略する&|も使用できます。以下はOR条件の例です。
注意点としては、カッコの入れ子は無し。また、カッコの中でOR条件を指定するのは無しという点です。以下は例です。
また、&条件は全ての条件に適用されます。以下の例1、2は同じ条件です。
全文検索では複数項目を対象にすることができます。例えば、「nameまたはaddressが田中」かつ「nameまたはaddressが港区」という条件指定は以下のようにします。 ただし、複数項目を対象にする場合は、全文検索index指定(/_settings/templateのrightsタグ)において、|を使って複数の項目名を指定する必要があります。詳しくは、「全文検索Indexの設定」を参照してください。
name|address-ft-田中&name|address-ft-港区
項目が同じ場合、カンマを使って検索ワードのAND条件を1つにまとめる表現も指定可能です。以下は上記と同じ条件になります。
name|address-ft-田中,港区
上記条件で期待できる検索結果は、具体的には以下のパターンになります。
条件検索はメモリ内で実行しますが、データ量が多いとパフォーマンス悪化を招きます。そのような場合、Indexを設定することで、パフォーマンスを向上させることができます。
ただし、Index検索を適用できるのは検索条件の最初の項目(一番左の項目)のみとなります。2番目以降の項目についてはIndex検索で絞り込み後にメモリ内で検索を実行します。例えば、?foo=123&bar=456
という条件で検索した場合、fooにIndex設定があればIndex検索して絞り込みを行い、それからbarの検索をインメモリで実行します。
また、Index検索の条件はgt、ge、lt、le、完全一致、前方一致である必要があり、暗号化項目は指定できません。(暗号化項目については以下を参照)2つ目以降の検索条件についてはあいまい検索や暗号化項目の検索が可能です。
Indexは検索パフォーマンスを向上させる一方、書込パフォーマンスを悪化させるため、Index設定は必要最低限にすべきです。
Indexの設定はテンプレートで行います。テンプレートとは、(/_settings/template)エントリのことで、Index設定はtemplateエントリのrightsタグに記述します。(管理画面のエントリスキーマ管理でも設定できます)
以下のように、左辺の項目名に続けて:(コロン)の後にフォルダの正規表現を記述することでIndexを設定できます。また、右辺に登録するURIのうち正規表現にマッチするものをIndexとして登録します。URIにはエントリの実体(self)や別名(alias)などを指定します。
以下は、subInfo.favorite.food項目を/masterまたは/dataから検索した際にIndex検索となる設定例です。
{項目}:{フォルダの正規表現}
subInfo.favorite.food:/master|/data
通常の検索以外に全文検索を行うことができます。全文検索を行うには、下記の全文検索Indexを設定したうえで、検索条件の記号を{項目名}-ft-{値}のように指定してください。
全文検索Indexの設定はテンプレート(/_settings/template)のrightsタグに記述します。
以下のように、左辺の項目名に続けて;(セミコロン)の後にフォルダの正規表現を記述することで全文検索Indexを設定できます。また、右辺に登録するURIのうち正規表現にマッチするものを全文検索Indexとして登録します。URIにはエントリの実体(self)や別名(alias)などを指定します。
以下は、subInfo.favorite.food項目を/masterまたは/dataから検索した際に全文検索となる設定例です。
{項目};{フォルダの正規表現}
subInfo.favorite.food;/master|/data
複数項目が対象の場合、左辺において、|を使って複数の項目を記述してください。
注意:検索条件と全文検索Indexの左辺は完全一致である必要があります。つまり、以下のリクエストは、subInfo.favorite.food|subInfo.favorite.music-ft-XXXのように指定してください。(項目の順序も一致させてください)
subInfo.favorite.food|subInfo.favorite.music;/master|/data
項目名-asc
もしくは、?s={項目}パラメータを指定すると、その項目は昇順でソートされます。また、項目名-desc
指定でその項目は降順となります。
ただし、ソート指定はページネーション検索とともに使う必要があります。これらは、基本的にメモリに展開するインメモリソートになるためパフォーマンスが悪くなる可能性があります。一方、ソート指定する項目にIndexの設定がなされているとレスポンスは高速になります。ソート指定でインデックスが有効な場合は以下のとおりです。
また、index項目を検索する場合、その項目は昇順ソートされているため、ソート指定は不要です。
ソート指定をした項目が存在しない場合は検索できませんので注意してください。
?s=@keyを指定するとキー(link hrefの項目)でソートします。ただし、インメモリに展開するため指定しない場合に比べパフォーマンスが悪くなる場合があります。(デフォルトではkeyの昇順でソートされますがインメモリには展開されません)
OR検索の場合、デフォルトではOR検索条件ごとにソートされたものを合わせて返却します。OR検索内のソート項目は、OR検索内の第一条件がインデックス指定であればインデックス項目の昇順となります。インデックス項目でない場合、keyの昇順となります。
降順ソートを行いたい場合、検索項目とは別に降順用の項目(desc型)を追加すると高速に降順ソートができます。例えば、prop1_desc(desc)をエントリスキーマに定義するとprop1の値で降順ソート用のindexが作成されます。つまり、prop1_desc(desc)
をtemplateのcontentに追加して、rightsに、prop1_desc:/endpoint
を追加することで、検索時にs=prop1_descを指定すると降順ソートになります。
desc型は降順ソートのindexを目的としたもので、実際の値を取得することはできません。GETしてもエントリの中にその項目は現れません。
インメモリソートの項目名-desc
指定で降順ソートを行う場合、降順用の項目(desc型)の追加は必要ありません。
nometaオプションとデータのバックアップ
_nometa
オプションを付けてGET実行すると、id、author、published、updatedがついていないエントリが返ります。これは、データのバックアップを目的にしたもので、取得したFeedをPUT /d/?_bulkserial
実行によりデータをリストアすることができます。(?_bulkserial
オプションについては、「トランザクション処理と大量更新」を参照してください)
l=*
と組み合わせて、GET /d/{キー}?f&_nometa&l=*
と指定することでキー配下の全件のエントリを取得できます。
サーバの現在時刻の取得
GET /{d|s}?_now
でサーバの現在時刻を取得できます。
POSTによるエントリの登録
POSTにより複数のエントリを登録できます。(1回のPUTで最大1000件まで)
エントリのlink rel=selfタグのhref属性にキーを指定してPOSTを実行すると指定したキーで登録されます。以下を実行すると/foo/bar
エントリが登録されます。ただし、親フォルダである/foo
は存在していなければなりません。
登録に成功すると、HTTPステータス201が返ります。エラーの場合はFeed.titleタグにエラーメッセージが返ります。
POST /d
<entry> ・・・ <link href="/foo/bar" rel="self"/> <!-- link rel="href" にKeyを指定します --> </entry>
link rel=selfタグを省略して実行した場合、キーには自動的にユニークな番号が採番されて登録されます。ただし、URLに親階層フォルダ(/foo)を指定する必要があります。
POST /d/foo
<entry> ・・・ <!-- link rel=self は設定しません --> </entry>
PUTによるエントリの更新
PUTにより複数のエントリを同時に登録更新できます。(最大1000件まで)
このとき、登録されるエンドポイントはエントリのキー(link rel=selfタグのhref属性)の値になります。更新されると、HTTPステータスが200(登録時は201)で、メッセージfeed.title:"Updated."を返します。
楽観的排他チェックを行う場合はidを指定してください。(GETで取得したidをそのまま指定してください。+1する必要はありません。)
楽観的排他チェックでは、指定したidのエントリがデータベースに存在するかどうかのチェックを行います。更新が成功すると自動的にidがインクリメントされます。
idを省略して更新すると楽観的排他チェックは行われず強制上書きとなります。また、PUTは登録も可能であり、既存のデータが存在しない場合は更新ではなく新規登録となります。登録では基本的にPOSTを使いますが一度に1000件以上の登録ができないため、実はPOSTを使う意味はほとんどありません。
また、PUTは部分更新であり以下のような注意点があります。
POSTやPUTによりFeed更新する際に1トランザクションで実行できるエントリは1000件までです。1000件を超える大量のエントリを更新するには、PUT /d/?_bulk
等を使って実行する必要があります。ただし、このとき内部では1000件ずつ分割して実行していますので一貫性は担保されません。
PUTによる大量更新では、以下のように、並列/直列、同期/非同期の4種類の実行方法があります。直列実行では、先頭のエントリから1000件づつ実行し完了をまってから次の1000件を実行します。
DELETE によりリソースを削除できます。
例えば、
DELETE /d/foo
を実行すると、fooのリソース(コンテンツとメタデータ)を削除します。このとき、/foo配下にエントリが存在すると「Can't delete for the child entries exist.」エラーとなります。その場合、DELETE /d/foo?_rf
を実行すると配下のエントリを含めて削除することができます。
DELETE /d/foo?r=1
のように、rパラメータでリビジョンを指定すると楽観的排他チェックを行います。
PUTにより複数のエントリを1トランザクションで登録更新できますが、以下の方法で登録更新だけでなく削除も1トランザクション内で実行できます。
PUTするエントリのうち以下のものが削除される
?_delete
を指定(楽観的排他チェックが実行される)
リソースに対してGETを実行するとコンテンツが返ります。ただし、高速化のためETagによるレスポンスコントロールを行っています。つまり、前回参照したコンテンツに更新がなければ、304 Not Modifiedステータスを返します。
?eや?fでメタデータを検索した場合はfeedが返ります。
POSTやPUTなどの場合は基本的なレスポンスの形式は以下のような形になります。
<feed>
<title>{メッセージ}</title>
</feed>
エラーの場合、HTTPステータスのみが返る場合があります。
詳しくは、「HTTPステータスコードとメッセージ」を参照してください。
リソースやメタ情報とは別に採番や採番カウンタを持つことができます。これは、在庫数などの管理や連番が必要なケースで使うことを想定しています。
以下のようなメソッドが用意されています。
vte.cxではユーザ登録の際にメール認証によって本人確認を行うため、メールアドレスが必要になります。また、登録で使用したメールアドレスがログインIDとなります。
メールアドレスの表記は
valid e-mail addressに従います。
しかし、メールアドレスは1つで複数の受信が可能となるような記述方法があるため一意にすることはできません。
例えば、Gmailでは+以降@までを無視して送信しますが受信では無視しません。
foo+1@gmail.com
はfoo@gmail.com
で送信し、foo+1@gmail.com
で受信します。また、ピリオド(.)も無視するため、foo@gmail.com
と f.oo@gmail.com
は、Gmail 上では同じものとみなされます。
vte.cxでは上記のGmailのルールに合わせる形を取っており、メールアドレスとは別のアカウント管理を行っています。それはセキュリティ上の理由からで、ピリオドなどを無視する仕様が、Gmail アドレスを登録する Web サービス側の仕様と組み合わさることによってフィッシングのリスクを増加させるという指摘がなされているからです。詳細
入力されたメールアドレスを以下のルールで変換しシステムで一意なユーザアカウントとします。
ユーザアカウントはuid(ユーザ識別番号)と関連づけられます。uidはシステムで自動的に振られる一意の連番です。
/_user/{uid}
エントリで管理されます。これをユーザエントリといいます。
GET /d/?_whoamiを実行すると現在ログインしているユーザエントリが返ります。(ログインしていない場合、HTTPステータスコード401を返却します。)
<feed>
<entry>
<contributor>
<email>foo.bar@vte.cx</email>
</contributor>
<id>/_user/216,2</id>
<link href="/_user/216" rel="self" />
<summary>Activated</summary>
<title>foobar@vte.cx</title>
<subtitle>nickname</subtitle>
<published>2018-06-09T11:08:38.122+09:00</published>
<updated>2018-06-09T13:38:15.360+09:00</updated>
</entry>
</feed>
ユーザステータスには以下の種類があります。
ユーザ登録画面から仮登録を実行するとステータスがInterimになりユーザに確認メールが送信されます。メール内のリンクをクリックすることで本人認証がなされステータスが本登録(Activated)に変わります。Activatedになることではじめてサービスを使うことができるようになります。
また、ユーザ管理者によってユーザを追加することもできます。詳しくは、「管理者によるユーザ登録」を参照してください。
ユーザ管理者は
PUT /d/?_revokeuser={ユーザアカウント}
を実行することでアカウントを一時的に無効(Revokded)にすることができます。有効にするには、PUT /d/?_activateuser={ユーザアカウント}
を実行します。
また、DELETE /d/?_canceluser
で、ログインユーザのステータスを「Cancelled」(退会)に変更できます。これは、ログインユーザ自身のみ実行可能です。
uid検索
GET /d/?_uid
を実行するとログイン中のuidを返却します。戻り値にはfeedのtitleにuidがセットされます。ログインしていない場合、HTTPステータス401を返却します。
ユーザアカウント検索
GET /d/?_account
を実行するとログイン中のユーザアカウントを返却します。戻り値にはfeedのtitleにユーザアカウントがセットされます。ログインしていない場合はHTTPステータス401を返却します。
ログインとログアウト
ログイン画面からログインIDとパスワードを入力して実行することでログイン認証が行われセッションを開始します。このとき、ログイン画面からはGET /d/?_login
が実行されます。また、パスワードはハッシュ化され、ワンタイムトークン(WSSE)としてリクエストされます。2回のログイン認証に失敗するとreCAPTCHAによる認証が必要になります。ログインロジックの詳細については、vtecxblankプロジェクトのログイン画面のソースを参照してください。ユーザアプリケーション作成の際にはこのログイン画面をカストマイズするとよいでしょう。
GET /d/?_logout
が実行されるとログアウトされセッションが破棄されます。
ユーザ仮登録
ユーザ仮登録はPOST /d/?_adduser
で以下のエントリを登録すると実行されます。
機械的に実行されることを防ぐためreCAPTCHAが要求されます。
<feed>
<entry>
<contributor>
<uri>urn:vte.cx:auth:{ユーザアカウント},{パスワード}</uri>
<name>{ニックネーム}</name>
</contributor>
<title>{メールのタイトル(省略可)}</title>
<summary>
{テキストメール本文(省略可)}
</summary>
<content>
{HTMLメール本文(省略可)}
</content>
</entry>
</feed>
上記を実行すると、summaryタグもしくは、contentタグに記述された文章とタイトル(titleタグ)がメール送信されます。contentにはHTMLメール(任意)を、summaryにはテキストメール(必須)を記述してください。HTMLメールを表示できない場合はテキストメールになります。
メール送信内容は、以下のように
/_settings/adduser
エントリにも記述できます。上記エントリのsummary、content、titleが省略された場合は、/_settings/adduser
エントリの内容でメール送信します。上記エントリのメール送信内容が存在せず、かつ、/_settings/adduser
エントリーも存在しない場合、?_adduser
がリクエストされるとHTTPステータス412(Precondition Failed)が返ります。
また、メール送信するには、
/_settings/properties
にメール送信設定が記述されている必要があります。詳しくは、各種設定情報の「プロパティ情報」を参照してください。
<entry>
<link rel="self" href="/_settings/adduser" />
<title>{メールのタイトル}</title>
<summary>
{テキストメール本文}
</summary>
<content>
{HTMLメール本文}
</content>
</entry>
メールの本文には、ワンタイムトークン(RXID)が付いたリンクを含む必要があります。メールの本文に${RXID=Key}を挿入することで、RXIDを含むURLが自動的に組み立てられます。ユーザがこのリンクをクリックするまでは仮登録となります。
初期フォルダ作成
また、以下のようにadduserinfoエントリのsummaryタグにフォルダのキーを設定することで、最初にログインしたタイミングで自動的にフォルダが作成されます。
キー情報の"#"部分は登録時のユーザ(uid)で置換されます。
<entry>
<link rel="self" href="/_settings/adduserinfo" />
<summary>
{ユーザ登録時に作成するフォルダのキー}
/#/aaaa
/#/bbbb
....
</summary>
</entry>
vtecxblankプロジェクトには、ユーザ仮登録の画面が用意されています。ユーザアプリケーション作成の際にはこの画面をカストマイズするとよいでしょう。
パスワードリセットはPOST /d/?_passreset
で以下のエントリを登録することで実行されます。機械的に実行されることを防ぐためreCAPTCHAが要求されます。
<feed>
<entry>
<contributor>
<uri>urn:vte.cx:auth:{ユーザアカウント}</uri>
</contributor>
<title>{メールのタイトル(省略可)}</title>
<summary>
{テキストメール本文(省略可)}
</summary>
<content>
{HTMLメール本文(省略可)}
</content>
</entry>
</feed>
上記を実行すると、summaryタグ(テキストメール)もしくは、contentタグ(HTMLメール)に記述された文章がメール送信されます。メール送信内容は、以下のように/_settings/passreset
エントリにも記述できます。上記エントリのsummary、content、titleが省略された場合は、/_settings/passreset
エントリの内容でメール送信します。
メールにはワンタイムトークン(RXID)リンクを含む必要があります。
ユーザがこのリンクをクリックすることでパスワードを再設定します。
上記エントリにメール送信情報がなく、
/_settings/passreset
エントリーにも存在しない場合、?_passreset
がリクエストされるとHTTPステータス412(Precondition Failed)が返ります。
vtecxblankプロジェクトには、パスワードリセットの画面が用意されています。ユーザアプリケーション作成の際にはこの画面をカストマイズするとよいでしょう。
パスワード変更はPUT /d/?_changephash
で以下のエントリを更新することで実行されます。ログイン中のアカウントに対して実行するため、reCAPTCHAは要求されません。
<feed>
<entry>
<contributor>
<uri>urn:vte.cx:auth:,{パスワード}</uri>
</contributor>
</entry>
</feed>
vtecxblankプロジェクトには、パスワード変更の画面が用意されています。ユーザアプリケーション作成の際にはこの画面をカストマイズするとよいでしょう。
?_adduserByAdmin
パラメータを付けて以下を実行することで新規ユーザを登録します。
/_settings/adduserinfo
に設定した初期フォルダが作成されます。/_settings/adduserByAdmin
エントリが登録されている場合、このエントリの内容でメールが送信されます。POST|PUT /d?_adduserByAdmin
<feed>
<entry>
<contributor>
<uri>urn:vte.cx:auth:{メールアドレス},{パスワード}</uri>
<name>{ニックネーム}</name>
</contributor>
<title>メールのタイトル(任意)</title>
<summary>テキストメール本文(任意)</summary>
<content>HTMLメール本文(任意)</content>
</entry>
...
</feed>
?_changephashByAdmin
パラメータを付けて以下を実行することでパスワードを変更します。
PUT /d?_changephashByAdmin
<feed>
<entry>
<contributor>
<uri>urn:vte.cx:auth:,{パスワード}</uri>
</contributor>
<link href="/_user/{UID}/auth" rel="self" />
</entry>
...
</feed>
PUT /d?_changeaccount
でアカウント変更のためのメール送信を行います。これはログインユーザ自身のみ実行可能です。
<feed>
<entry>
<contributor>
<uri>urn:vte.cx:auth:{メールアドレス}</uri>
</contributor>
<title>メールのタイトル(任意)</title>
<summary>メールの本文(任意)</summary>
</entry>
</feed>
PUT /d?_changeaccount_verify={認証コード}
で実際にアカウント変更を実行します。
注) サービス作成者のアカウント変更を行う場合、作成したサービスとシステム管理サービスそれぞれについてアカウント変更を実施してください。
アカウント名が一致していないと、システム管理サービスから作成したサービスへのログインができません。
DELETE /d?_deleteuser={アカウント|uid}
で指定されたアカウントの削除を行います。アカウントかuidのいずれかを指定できます。
DELETE /d?_deleteuser
で本文(body)にアカウントエントリを指定することで複数のユーザを削除できます。
<feed>
<entry>
<link rel="self" href="/_user/{uid}" />
<title>{アカウント|uid}</title>
</entry>
・・・
</feed>
システムグループは複数のユーザーの権限をまとめて管理するために、システムがあらかじめ定義しているグループです。
つまり、
/_group
フォルダ配下にグループエントリ、その配下にuidエントリを作成し、/_user/{uid}/group
へのaliasを付与することで、ユーザ(uid)がシステムグループに参加したものとして認識されます。uidはサービスを作成した本人のものが使われます。
例:サービス管理者権限のグループエントリ
<feed>
<entry>
<id>/_group/$admin/216,3</id>
<link href="/_group/$admin/216" rel="self" />
<link href="/_user/216/group/$admin" rel="alternate" />
<published>2018-06-07T14:32:40.444+09:00</published>
<updated>2018-06-09T11:08:47.328+09:00</updated>
</entry>
</feed>
/_group/$admin
グループに属することでサービス管理権限が付与されます。
システムグループには以下のものがあります。
(システムグループは
$admin
のように$
で始まる名前のグループです)
/_group/$admin
<contributor>
タグおよび、<rights>
タグの内容を自由に編集可能/_settings
フォルダにはサービス管理権限のACL(CRUD)が付与されている/_group/$useradmin
/_group/$content
<content>
タグのテキストノードの内容を自由に更新可能ユーザ作成グループ
ユーザ作成グループはユーザが自由に定義できるグループで/_user/{uid}
配下にあるものです。
/_user/{uid}/group/{グループ名}
というエントリを作成することで、ユーザ作成グループを作ることができます。
※ユーザ作成グループは署名が必要です。詳しくは署名を参照してください。
グループ署名のパターンには、以下の2つがあります。グループ署名は作成者、参加者双方の署名を必須とします。
例)
* グループ名: /_user/123/mygroup -> created uid=123
* グループ作成者uid: 123
* グループ参加者uid: 456
①の場合
* 子がそのグループに参加申請
* 以下のエントリーを登録
* idキー: /_user/123/mygroup/456
* alias: /_user/456/group/mygroup
* 以下のキーに署名
* alias: /_user/456/group/mygroup uid=456
* 親が署名して参加承認
* idキー/_user/123/mygroup/456のtitleに署名。uid=123
* 参加者のグループ参加チェック
* /_user/456/group配下をfeed検索
* /_user/456/group/mygroupのidキーが/_user/123/mygroup/456なので、署名検証を行う。(/_groupで始まらない、かつ自uidとグループ名のUIDが異なるため)
* /_user/123/mygroup/456の署名検証を行う。uid=123
* /_user/456/group/mygroupの署名検証を行う。uid=456
②の場合
* 親が子に参加依頼
* 以下のエントリーを登録
* idキー: /_user/123/mygroup/456
* alias: /_user/456/group/mygroup
* 以下のキーに署名
* idキー: /_user/123/mygroup/456 uid=123
* 子が署名して参加
* alias/_user/456/group/mygroupのtitleに署名。uid=456
* 参加者のグループ参加チェック
* /_user/456/group配下をfeed検索
* /_user/456/group/mygroupのidキーが/_user/123/mygroup/456なので、署名検証を行う。(/_groupで始まらない、かつ自uidとグループ名のUIDが異なるため)
* /_user/123/mygroup/456の署名検証を行う。uid=123
* /_user/456/group/mygroupの署名検証を行う。uid=456
フォルダACL
vte.cxでは、これまで述べたように、エンドポイントURLをフォルダに見立てて、その配下にリソースを格納できます。また、そのフォルダにACLを設定することで自身および配下のエントリについてアクセスコントロールを設定できます。
ACLは以下のようにエントリのcontributorタグに指定します。特定のユーザ(uid)、ログイン済ユーザ(+)、すべてのユーザ(
*
)、あるいはグループ(GroupKey)を設定できます。グループ指定ではワイルドカードの指定も可能です。(/group*など)
<contributor>
<uri>urn:vte.cx:acl:{uid|*|+|GroupKey},{C|R|U|D|E|.|/}</uri>
</contributor>
ACLの種類 (複数指定可能だが「E」「.」「/」のみの指定は不可)
*
)を指定可能*
: ログインしていないユーザを含むすべてのユーザが対象+
: ログインしているすべてのユーザが対象以下をリクエストすることでACLを追加できます。
PUT /d/?_addacl
<feed>
<entry>
<link rel="self" href="{キー}" />
<contributor>
<uri>urn:vte.cx:acl:{ACL対象},{権限}</uri>
</contributor>
</entry>
</feed>
また、PUT /d/?_removeacl
で指定したACLを削除できます。
/_settings/template
エントリのタグに記述することで項目に対してACLを設定することができます。ACLにはユーザおよびグループの読込(R)または書込(W)権限を指定できます。
以下のように、項目名に続けて=の後に、{uid|group}+{RW}の形式でACLを指定します。 ,(カンマ)で複数件指定できます。
subInfo.favorite.food=3+W,/grp1+W,/*+R
/_settings/template
エントリのrightsタグにはIndexも指定できますが、以下のようにindexとACLを同時に記述することができます。:
コロンの右辺がIndex、=
の右辺がACLになります。
subInfo.favorite3.food:/[0-9]+/(self|alias)=1+W
項目名に続けて#を付けると暗号化項目となります。Index項目と暗号化項目はどちらか一つを指定できます。 暗号化では、vte.cx内部で持っている秘密Key(usersecret)とidと組み合せてハッシュ化したものが実際の暗号化において使われます。
以下はcontributor.uriを暗号化する設定例です。
contributor.uri#
また、以下のように、暗号化と項目ACLは同時に設定できます。これは、rightsの暗号指定、かつ、自身と/_group/$adminグループのRW権限の付与を意味します。
rights#=@+RW,/_group/$admin+RW
_
から始まるフォルダをシステムフォルダといいます。システムフォルダはシステムが管理する特殊なフォルダであり以下の種類があります。
/_group
:システムグループフォルダ
/_user
:ユーザフォルダ
/_html
:htmlコンテンツフォルダ
/_html
は /
にマッピングされます。/_settings
:各種設定情報
/_log
:ログ
/_security
:認証の失敗回数を格納
vte.cxの設定情報は以下のように/_settings配下で管理されます。これらは、直接編集もできますが、管理画面の「メール・詳細設定」でも設定できます。(推奨)
/_settings/properties
:プロパティ情報/_settings/template
:エントリスキーマやIndex、項目ACLなど/_settings/bigquery.json
:BigQuery用サービスアカウント秘密鍵ファイル/_settings/adduser
:ユーザ登録時(?_adduser)に送られるメール本文/_settings/passreset
:パスワードリセット時(?_passreset)に送られるメール本文プロパティ情報(/_settings/properties
)のrightsタグにおいて以下のような情報を設定できます。ここで設定されていないものについてはシステム内部で持つ設定情報がデフォルトで使用されます。RXIDの詳細については「認証キーとトークン」を参照してください。
_entry.number.default : エントリーGET時のデフォルト最大数 [100]
_rxid.minute : RXID(WSSE)有効時間(分) [120]
_rxid.counter.{連番}.{回数} : 同じRXIDを使用しても指定回数まで許可されるURLパターンを指定。
_session.minute : セッション有効時間 [30]
エラーページ設定
以下はエラーページの設定です。これは、アクセス時にエラーが発生した場合にリダイレクトするルールを設定します。
また、リダイレクトする際に、Cookieの「ERROR_STATUS」にHTTPステータスコードが、また「ERROR_MESSAGE」にエラーメッセージがセットされます。このCookieは10秒間だけ有効です。
_errorpage.{適用順}.{エラーページselfid}={PathInfoの正規表現} : エラー画面表示URLパターン(正規表現)
confirm.html表示中にエラーの場合はerror.htmlに遷移する。それ以外のエラーはlogin.htmlに遷移する設定例
_errorpage.1.error.html=^/_html/confirm.html$ _errorpage.2.login.html=^/_html/.*$
メール送信設定
以下はメール送信の設定です。これが設定されていないとユーザ登録においてメール送信ができないため本人確認ができません。ご自身でEmailを用意して設定してください。
_mail.from.personal : EMailの送信元名
_mail.from : Emailのfrom
_mail.transport.protocol : smtpsなどのメール送信プロトコル
_mail.password : メール送信アカウントのパスワード
_mail.smtp.host : メール送信ホスト
_mail.smtp.port : メール送信ポート
_mail.smtp.auth : true/false
BigQuery接続設定
以下はBigQueryに接続するための設定です。
これとは別に、/_settings/bigquery.jsonに、サービスアカウントの秘密鍵JSONを登録する必要があります。BigQueryを使用するためにはGoogle Cloud Platformプロジェクトを作成し、BigQueryを有効にする必要があります。
_bigquery.projectid : プロジェクトID
_bigquery.dataset : データセット名
_bigquery.location : ロケーション(デフォルト値は asia-northeast1)
プロジェクトの作成とIDの確認
まず、Googleアカウントを取得します。
(https://accounts.google.com) のログイン画面からアカウントを作成を選択しアカウントを作成します。
1.作成したアカウントで Google Cloud Platform にログインします。(https://console.cloud.google.com)
2.プロジェクトを作成します。
左上のGoogle Cloud Platformという題名の隣にプロジェクトの選択 ▼というリスト項目が表示されているのでクリックします。
既に別のプロジェクトを作成し選択している場合、プロジェクト名が表示されます。
表示された画面右上の新しいプロジェクトを選択し、新規プロジェクトを作成します。
3.プロジェクトIDの確認
Google Cloud Platform のホーム画面に表示されるプロジェクト情報にプロジェクトIDが表示されます。
Billingの設定
BigQueryを使用するにはBillingの設定が必要です。
Google Cloud Platform の左メニューから「お支払い」を選択し、請求先アカウントを追加します。
データセットの作成
Google Cloud Platform の左メニューからBigQueryを選択すると、BigQueryの管理画面が表示されます。(https://bigquery.cloud.google.com)
BigQuery管理画面の左メニューより、プロジェクト名の横の下三角▼をクリックします。
Create new datasetを選択し、データセットを作成します。
データセットID、ロケーションを入力します。
BigQueryのサービスアカウント秘密鍵の作成
サービスアカウントとは、Googleの各サービスに対する権限を持つアカウントです。Googleのサービスに対しAPIリクエストを行う際に認証情報として使用します。
Google Cloud Platform の左メニューからIAMと管理-サービスアカウントを選択します。
画面上部のサービスアカウント表題の隣にある+サービスアカウントを作成をクリックします。
1.サービスアカウント名: 任意の値を入力
2.Project role: BigQuery-BigQueryユーザーおよびBigQueryデータオーナーを選択
3.新しい秘密鍵の提供にチェックを入れます。キーのタイプをJSONとします。
4.保存をクリックします。
サービスアカウントが作成されます。同時に、JSON形式の秘密鍵がダウンロードされます。
アクセストークンは時間制限なしの認証トークンであり、スマホアプリからの認証の他、サービスデプロイ時の認証トークンとしても使われます。
ログイン後、GET /d/?_accesstoken
で取得できます。 feed.titleにアクセストークンが入ったものが返ります。
<feed>
<title>{Accesstoken}</title>
</feed>
リクエスト時にAccesstokenをリクエストヘッダに付与することで認証されます。
認証が成功してもログイン状態にはなりません。
Authorization : Token {Accesstoken}
リンクトークンはアクセストークンと同様に時間制限なしの認証トークンですが、アクセスできるURLに制限が付けられています。
これは、メールに含まれるURLリンクとして使うことを想定しています。送信するメールの本文に以下のように${LINK=...}を挿入することで、Linktokenを含むURLが自動的に組み立てられます。
挿入文
${LINK=/setpass.html}&value=abc
実際のメール本文
https://test.vte.cx/setpass.html?_token=xxx&value=abc
URLリンク(上記Linktoken)をクリックするとsetpass.htmlが表示されます。
ただし、ログイン状態にはなりません。
Keyを /foo と指定した場合、 許可される操作は以下のみです。
また、リンクトークンは、ログイン後、GET ?_linktoken={Key1[,Key2],・・}
をリクエストすることでも取得できます。 KeyにはURLのPathinfoを指定します。カンマで区切ることで複数指定できます。Keyに含まれる#はuidに変換されます。
アクセスキーは、アクセストークンやリンクトークンを発行するために使用されるシークレットキーであり、サーバ内(/_user/{uid}/accesskey
エントリ)に保持しています。
アクセスキーを更新することで、これまで発行したアクセストークンやリンクトークンを無効にすることができます。つまり、アクセストークンが悪意のある第三者に漏洩してしまった場合、そのアクセストークンに認可されているあらゆる操作が永久に実行可能になるため、アクセスキーを更新してこれを防ぐ必要があります。
PUT /d/?_accesskey
でアクセスキーを更新します。更新するとこれまで使っていたアクセストークンやリンクトークンは使えなくなります。
ワンタイムトークン(RXID)は鍵付きハッシュを利用した認証トークンです。ワンタイムであり一度認証が実行されると同じものはもう使えません。これは、AndroidやiPhoneなどのスマホのログイン認証で使うことを想定しています。また、異なるサービス間で通信が必要になった場合にも使われることがあります。
トークン生成にはユーザアカウント、パスワード、APIキー(後述)、サービス名を使用します。ハッシュ化された文字列なのでネットワーク上で生のパスワードが流れる心配はありません。
ログイン後、GET /d/?_getrxid
を実行することでRXIDを取得できます。
RXIDは、
GET /d/?_RXID={RXID文字列}
で認証されます。認証後はログイン状態になります。また、以下のようにリクエストヘッダにつけて認証することもできます。
Authorization: RXID {RXIDトークン}
RXIDの有効時間や実行可能回数は、設定ファイル(/_settings/properties
のrightsタグ)に指定します。
_rxid.minute=60 _rxid.counter.1.10000=^/_html/foo.html.*$
APIキーはワンタイムトークン(RXID)などに使用されるクライアントシークレットキーです。また、サーバサイドJavaScriptにおいてサービスを跨ぐAPI実行の際にもAPIキーが必要になります。サービスに対してAPIキーを1つ発行します。
APIキーを更新したい場合は以下を実行します。サービス管理者が実行できます。
PUT /d/?_apikey
APIキーを更新すると、これを保持している全てのクライアントから認証ができなくなります。第三者に漏洩してしまった場合などはAPIキーを更新することで悪意のある操作を防ぐことができます。ただし、更新した場合は新しいAPIキーをクライアントや他のサービスに配布する必要があります
APIキーを更新してもアクセストークンやリンクトークンには影響はありません。これらを無効にしたい場合はアクセスキーを更新してください。
スマホアプリからログインするにはRXIDを使います。
RXIDを生成するライブラリであるvtecxauthパッケージをnpmで公開しています。まず、nodeをインストールし、npm install vtecxauth
でvtecxauthパッケージをインストールしてください。
vtecxauthパッケージの
vtecxauth.getRXID()
メソッドを実行することでRXIDを取得することができます。
パラメータには、ユーザアカウント、パスワード、サービス名、APIキーを指定します。APIキーは管理画面で確認できます。
vtecxauth.getRXID(username: string, password: string, servicename: string, apikey: string): string;
以下はRXIDを使った認証の例です。初回はRXIDで認証してcookieを保存し、2回目以降はcookieを使って認証してください。(サーバではセッションが生成されています)
セッションが不要な場合はcookieの代わりにアクセストークンを使用してください。アクセストークンを使う場合はセッションは生成されずタイムアウトもありません。
// 初回(RXIDでログイン)
const rxid = vtecxauth.getRXID(username, password, servicename, apikey)
axios({
url: 'http://{サービス名}.vte.cx/d',
method: 'get',
headers: {
'Authorization: RXID '+ rxid,
'X-Requested-With': 'XMLHttpRequest'
}
}).then((result) => {
cookie = result.headers['set-cookie'][0].split(';')[0] // cookieを保存
}).catch((error) => {
・・・
})
// 2回目以降(cookie認証)
axios({
url: 'http://{サービス名}.vte.cx/d',
method: 'post',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Cookie': cookie
},
data : reqdata
}).then((result) => {
・・・
}).catch((error) => {
・・・
})
Entryに署名をつけることでユーザが承認したことを示す記録を残すことができます。グループにおけるメンバー承認のプロセスにおいても署名が使われます。
キーはselfまたはエイリアスに署名を付けることができます。ただし、キーのuidはログインユーザと一致する必要があります。つまり、/_user/{uid}
で始まるキーに対してのみ署名可能です。そうでない場合、署名不可エラーとなります。
<link rel="{self | alternate}" href="{キー}" title="{revision},{uid},{署名}" />
以下で署名を付与できます。
PUT {キー}?_signature&r={リビジョン(任意)}
正常に署名できた場合、HTTPステータスコード200(A signature has been applied.)が返ります。ログインしていない場合、401が、指定されたKeyに署名する権限がない場合、403が返ります。
既に署名が付いている場合は更新します。更新の際はRevisionを+1し、更新時刻を新たに設定します。署名ができるのは、idのキー、またはエイリアスのキーのみです。
以下で署名を削除できます。
DELETE {キー}?_signature&r={リビジョン(任意)}
削除が成功すると、linkタグのtitle属性に"-"(署名削除)を設定します。(署名がないものとの区別が必要になるケースがあるため、ブランクにするのでなく、無効な値(-)をセットする必要があります。)
正常に署名を削除できた場合、HTTPステータスコード200が返ります。ログインしていない場合、401が、指定されたKeyに署名する権限がない場合、403が返ります。
署名削除の際も通常の更新と同じくRevisionを+1し、更新時刻を新たに設定します。
以下で署名を検証できます。
GET {キー}?_signature
指定するキーは、署名検証したいキーです。キーでなくエントリーそのものの参照権限がある場合、署名検証を可能とします。
正常に署名を検証できた場合、HTTPステータスコード200が返ります。署名が不正である場合、412(The signature is invalid.)が返ります。キーのエントリーそのものを参照する権限がない場合、403が返ります。
ブラックリストに登録することでDoS攻撃などの可能性があるリクエストを拒否(ブロック)できます。ブラックリストに登録されているユーザ・IPアドレスの組み合わせからのリクエストは認証エラーとなります。特定のIPから1000回(デフォルト)以上ログイン失敗でユーザ・IPアドレスの組み合わがブラックリストに自動的に追加されます。ブラックリストに追加されても異なるIPからログインすることは可能です。
ブラックリストは認証失敗カウンタによって管理します。認証失敗カウンタは、/_security/{アカウント認識文字列}/{IPアドレス}キーのカウンタが使われます。
ブラックリストのブロックは最後に認証失敗してから24時間後(デフォルト)に解除されます。または、認証失敗カウンタを0にすることでブロックを解除できます。具体的には以下のようなリクエストです。
ブロック解除
DELETE /d/_security/{アカウント認識文字列}/{IPアドレス}?_cachelong
vte.cxのアプリケーションを作成すると基本的にSPA(Single Page Application)になります。SPAのスタイルでは、サーバサイドでレンダリングするのではなく、クライアントからはAjax(XHR)を用いてサーバのAPIにアクセスし、サーバはJSONデータを返すのみとなります。サーバサイドは純粋なAPIとなり、複雑さがサーバサイドからクライアントに移動していることから、これはThin server architecture とも呼ばれます。
しかし、ビジネスロジックをもたない純粋なREST APIだけで構築すると開発生産性やパフォーマンスを大きく損ねることがあります。あまりにもクライアント側にビジネスロジックが偏重しすぎて開発工数が膨らんでしまうのです。また、クライアントから何回もAPIを呼ぶいわゆるN+1問題も発生しがちです。
実はクライアントよりもサーバ側でビジネスロジックを実行する方が有利であることがわかっています。つまり、クライアントはビジネスロジックを持たない単純なViewとし、Viewに必要なデータの組み立てなどはサーバ側で行うことで、クライアント側へのビジネスロジック偏重を防ぎます。こうすることで、N+1問題も解決できます。
このスタイルを、BFF(Backend for Frontend)といいます。vte.cxではサーバサイドのBFFにもJavaScriptを採用することで、統一的(Isomorphic)な環境で高い生産性を発揮できるようにしています。
BFFは実はフロントエンドアプリケーションの範疇になります。重要なポイントは、あくまでフロントエンドのアプリケーションとしてサーバサイドJavaScriptを実装するということであり、データベース設定や認証設定などのようなサーバ構築のための設定などではないという点です。
サーバサイドJavaScriptを設定する方法は簡単で、/serverフォルダ配下にJavaScriptファイルを格納するだけです。また、GET|POST|PUT /s/{スクリプト名}のようにリクエストすると、/serverフォルダに格納されている {スクリプト名}.jsファイルがサーバ上で実行されます。
サーバサイドJavaScriptの実行時間は同期リクエストで最大5分までで、それを超えると強制的にキャンセルされます。長時間かかる場合は_asyncパラメータを付けて非同期リクエストを実行してください。非同期リクエストでは別スレッドが起動し、ステータス202 Acceptedをレスポンスします。実際の処理はバッチジョブサーバで実行されるため、バッチジョブサーバに設定されたタイムアウト時間が最大の実行時間になります。
サーバサイドJavaScriptは、TypeScriptで書かれたソースコードをWebpackなどによりECMAScript5の形式に変換されたものを実行します。 これは、以下のようにビルドします。
ログイン
管理画面にてサービスを作成後(仮にfooserviceとします)、githubのブランクプロジェクトをチェックアウトして、npm installします。その後、以下のコマンドでログインします。アカウントとパスワードはサービス作成で使ったものと同じもの使用してください。
npm run login service:fooservice is production?:n login:foo@bar.com password:********* Logged in.
ビルドとデプロイ
以下のコマンドを実行するとssr.html.tsxをビルド&デプロイできます。また、ソースを更新すると自動的にビルド&デプロイされます。デプロイ後、/s/ssr.htmlをブラウザで開いて確認してください。
npm run watch — --env entry=/server/ssr.html.tsx
クライアントコードの開発
以下のコマンドを実行するとクライアントコードの開発環境が開きます。(webpack-dev-serverが起動します)
npm run serve:index
ログインが必要な場合は以下でログインページを開き、ログイン後に再び上記コマンドを実行してください。
npm run serve:login
index.tsxとは異なるファイル名(以下の例はhello.tsx)を指定する場合は以下のようにしてください。
npm run serve — --env entry=/components/hello.tsx
vtecxapiパッケージをインポートすることでサーバサイドJavaScriptからvte.cxのAPIを利用することができます。まず初めにnpm install vtecxapiでvtecxapiパッケージをインストールしてください。
以下はReactの機能を使ってSSR(サーバサイドレンダリング)を実行するサンプルです。最後の行のHTMLを返却するところでvtecxapiが使われています。
以下をデプロイして、/s/ssr.htmlにアクセスすると、「Hello, World!」が表示されるのを確認できます。
// server/ssr.html.tsx
import * as vtecxapi from 'vtecxapi'
import * as React from 'react'
import * as ReactDOMServer from 'react-dom/server'
const element = (
<h3> Hello, World! </h3>
)
const html = ReactDOMServer.renderToStaticMarkup(element)
vtecxapi.doResponseHtml(html)
vtecxapi.doResponse()により、オブジェクトをJSONなどに変換してクライアントに返すことができます。サーバサイドのビジネスロジックはこのような形でJSONを返すサービスとして実装することが多いと思われます。
以下は/fooエントリを取得してJSONを返すサンプルです。
前述のサンプルでは、コンテンツを返すのに、doResponseHtml()を使っていましたが、JSONなどのデータを返すには、doResponse()を使います。 ちなみに、doResponse()は、リクエストパラメータにデータタイプを指定することで様々なデータ形式に変換することができます。例えば、/registration?xでxml、/registration?mでMessagePackを返すことができます。
// server/registration.tsx
import * as vtecxapi from 'vtecxapi'
const feed = vtecxapi.getFeed('/registration') // /registration?xでxmlになる
vtecxapi.doResponse(feed)
また、vtecxapi.getFeed()は検索条件を指定して検索することができます。
検索条件の文字列に&が含まれる可能性がある場合は、encodeURIComponent()などを使って必ずエンコードしてください。レスポンスの最初のエントリのrightsに文字列(nextpagelink)が格納されている場合、次ページが存在することを示します。次のリクエストにて、p={nextpagelink}パラメータを付けることで次ページを取得できます。
// server/registration.tsx
import * as vtecxapi from 'vtecxapi'
const param = encodeURIComponent(vtecxapi.getQueryString('param'))
const feed = vtecxapi.getFeed('/registration?param='+param) // /registration?xでxmlになる
vtecxapi.doResponse(feed)
リソースの登録更新では、raw形式のバイナリの他にFormDataオブジェクトを使ったアップロードを行えます。FormDataオブジェクトはmultipart/form-data形式のデータであり、ブラウザから直接アップロードすることができます。
アップロードファイルが一つでありキーも指定されていない場合、アップロードしたファイル名がエントリのKeyとして登録されます。アップロードファイルが複数の場合は以下のようにサーバサイドJavaScriptを使って処理することができます。
vtecxblankに複数のファイルを登録するサンプルプログラム(upload_pictures_sample)があります。これは、クライアントから2つの画像データを送信してサーバに登録するサンプルです。
サーバサイドJavaScriptのvtecxapi.saveFiles(param)は、ファイルアップロードにおけるMultipart Postリクエストをサーバ側で処理するために使用します。具体的には、saveFiles(param)で指定するparamオブジェクトに、inputタグのnameをキーにして任意のファイル名を設定します。
サービスを作成し、デプロイしてログインすると、/upload_pictures_sample.html
を表示できますので、そこから2つの画像ファイルをアップロードしてみてください。アップロードに成功すると、http://{サービス名}.vte.cx/{アップロードファイル名}
でブラウザに表示して確認できます。(※ サービスの作成方法については、チュートリアルを参照してください。)
画面側(upload_pictures_sample.tsx)
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import axios, { AxiosError } from 'axios'
import {
Form,
FormGroup,
FormControl,
Button
} from 'react-bootstrap'
/* コンポーネントのPropsの型宣言 */
interface ComponentProps {
}
/* コンポーネントのStateの型宣言 */
interface ComponentState {
picture1: any,
picture2: any,
[propName: string]: any
}
class UploadPictureForm extends React.Component<ComponentProps, ComponentState> {
constructor(props: ComponentProps) {
super(props)
this.state = { picture1: {}, picture2: {} }
}
handleChange(e: React.FormEvent<any>) {
if (e.currentTarget.files) {
const file = e.currentTarget.files.item(0)
if (file) {
const key = '/_html/img/' + encodeURIComponent(file.name)
const name = e.currentTarget.name
// 画像以外は処理を停止
if (!file.type.match('image.*')) {
return
} else {
// 画像表示
let reader = new FileReader()
reader.onload = () => {
this.setState({ [name]: { value: reader.result, key: key } })
}
reader.readAsDataURL(file)
}
}
}
}
handleSubmit(e: React.FormEvent<any>) {
e.preventDefault()
const formData = new FormData(e.currentTarget)
const param = (this.state.picture1.key ? 'key1=' + this.state.picture1.key + '&' : '') +
(this.state.picture2.key ? 'key2=' + this.state.picture2.key : '')
// 画像は、/d/_html/img/{key} としてサーバに保存されます
axios({
url: '/s/savefiles?' + param,
method: 'post',
headers: {
'X-Requested-With': 'XMLHttpRequest'
},
data: formData
}).then(() => {
alert('success')
}).catch((error: AxiosError) => {
if (error.response) {
alert('error=' + JSON.stringify(error.response))
} else {
alert('error')
}
})
}
render() {
return (
<Form horizontal onSubmit={(e) => this.handleSubmit(e)}>
<img src={this.state.picture1.value} />
<br />
<img src={this.state.picture2.value} />
<br />
<FormGroup>
<FormControl type="file" name="picture1" onChange={(e) => this.handleChange(e)} />
</FormGroup>
<FormGroup>
<FormControl type="file" name="picture2" onChange={(e) => this.handleChange(e)} />
</FormGroup>
<FormGroup>
<Button type="submit" className="btn btn-primary">
登録
</Button>
</FormGroup>
</Form>
)
}
}
ReactDOM.render(<UploadPictureForm />, document.getElementById('container'))
サーバサイドJavaScript(/server/savefiles.tsx)
import * as vtecxapi from 'vtecxapi'
interface Param {
picture1: string
picture2: string
}
const param: Param = {
picture1: vtecxapi.getQueryString('key1'),
picture2: vtecxapi.getQueryString('key2')
}
vtecxapi.saveFiles(param)
サーバサイドJavaScriptを利用することでCSVデータのアップロードを行うことができます。vtecxapi.getCsv(header[],items[],parent,skip,encoding)
は、指定したパラメータを元にCSVを受信してJSONオブジェクトに変換します。パラメータの意味は以下の通りです。
{"feed":{"entry":[{"title" : "Header parse error"}]}}
のようにパースエラーとなる。vtecxblankプロジェクトには以下のサンプルプログラムがあります。
デプロイしてログインすると、/upload_csv_sample.html
を表示できますので、その画面から/data/sample.csv
を指定してアップロードしてください。アップロードに成功するとログに読み取った結果のJSONを表示しますので、管理画面のログで確認してください。
画面側(upload_csv_sample.tsx)
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import axios, { AxiosError } from 'axios'
import {
Form,
FormGroup,
FormControl,
Button
} from 'react-bootstrap'
/* コンポーネントのPropsの型宣言 */
interface ComponentProps {
}
/* コンポーネントのStateの型宣言 */
interface ComponentState {
}
class UploadCsvForm extends React.Component<ComponentProps, ComponentState> {
constructor(props: ComponentProps) {
super(props)
this.state = {}
}
handleSubmit(e: React.FormEvent<any>) {
e.preventDefault()
const formData = new FormData(e.currentTarget)
// 画像は、/d/registration/{key} としてサーバに保存されます
axios({
url: '/s/getcsv',
method: 'post',
headers: {
'X-Requested-With': 'XMLHttpRequest'
},
data: formData
}).then(() => {
alert('success')
}).catch((error: AxiosError) => {
if (error.response) {
alert('error=' + JSON.stringify(error.response))
} else {
alert('error')
}
})
}
render() {
return (
<Form horizontal onSubmit={(e) => this.handleSubmit(e)}>
<FormGroup>
<FormControl type="file" name="csv" />
</FormGroup>
<FormGroup>
<Button type="submit" className="btn btn-primary">
登録
</Button>
</FormGroup>
</Form>
)
}
}
ReactDOM.render(<UploadCsvForm />, document.getElementById('container'))
サーバサイドJavaScript(/server/getcsv.tsx)
import * as vtecxapi from 'vtecxapi'
const items = ['item1', 'item2(int)', 'item3(int)']
const header = ['年月日', '件数', '合計']
const parent = 'order'
const skip = 1 // 1行スキップ
//const encoding = 'Windows-31J'
const encoding = 'UTF-8'
// CSV取得
const result = vtecxapi.getCsv(header, items, parent, skip, encoding)
vtecxapi.log(JSON.stringify(result))
CSVデータ(/data/sample.csv)
// 1行skip
年月日,件数,合計
"2017/7/5",3,3
"2017/7/6",5,8
"2017/7/7",2,10
ReactのSSR(Server Side Rendering)機能とvtecxのPDF出力機能を組み合わせることで動的にPDFを生成することができます。vtecxapi.toPdf(pages,html,outfilename)
は、指定したパラメータを元にPDFを生成します。パラメータの意味は以下の通りです。
vtecxblankプロジェクトには以下のサンプルプログラムがあります。
デプロイ後にログインして /s/ssr.pdfにアクセスすると、「Hello, Harper Perez」と表示されたpdfがダウンロードされます。
/pdf/pdfstyles.tsはPDFのレイアウト等を指定するスタイルシートファイルです。以下のようにstyles属性に指定することで色やサイズなど様々なスタイルを設定することができます。詳しくは、「PDFスタイルシート」を参照してください。
サーバサイドJavaScript(/server/ssr.pdf.tsx)
import * as vtecxapi from 'vtecxapi'
import * as React from 'react'
import * as ReactDOMServer from 'react-dom/server'
import * as pdfstyles from '../pdf/pdfstyles'
interface User {
firstName: string
lastName: string
}
function formatName(user: User) {
return user.firstName + ' ' + user.lastName
}
const user: User = {
firstName: 'Harper',
lastName: 'Perez'
}
const element = (
<html>
<body>
<div className="_page" style={pdfstyles._page}>
<table style={pdfstyles._table}>
<tr>
<td>
<p> Hello, {formatName(user)}! </p>
</td>
</tr>
</table>
</div>
</body>
</html>
)
const html = ReactDOMServer.renderToStaticMarkup(element)
// PDF出力
vtecxapi.toPdf(1, html, 'test.pdf')
PDF用スタイルファイル(/pdf/pdfstyles.ts)
export const _page: any = {
pagesize: 'A4',
orientation: 'portrait'
}
export const _table: any = {
bgcolor: '#FFEC8B',
frame: 'box',
cellspacing: '3',
cellpadding: '3',
width: '90%',
align: 'left'
}
vtecxapi.sendMail(entry: any, to: string[] | null, cc?: string[], bcc?: string[], attachments?: string[])
によりメールを送信することができます。
パラメータには以下を指定します。
entryのsummaryにはテキストメール本文を、contentにはHTMLメール本文を指定します。HTMLメールを送信できない場合はテキストメールが送信されます。
HTMLメールのインライン画像は
<img src="CID:/_html/img/ajax-loader.gif">
のように、CIDに続けてキーのURLを指定します。
以下はメールを送信するvtecxblankのサンプルプログラムです。
/server/sendmail.tsx
import * as vtecxapi from 'vtecxapi'
const mailentry = {
'entry': {
'title': 'sendmail テスト',
'summary': 'hello text mail',
'content': {
'______text': '<html><body>hello html mail <img src="CID:/_html/img/ajax-loader.gif"></body></html>'
}
}
}
const to = ['xxxx@xxx']
const cc = ['xxxx@xxx']
const bcc = ['xxxx@xxx']
const attachments = ['/_html/img/vtec_logo.png']
vtecxapi.sendMail(mailentry, to, cc, bcc, attachments)
これを実行するには、あらかじめpropertiesにメール送信のための設定を行う必要があります。/_settings/properties.xmlに直接記述するか管理画面の「メール|詳細設定」から設定してください。
送信元の指定方法
_mail.from={送信元アドレス}
_mail.from.personal={送信者名}
_mail.password={認証アカウントのパスワード}
_mail.transport.protocol={送信プロトコル} // "smtp"または"smtps"(Gmailはこちら)。デフォルトは"smtp"。
_mail.smtp.host={SMTPサーバ}
_mail.smtp.port={SMTPポート番号}
_mail.smtp.auth={認証する場合true(デフォルトはtrue)}
_mail.smtp.starttls.enable={STARTTLSを利用する場合true(デフォルトはtrue)}
以下はgmailで送信するための設定例です。
_mail.from=foo@gmail.com
_mail.from.personal=foo
_mail.password=xxx
_mail.transport.protocol=smtps
_mail.smtp.host=smtp.gmail.com
_mail.smtp.port=587
_mail.smtp.auth=true
認証付きリンク
送信するメール本文(テキストメールおよびHTMLメール)に以下の文字列が設定されている場合に認証トークンに変換します。例えば、メールの本文に${RXID=Key}を挿入することで、RXIDを含むURLが自動的に組み立てられます。RXIDは送信先のアカウントのものが生成されます。Keyにはコンテキストパスまで自動的に付加されるためそれ以降のパスを指定してください。
挿入文: ${RXID=/setpass.html}&value=abc
実際のメール本文: https://test.vte.cx/setpass.html?_RXID=xxx&value=abc
また、変換に際して以下のようなルールがあります。
vtecxapi.getMail(settings)
によりメールを受信することができます。受信結果はfeedのentryに格納されます。複数件受信すると複数件のentryが返ります。
feed.entryの各項目には以下のようにメールの情報がセットされます。()が対応するメールの情報
vtecxblankのmail受信サンプル(/server/getmail.js)では、/s/getmailにアクセスすると、Yahoo!メールの設定情報を元にメールを受信します。
vtecxapi.getMail(settings)
のsettingsパラメータにはメール受信のための設定を入れます。
mail受信サンプル(/server/getmail.js)
import * as vtecxapi from 'vtecxapi'
const settings: { [index: string]: string } = {}
// 基本設定(例:yahooメール)
settings['mail.pop3.host'] = 'pop.mail.yahoo.co.jp'
settings['mail.pop3.port'] = '995'
// タイムアウト設定
settings['mail.pop3.connectiontimeout'] = '60000'
//SSL関連設定
settings['mail.pop3.socketFactory.class'] = 'javax.net.ssl.SSLSocketFactory'
settings['mail.pop3.socketFactory.fallback'] = 'false'
settings['mail.pop3.socketFactory.port'] = '995'
settings['username'] = 'xxxxx@yahoo.co.jp'
settings['password'] = 'xxxxx'
const result = vtecxapi.getMail(settings)
vtecxapi.log(JSON.stringify(result))
vtecxapi.postBQ(request,async,tablenames?)
によりBiqQueryにデータを登録することができます。(※ BigQuery連携を使うには事前に設定が必要です。詳しくは、システムフォルダと各種設定を参照してください)
requestはエントリスキーマで定義されたJSONオブジェクトになります。第一階層の項目名がテーブル名に対応します。tablenamesに第一階層の項目名とテーブル名を指定することで異なる名前をテーブル名に指定することができます。tablenamesには、tablanames['{エンティティの第一階層名}']='{Bigqueryテーブル名}' を指定します。例えば、エンティティの第一階層名がfooでテーブル名がbarの場合、const tablenames = { foo : 'bar' }
になります。BigQueryのスキーマはエントリスキーマから自動的に作成されるため定義する必要はありません。
エントリスキーマ以外では、key (STRING)
、updated (DATETIME)
、deleted (BOOL)
項目が自動的に登録されます。
vtecxapi.deleteBQ(keys,async,tablenames?)
により、BigQueryのデータを削除することができます。これは論理削除であり、実際にはdeleted
がtrueのレコードが登録されます。
vtecxapi.getBQ(sql,parent)
で登録したデータをJSONとして取得できます。sqlにはBigQueryにおいて実行できるSQL文を指定してください。
また、doResponseBQcsv(sql,filename,header?)
により、csvとしてダウンロードできます。
データの更新はなく常に追記される形になるため、sqlでは同じkeyでupdatedが最新かつdeletedがfalseのものを取得するようにしてください。
以下にサンプルコードを示します。
import * as vtecxapi from 'vtecxapi'
const reqdata = {
'feed': {
'entry': [{
'foo': { 'bar': 'test', 'baz': 'テスト' },
'link':
[{ '___rel': 'self', '___href': '/footest/1' }]
}
]
}
}
vtecxapi.postBQ(reqdata,false)
// 最新のレコードのみ取得
const sql = 'select f.key,bar,baz,k.updated from my_dataset.foo as f right join (select key,max(updated) as updated from my_dataset.foo group by key) as k on f.updated=k.updated and f.key=k.key where f.deleted = false'
const result = vtecxapi.getBQ(sql)
vtecxapi.log(JSON.stringify(result))
const keys = ['/footest/1']
vtecxapi.deleteBQ(keys,true)
ちなみに、エントリスキーマ(/_settings/template)は以下の通りです。これがBigQueryのスキーマとしても使われます。(テーブル=foo、項目=bar,baz)
foo
bar
baz
サーバサイドJavaScriptをバックグラウンドで実行したい場合にはバッチジョブ機能が使えます。バッチジョブ機能は対象サービスのプロパティ(/_settings/propertiesエントリーのrights)に以下を設定することで動作させることができます。
_batchjob.{ジョブ名}={分} {時} {日} {月} {曜日} {サーバサイドJS名}
以下は毎朝8:30にメール送信するサーバサイドJS(/server/send-mail.js)を実行する設定の例です。
_batchjob.sendmail=30 8 * * * send-mail
バッチジョブ管理テーブルを参照することでバッチジョブの実行結果を確認できます。これはジョブの実行ごとに登録されるため、ジョブの実行ステータスを記録するログとして扱うことができます。
/_batchjob/{ジョブ名}/{ジョブ実行時刻(yyyyMMddHHmm)}
のエントリジョブ管理ステータス
waiting(実行待ち)のバッチジョブはプロパティの設定を削除することでキャンセルできますが、running(実行中)のバッチジョブはキャンセルできません。
対象サービスのプロパティ(/_settings/propertiesエントリーのrights)に以下を設定することで各サービスにおけるサーバサイドJSのタイムアウト設定ができます。ただしこの設定はproductionサービスのみ有効となります。stagingサービスや設定がない場合などではデフォルトの値(300秒)が採用されます。
vtecxapiのメソッドには以下のようなものがあります。
リクエスト情報取得
メソッド | 説明 |
---|---|
getRequest(): any | リクエストオブジェクト(feed.entry[0] ~ feed.entry[n])を取得する |
getPathinfo(): string | PATHINFO(リクエストURLのパス)を取得する |
getQueryString(param?: string): string | URLパラメータ(クエリストリング)を取得する |
httpmethod(): string | HTTPメソッド(GET,POST,PUT,DELETE)を取得する |
getUriAndQueryString(): string | PATHINFO+クエリストリングを取得する |
getContentType(): string | Content Typeを取得する |
getHeaders(): any | リクエストヘッダを取得する |
getCookies(): any | Cookieを取得する |
getHeaders(): any | リクエストヘッダを取得する |
uid(): number | uidを取得する |
getSettingValue(key: string): string | keyを指定してサービス設定情報を取得する |
getRemoteIP(): string | 送信元のIPアドレスを取得する |
データ操作
メソッド | 説明 |
---|---|
getEntry(url: string): any | Entryを取得する。キーとクエリパラメータを指定する |
getFeed(url: string, force?: boolean): any | Feedを取得する。キーとクエリパラメータを指定する。rightsに文字列がある場合、次ページ(nextpagelink)が存在することを示す。p={nextpagelink}で次ページを取得できる。forceがtrueで全件取得 |
count(url: string): number | 件数を取得する。キーとクエリパラメータを指定する |
post(request: any, url: string, force?: boolean): any | 親フォルダurlを指定してrequest(feed)をPOSTする。force:1000件以上登録 |
put(request: any, isbulk?: boolean, parallel?: boolean, async?: boolean): any | request(feed)をPUTする。isbulk:1000件以上、parallel:並列実行、async:非同期実行 |
deleteEntry(url: string, revision?: number): void | urlおよびrevisionのentryを削除する |
deleteFolder(url: string): any | urlとその配下のentryを削除する |
saveFiles(props: any): void | props(Map)に指定されたファイル名でアップロードファイルを保存する |
getHtml(url: string): string | 指定されたurlのHTMLを取得する |
getContent(url: string): string | 指定されたurlのコンテンツを取得する |
getCsv(header: string[], items: string[], parent: string, skip: number, encoding: string): any | アップロードされたCSVをJSONオブジェクトに変換する |
adduserByAdmin(feed: any): any | 管理者権限でユーザを追加する |
他サイトへのアクセス
メソッド | 説明 |
---|---|
urlfetch(url: string, method: string, reqData?: string, headers?:any): any | 指定されたurlに対してHTTPメソッド(method)を実行する。戻り値は{ status、headers、data }のJSON形式 |
採番
メソッド | 説明 |
---|---|
allocids(url: string, num: number): any | 指定された採番数(num)だけ採番する |
採番カウンタ操作
メソッド | 説明 |
---|---|
setids(url: string, num: number): void | urlの採番カウンタをnumにセットする |
addids(url: string, num: number): any | urlの採番カウンタの値を加算(+num)する |
rangeids(url: string, range: string): void | uriの採番カウンタに範囲を指定する(value=start-end) |
レスポンス関連
メソッド | 説明 |
---|---|
setStatus(status_code: number): void | レスポンスにステータスコードnumberをセットする |
setHeader(name: string, value: string): void | レスポンスにレスポンスヘッダをセットする |
sendRedirect(location: string): void | リダイレクトを実行する |
sendError(status_code: number, message?: string): void | ステータスコードとメッセージを送信する(HTTPプロトコル) |
sendMessage(status_code: number, message: string): void | ステータスコードとメッセージを送信する(JSON) |
doResponse(feed: any, status_code?: number): void | feed.entry[0] ~ feed.entry[n]をレスポンスする。ステータスコードstatus_codeを指定可能 |
doResponseHtml(html: string): void | htmlをレスポンスする |
doResponseCsv(value: string[], filename: string): void | csvファイルをレスポンスする |
getStatus(): number | ステータスコードを取得する |
RXID(): string | RXIDを取得する |
ログ
メソッド | 説明 |
---|---|
log(message: string, title?: string, subtitle?: string): void | ログに記録する |
PDF、XLS出力
メソッド | 説明 |
---|---|
toPdf(data: any, html: string, outfilename: string, baseurl?: string): void | PDFを出力する。baseurlに合成するPDFファイルを指定可 |
toXls(data: any, inputxls: string, outfilename: string): void | XLSを出力する |
メール送受信
メソッド | 説明 |
---|---|
sendMail(entry: any, to: string[] null, cc?: string[], bcc?: string[], attachments?: string[]): void | メールを送信する |
getMail(settings: any): any | メールを受信する |
セッション関連
メソッド | 説明 |
---|---|
setSessionFeed(name: string, feed: any): void | feedをセッションに登録する |
setSessionEntry(name: string, entry: any): void | entryをセッションに登録する |
setSessionString(name: string, str: string): void | 文字列をセッションに登録する |
setSessionLong(name: string, num: number): void | 数値をセッションに登録する |
getSessionFeed(name: string): any | セッションからfeedを取得する |
getSessionEntry(name: string): any | セッションからentryを取得する |
getSessionString(name: string): string | セッションから文字列(string)を取得する |
getSessionLong(name: string): number | セッションから数値を取得する |
deleteSessionFeed(name: string): void | セッションにあるfeedを削除する |
deleteSessionEntry(name: string): void | セッションにあるentryを削除する |
deleteSessionString(name: string): void | セッションにある文字列(string))を削除する |
deleteSessionLong(name: string): void | セッションにある数値を削除する |
incrementSession(name: string, num: number): void | セッションにある数値をnumだけ加算する |
ページネーション
メソッド | 説明 |
---|---|
pagenation(url: string, num: number): void | ページIndexを作成する |
getPage(url: string, num: number): any | num番目のページを取得する |
BigQuery連携
メソッド | 説明 |
---|---|
postBQ(request: any, async: boolean, tablenames?:any): void | BigQueryに対してデータを登録する。tablenamesを指定することで異なるテーブルに登録できる。 |
deleteBQ(keys: string[], async: boolean, tablenames?:any): void | BigQueryのデータを削除する(論理削除)。tablenamesを指定することで異なるテーブルに登録できる。 |
getBQ(sql: string,parent?: string): any | BigQueryのデータを取得する。parentを指定すると実行結果はparentの子要素になる |
doResponseBQcsv(sql: string,filename: string,header?: string):void | BigQueryのデータをcsvでダウンロードする。ファイル名やヘッダを指定できる。 |
PDFのページは以下のように3つのレベル要素により構成されます。
第一レベル要素はすべて左下点を基準とする絶対座標で指定することができます。
第二レベル要素は第一レベル要素からの相対位置座標となります。
<html>
<body>
<div class="_page">
<table>
<tr>
<td>
<table>
の子要素に<table>
を指定することはできません。<img>
<div class="_rectangle">
、<div class="_line">
<div>
、<p>
<ul>
、<ol>
<span>
<a>
<img>
<br>
<div class="_page">
タグ(以下、pageタグとします)には、ページの大きさや向き、余白サイズ、暗号化や署名などを設定できます。styleで指定できる属性には以下のものがあります。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
pagesize | ページサイズ | A4 | A0~A10, B0~B5, HAGAKI, NOTE, LEGAL, ARCH_E, ARCH_D, ARCH_C, ARCH_B, ARCH_A, FLSA, FLSE, HALFLETTER, _11X17, LEDGER のいずれか。 |
orientation | ページの向き | portrait | portrait(縦長)かlandscape(横長)のいずれか。 |
left, right, top, bottom | 左、右、上、下の余白 | 36 | 数値 |
nodata | ページ制御の際、オフセットはカウントされるがエンティティのインデックスはカウントされない。 | (なし) | "nodata"のみ記述 |
footer | ページ数表示 | false | true/false |
fontsize | ページ全体のデフォルト文字サイズ。ただし、p、span、div、chunk、vchunk、paragraph、a、liタグに囲まれた文字のみ適用される。tdタグに記述された文字列には適用されない。 | 12 | 数値 |
color | ページ全体のデフォルト文字色。ただし、p、span、div、chunk、vchunk、paragraph、a、liタグに囲まれた文字のみ適用される。tdタグに記述された文字列には適用されない。 | #000000 | #xxxxxx |
linecolor | ページ全体のデフォルト罫線色 | #000000 | #xxxxxx |
font | ページ全体のデフォルトフォント | HeiseiKakuGo-W5 | フォント名(HeiseiKakuGo-W5,HeiseiMin-W3,KozMinPro-Regularのうちいずれか ) |
title | PDF文書のタイトル | (なし) | 文字列 |
author | PDF文書の作成者 | (なし) | 文字列 |
subject | PDF文書のサブタイトル | (なし) | 文字列 |
keywords | PDF文書のキーワード | (なし) | 文字列 |
encryption | 暗号化 | (なし) | 40:「40-bit RC4」で暗号化、128:「128-bit RC4」で暗号化 パスワードおよび文書に関する制限(allowで始まる属性)を指定した場合暗号化される。このとき本項目を指定していない場合は128。 |
password | 文書を開くパスワード(PDFファイルを開く(参照する)際に入力するパスワード。) | (なし) | 文字列 |
ownerpassword | 権限パスワード(PDFファイルのセキュリティ設定を変更する際に入力するパスワード。) | (なし) | 文字列 |
allowprinting | 印刷 | true | true:印刷可、false:印刷不可 |
allowmodifycontents | 文書の変更 | true | true:文書編集可、false:文書編集不可 |
allowassembly | 文書アセンブリ ページの挿入/削除/回転、しおりとサムネールの作成の可否。 | false | true:変更可、false:変更不可 |
allowcopy | 内容のコピーと抽出 | true | true:コピー可、false:コピー不可 |
allowscreenreaders | アクセシビリティのための内容の抽出 視覚に障碍を持つユーザに対して、スクリーンリーダ(読み上げ)の利用可否。 | true | true:可、false:不可 |
allowmodifyannotations | 注釈、フォームフィールドの入力および署名 | false | true:編集可、false:編集不可 |
allowfillin | フォームフィールドの入力および署名 | false | true:入力可、false:入力不可 注)文書パスワードを付けた場合のみ有効。allowmodifyannotationsが編集可(true)の場合、trueとなる。 |
<table>
タグ<table>
タグにより表を作ることができます。これは、行(<tr>
)と列(<td>
もしくは<th>
)を子要素にもちます。style属性には以下を指定することができます。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
cols | 列数 | 1 | 数値 |
width | 幅の縮尺 | 80% | 固定の数値、またはパーセント指定 |
cellpadding | 全体の縦の間隔 | 0 | 数値 |
cellspacing | セル同士の間隔 | 0 | 数値 |
frame | 罫線 | void | void,above,below,hsides,vsides,lhs,rhs,box,border のいずれか。外枠のみの指定であり、内側の線はtdタグの属性で指定。 |
border | 枠線の幅 | 1 | 数値 |
bordercolor | 枠線の色 | ページで指定されたデフォルト罫線色 | #xxxxxx |
bgcolor | 背景色 | #FFFFFF | #xxxxxx |
align | 表示位置 | center | center, left, right のいずれか |
widths | 各列の幅(どれだけのカラムを割くか) | (なし) | 列数分、割合をカンマでつないで指定。例)width: 3,4,4; |
absolutex, absolutey | ページ内の絶対座標 | (なし) | ページ左下を基点とし、テーブルの右上角の座標を指定。 |
font | テーブルのデフォルトフォント名 | (なし) | フォント名。"$" + エンティティの項目名を指定することで、エンティティの内容を適用できる。 |
size | テーブルのデフォルト文字サイズ | (なし) | 数値 |
style | テーブルのデフォルト文字スタイル | (なし) | bold(太字),italic(斜体),underline(下線),strikethru(取消線) 複数指定の場合カンマでつなぐ。 |
color | テーブルのデフォルト文字色 | (なし) | #xxxxxx |
<tr>
タグ
style属性には以下を指定できます。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
font | 行のデフォルトフォント名 | (なし) | フォント名 |
size | 行のデフォルト文字サイズ | (なし) | 数値 |
style | 行のデフォルト文字スタイル | (なし) | bold(太字),italic(斜体),underline(下線),strikethru(取消線) 複数指定の場合カンマでつなぐ。 |
color | 行のデフォルト文字色 | (なし) | #xxxxxx |
<td>
タグ
style属性には以下を指定できます。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
align | セル内データの横方向の配置 | left | left,center,right,justifyall(均等割付) のいずれか |
valign | セル内データの縦方向の配置 | top | top,middle,bottom,baseline のいずれか |
colspan | 結合する列数 | 1 | 数値 |
rowspan | 結合する行数 | 1 | 数値 |
bordercolor | セルの枠線の色 | ページで指定されたデフォルト罫線色 | #xxxxxx |
bgcolor | セルの背景色 | #FFFFFF | #xxxxxx |
height | セルの高さ。設定値が最低限保証され、文字の折り返しなどで超える場合はこれ以上の高さとなる。 | (なし) | 数値 |
leading | 文字の改行ピッチ | 16 | セル上枠と文字下部の距離を数値で指定。 |
borderwidth | セルの罫線の太さ | 1 | 数値 |
offsetx, offsety | セル内の表示開始座標 | (なし) | セル左下を基点とし、表示内容の左下の座標を指定。 |
space | 表示文字列の文字間隔 | 0 | 数値 |
roundrighttop, roundrightbottom, roundlefttop, roundleftbottom | セルの右上、右下、左上、左下の角を丸める | false | true/false |
roundr | セルの角を丸める際の曲率 | 1 | 数値 |
lefttoprightbottom, righttopleftbottom | セルの左上から右下、右上から左下へ斜線を引く | false | true/false |
linehscale, linevscale | セルの罫線を横、縦方向に拡大・縮小 | 1 | 中心からの倍率を指定。拡大時は1より大きな数値、縮小時は0.x。 |
doubleline | セルの罫線を二重線にする | false | true/false |
left, right, top, bottom | 左、右、上、下の枠線 | false | true/false |
nowrap | 改行しない。セルに収まる分のみ表示される。 | false | true/false |
font | セルのデフォルトフォント名 | (なし) | フォント名。"$" + エンティティの項目名を指定することで、エンティティの内容を適用できる。 |
size | セルのデフォルト文字サイズ | (なし) | 数値 |
style | セルのデフォルト文字スタイル | (なし) | bold(太字),italic(斜体),underline(下線),strikethru(取消線) 複数指定の場合カンマでつなぐ。 |
color | セルのデフォルト文字色 | (なし) | #xxxxxx |
文字列を表示する場合、<p>,<div>,<span>のいずれかのタグを使用します。
上記タグで囲まなければ、デフォルトを含むレイアウト表示はされませんのでご注意ください。
<p>,<div>タグの場合、文字列表示の後に改行されます。<span>タグの場合改行されません。style属性には以下を指定できます。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
font | フォント | セル、行、テーブル、ページで指定されたデフォルトフォント | フォント名。 |
size | 文字サイズ | セル、行、テーブル、ページで指定されたデフォルト文字サイズ | 数値 |
style | 文字編集 | セル、行、テーブルで指定された文字スタイル | bold(太字),italic(斜体),underline(下線),strikethru(取消線) 複数指定の場合カンマでつなぐ。vertical:trueの場合指定しないこと。 |
color | 文字の色 | セル、行、テーブル、ページで指定されたデフォルト文字色 | #xxxxxx |
nowrap | 改行しない。セルを超えて表示される。 | false | true/false |
hscale | 文字の横幅割合 | 1 | 1を基準とした割合。縮めるなら0.x、広げるなら1より大きな数字。 |
vertical | 縦書き | false | true/false |
offsetx, offsety | セル内の文字列表示開始座標 | (なし) | セル左下を基点とし、表示内容の左下の座標を指定。 |
改行は<br/>
タグを使用します。(必ず最後にスラッシュを入れて閉じてください)
リンクは<a>
タグを使用します。href属性にリンク先URLを指定してください。
<a>
タグに囲む文字列は、<p>
,<div>
,<span>
のいずれかで囲んでください。
リストは<ul>
、<ol>
、<li>
タグを使用します。
<li>
タグに囲む文字列は、<p>
,<div>
,<span>
のいずれかで囲んでレイアウトを編集してください。シンボルは<ul>
、<ol>
タグでレイアウト編集できます。style属性には以下を指定できます。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
type | ulの場合固定文字。olの場合項目番号。 | ulの場合 - 。olの場合 1 。 | ulの場合任意の一文字。olの場合、1, A, a のいずれか。 |
start | olの場合に使用。項目番号の開始文字。 | 1, A, a のいずれか | type未指定もしくは1の場合数字。typeがA, a の場合開始したいアルファベット。 |
symbolindent | 数値(11.0ぐらいをデフォルトでセットしないと重なってしまう) | (なし) | true/false |
font | シンボルのフォント | ページで指定されたデフォルトフォント | フォント名 |
size | シンボルのフォントサイズ | ページで指定されたデフォルトフォントサイズ | 数値 |
style | シンボルのスタイル | (なし) | bold(太字),italic(斜体),underline(下線),strikethru(取消線) 複数指定の場合カンマでつなぐ。 |
color | シンボルの文字色 | ページで指定されたデフォルト文字色 | #xxxxxx |
leading | 文字の改行ピッチ | 16 | セル上枠と文字下部の距離を数値で指定。 |
画像は<img>
タグを使用します。src属性に画像のurl、width属性に画像の幅、height属性に画像の高さを指定してください。style属性には以下を指定できます。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
plainwidth | 画像の幅。widthより優先される。 | (なし) | 数値 |
plainheight | 画像の高さ。heightより優先される。 | (なし) | 数値 |
rotation | 回転 | 0 | 数値 |
absolutex, absolutey | ページ内の絶対座標 | (なし) | ページ左下を基点とし、画像の左下角の座標を指定。 |
<div class="_line">
で線を描画します。style属性には以下を指定できます。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
linewidth | 線の幅 | 1 | 数値 |
color | 線の色 | #000000 | #xxxxxx |
x1, y1 | 開始点の座標 | (なし) | 数値 |
x2, y2 | 終了点の座標 | (なし) | 数値 |
linedushon | 描画する線を破線にする場合、表示部分の長さを設定 | 1 | 数値 |
linedushoff | 描画する線を破線にする場合、非表示部分の長さを設定 | 0 | 数値 |
<div class="_rectangle">
で四角形を描画します。style属性には以下を指定できます。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
linewidth | 線の幅 | 1 | 数値 |
width | 四角形の幅 | 0 | 数値 |
height | 四角形の高さ | 0 | 数値 |
color | 四角形の線の色 | #000000 | #xxxxxx |
absolutex, absolutey | ページ内の絶対座標 | (なし) | ページ左下を基点とし、四角形の左下角の座標を指定。 |
linedushon | 描画する線を破線にする場合、表示部分の長さを設定 | 1 | 数値 |
linedushoff | 描画する線を破線にする場合、非表示部分の長さを設定 | 0 | 数値 |
<div class="_roundrectangle">
で四角形を描画します。style属性には以下を指定できます。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
linewidth | 線の幅 | 1 | 数値 |
width | 四角形の幅 | 0 | 数値 |
height | 四角形の高さ | 0 | 数値 |
roundr | 角の曲率 | 1 | 数値 |
color | 四角形の線の色 | #000000 | #xxxxxx |
absolutex, absolutey | ページ内の絶対座標 | (なし) | ページ左下を基点とし、四角形の左下角の座標を指定。 |
linedushon | 描画する線を破線にする場合、表示部分の長さを設定 | 1 | 数値 |
linedushoff | 描画する線を破線にする場合、非表示部分の長さを設定 | 0 | 数値 |
<div class="_circle">
で円を描画します。style属性には以下を指定できます。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
linewidth | 線の幅 | 1 | 数値 |
absolutex, absolutey | ページ内の絶対座標 | (なし) | ページ左下を基点とし、円の左下角の座標を指定。 |
radius | 円の半径 | (なし) | 数値 |
linedushon | 描画する線を破線にする場合、表示部分の長さを設定 | 1 | 数値 |
linedushoff | 描画する線を破線にする場合、非表示部分の長さを設定 | 0 | 数値 |
color | 円の線の色 | #000000 | #xxxxxx |
<div class="_barcodeEAN">
でJAN(EAN、UPC)規格のバーコードを描画します。style属性には以下を指定できます。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
value | バーコードに表示する値 | 4512345678901 | 数字 |
height | 高さ | 30 | 数値 |
size | 文字のサイズ | 10 | 数値 |
width | 幅 | 0.75 | 数値 |
font | フォント名 | (なし) | 文字を表示しない場合、"null"を指定する。 |
<div class="_barcodeNW7">
でNW-7(CODABAR)規格のバーコードを描画します。style属性には以下を指定できます。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
value | バーコードに表示する値 | 4512345678901 | 文字列 |
height | 高さ | 30 | 数値 |
size | 文字のサイズ | 10 | 数値 |
width | 幅 | 0.75 | 数値 |
startstop | スタートストップ文字の有無 | true | true/false |
font | フォント名 | (なし) | 文字を表示しない場合、"null"を指定する。 |
<div class="_barcode39">
でcode39規格のバーコードを描画します。style属性には以下を指定できます。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
value | バーコードに表示する値 | 4512345678901 | 文字列 |
height | 高さ | 30 | 数値 |
size | 文字のサイズ | 10 | 数値 |
width | 幅 | 0.75 | 数値 |
startstop | スタートストップ文字の有無 | true | true/false |
extended | 拡張 | true | true/false |
font | フォント名 | (なし) | 文字を表示しない場合、"null"を指定する。 |
<div class="_barcode128">
でcode128規格のバーコードを描画します。style属性には以下を指定できます。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
value | バーコードに表示する値 | 4512345678901 | 文字列 |
height | 高さ | 30 | 数値 |
size | 文字のサイズ | 10 | 数値 |
width | 幅 | 0.75 | 数値 |
codetype | コードタイプ | (なし) | "UCC"(CODE128_UCC規格)または"RAW"(CODE128_RAW規格)を指定。それ以外はCODE128規格。 |
font | フォント名 | (なし) | 文字を表示しない場合、"null"を指定する。 |
<div class="_qrcode">
でQRコードを描画します。style属性には以下を指定できます。
プロパティ | 内容 | 初期値 | 指定方法 |
---|---|---|---|
value | バーコードに表示する値 | 4512345678901 | 文字列 |
height | 高さ | 30 | 数値 |
width | 幅 | 0.75 | 数値 |
version | 型番(シンボルの大きさ) | 0 | 0~10を指定 |
errorcorrectionlevel | 誤り訂正レベル | "H" | "L"(コード語の約7%を復元可能), "M"(15%), "Q"(25%), "H"(30%)のいずれかを指定 |
cellsize | セルのサイズ(pixel) | 1 | 1~4を指定 |
margin | 余白(pixel) | 0 | 0~32を指定 |
ステータスコード | メッセージ | 意味 | 解説 |
---|---|---|---|
200 | OK. | 成功 | 処理が正しく実行された |
201 | Created. | 生成 | リソースが新しく生成された |
202 | Accepted. | 受付 | async処理の場合、またはdeleteserviceの戻り値 |
204 | No entry. | コンテンツなし | リソースがなかった |
206 | Partial Content. | 部分的内容 | 返された結果が一部である |
400 | Request object is invalid. | リクエスト不正 | 不正なリクエストが送信された |
401 | Authentication error. | 認証エラー | 認証に失敗 |
403 | Access denied. | 認可エラー | 認証は成功しているが認可でエラー |
404 | No entry. | 不存在 | コンテンツが見つからなかった |
405 | Method Not Allowed. | 不許可 | productionサービスにおいてhttps以外でアクセスしてきた場合。またはstagingサービスにおいてhttp以外でアクセスしてきた場合 |
406 | Authentication time out. | 認証タイムアウト | 認証タイムアウトが発生した |
409 | Conflict | 競合発生 | キー重複、もしくは楽観的ロック失敗 |
412 | The signature is invalid. | 署名検証エラー | 署名の検証において失敗した(署名が不正) |
413 | Payload Too Large. | リクエストサイズエラー | リクエストデータのサイズ超過 |
417 | Request security error. | リクエストセキュリティエラー | XMLHttpRequestからのリクエストではないがJSONで出力しようとしている |
424 | Not in service. | サービスを利用不可 | サービスを利用できないかサーバサイドJavaScriptでエラーが発生した |
426 | Upgrade Required. | 設定不正 | サービスでの設定内容が不正 |
500 | INTERNAL_SERVER_ERROR | 内部サーバーエラー | サーバにおける致命的エラー |
メッセージ | 意味 |
---|---|
XX is required. | XXの指定が必要 |
XX is not available. | XXが使えない |
XX does not exist. | XXが存在しない |
XX is invalid. | XXが不正 |
Allocate id must be a numeric value. | 正しい数値がIDに指定されていない |
Callback strings must use alphanumeric characters. | callbackには英数字以外使用不可 |
Duplicated Link self. | Link selfが重複している |
Duplicated rules for ACLs. | 既に同じ権限が設定されている |
Duplicated URIs for | URIが重複している |
Forbidden request to this service. | このサービスへの許可されないリクエスト |
Max must be greater than min. | 最大値が最小値より大きい値ではない |
Must specify a 'E'(External) control. | E権限を指定する必要がある |
Not allowed to cancel the process. | プロセスをキャンセルできない |
Not allowed to use an alias for bulkcopy. | bulkcopyではaliasは使えない |
Optimistic locking failed for the specified template. | 指定したテンプレートの更新エラー(楽観的排他エラー)が発生 |
Password must be contain at least 8 characters, including at least 1 number and includes both lower and uppercase letters. | passwordは1文字以上で数字と小文字と大文字混じりである必要がある |
Request format is invalid: XX | リクエストのフォーマットが正しくセットされていない |
Revision number must be a numeric value. | リビジョンが数字ではない |
Specified value is out of range. | リビジョンの値が範囲外 |
Specified URI does not match the id nor key. | 指定したURIがIDとKeyに一致しない |
The first limit must be less than limit(XX). | first limitはlimit(XX)以下を指定しなければならない |
The first limit must be more than 0. | first limitは0以上を指定しなければならない |
The number of pages must be more than 0. | number of pagesは0以上を指定しなければならない |
Too many entities. | entityの数が多すぎる |
Unauthorized request to modify the auth. | Authの変更リクエストは受け付けられない |
URI must not contain any prohibited characters. | URIに許可していない文字の使用は不可 |
URI must not contain any white-space characters. | URIにblank文字は使えない |
URI must start with a slash. | URIは/から始まるものでなければならない |
Accesskey and Accesstoken can not be used. | AccesskeyとAccesstokenが使えない |
Please set only one key. | selfは1エントリ1件のみ |
Please make a pagination index in advance. | 先にpagination indexを作成する必要がある |
Session is disabled. | セッションが無効になっている |
Session does not exist. | セッションが存在しない |
Top entry can not be specified. | ルートエントリは指定不可 |
Forbidden request to this service. | このサービスで実行できない |
Service init entry is nothing. | サービス初期化エントリが存在しない |
.js' is not found. | .jsファイルが見つからない |
Service does not exist. | サービスが存在しない |
The Web Application has not been activated. | Webアプリケーションが有効になっていない |
メッセージ | 意味 |
---|---|
Authentication is locked. | 認証がロックされている |
Authentication time out. | 認証タイムアウト |
Remote access is not allowed. | リモートからのアクセスは禁止 |
Captcha required at next login. | 次回からCaptcha認証が必要 |
メッセージ | 意味 |
---|---|
Duplicated primary key. | Keyが重複している |
Alias is duplicated. | aliasが重複している |
User is already registered. | ユーザが既に登録されている |
Optimistic locking failed. | 更新エラー(楽観的排他エラー) |