Docsの目次

HTTPとHTML

目次 · 前:入門ガイド · 次:SQLite

Nagiでは、@getなどを付けたasync関数がHTTPの入口になります。classを返すとJSON応答、Htmlを返すとHTML応答です。まずデータを保存しない小さなサーバーから動かします。

このページの目次

1. サーバーを書く

次の完全なコードを、作業フォルダーにhttp.nagiとして保存してください。サンプルにもあります。

class Greeting:
    id: i64
    name: str

@get("/")
async def home() -> Result[Html, Error]:
    return ok(html("<!doctype html><html lang=\"ja\"><meta charset=\"utf-8\"><title>Nagi</title><h1>Hello, Nagi!</h1><a href=\"/greet/1\">JSONを見る</a></html>"))

@get("/greet/{id}")
async def greet(id: i64) -> Result[Greeting, Error]:
    if id < 1:
        return error("id must be positive")
    return ok(Greeting(id=id, name="Nagi"))

@post("/echo")
async def echo(req: Greeting) -> Result[Greeting, Error]:
    return ok(req)

async def main() -> Result[unit, Error]:
    db = try await db_open(":memory:")
    return await serve(db, 8094)

serveの現在のAPIにはDbが必要です。この例ではメモリ内SQLiteを開きますが、テーブルの作成や書き込みはしていません。serveはサーバーを起動し、リクエストを待ち続けます。

2. 起動して呼び出す

8094番ポートを空けて、保存したフォルダーで実行します。

nagic run http.nagi

Windows・Linux・macOSで同じコマンドです。ブラウザーでhttp://127.0.0.1:8094/を開くと「Hello, Nagi!」を表示します。

別のPowerShellでJSON APIを呼びます。

Invoke-RestMethod http://127.0.0.1:8094/greet/7
Invoke-RestMethod http://127.0.0.1:8094/echo -Method Post -ContentType 'application/json' -Body '{"id":2,"name":"sample"}'

Linux / WSL2では次のコマンドです。

curl http://127.0.0.1:8094/greet/7
curl -H 'Content-Type: application/json' -d '{"id":2,"name":"sample"}' http://127.0.0.1:8094/echo

GETの応答は{"id":7,"name":"Nagi"}、POSTは送った{"id":2,"name":"sample"}です。/greet/0は400のJSONエラーになります。終了は起動したターミナルのCtrl+Cです。

3. 引数と応答を決める

書き方 HTTPでの意味
@get("/greet/{id}")と引数id: i64 URLの{id}を整数として読む
引数req: Greeting requestのJSON bodyをclassへ読む
引数db: Db serveに渡したDBをhandlerへ供給する
pathにないprimitive引数 query parameterから読む。例はcrud.nagiの/query
引数body: view[bytes] request bodyを借用byte列として読む
Result[Greeting, Error]とok(...) 成功時にJSON応答
Result[Greeting?, Error]とok(None) 対象なしを404にする
Result[Html, Error]とok(html(...)) 成功時にtext/html応答
error("理由") 入力エラーとして400のJSON応答
not_found("理由") 対象なしとして404のJSON応答
internal_error("理由") 詳細を伏せた500のJSON応答
fail(problem) Errorの種類を保った応答。DBエラーなら500

HTTP handlerはasync defで定義し、Result[..., Error]を返します。属性には@get、@post、@put、@deleteがあります。JSONのfield欠落、型の違い、不明fieldなどは入力エラーです。

match await operation(...)で失敗を分け、既定値を返して回復することもできます。Result APIサンプルでは、入力不正の400、対象なしの404、DB失敗の500、代替データを返す200を実HTTPで確認できます。

4. HTMLを別ファイルにする

HTMLが長くなったら、.nagiと同じディレクトリにindex.htmlを置き、handlerの本体を次のようにします。

@get("/")
async def home() -> Result[Html, Error]:
    return ok(html(include_text("index.html")))

include_textはコンパイル時にHTMLを実行ファイルへ埋め込みます。HTMLを変更したら再ビルドしてください。配布先にはHTMLファイルを置く必要がありません。

画面のJavaScriptからfetch("/api/tasks")のように同じサーバーを呼べます。追加・編集・削除とSQLiteを組み合わせた完成例はタスク管理デモです。

現在のサーバーの範囲

Highの属性からAxumのroutingを生成します。HTTP/1.1、keep-alive、path parameter、型付きquery parameter、request body、JSON responseを実装しています。

標準の試験用endpointは/health、5chunkの/stream、echo WebSocketの/wsです。middlewareでrequest処理を2秒に制限し、bodyとWebSocket messageの上限を1 MiBにしています。DBや内部エラーは500などに変換し、詳細をresponseへ出しません。

この3つのGETは組み込み用です。同じGETを定義するとcheckでエラーになります。capture名だけ違うpath(/items/{id}と/items/{key}など)も競合するため、GETとPOSTを分ける場合もcapture名を揃えてください。

HTTPの待機期限は既定で10秒です。接続直後の無通信、途中のヘッダー、応答後から次のヘッダーが完成するまでが対象です。少量ずつ送信しても期限は延びません。期限を過ぎた接続は閉じられるため、クライアントは必要に応じて再接続してください。処理中の応答、ストリーム、アップグレード後のWebSocketには、この待機期限を適用しません。

変更する場合は、起動前に環境変数NAGI_HTTP_REQUEST_WAIT_SECONDSへ正の整数を指定します。例えばPowerShellでは$env:NAGI_HTTP_REQUEST_WAIT_SECONDS = "30"、bashではexport NAGI_HTTP_REQUEST_WAIT_SECONDS=30です。ヘッダーと未使用keep-aliveの期限は共通です。同時接続数を128に固定する制限はありません。

現在はloopback専用です。HTTP/2、TLS、認証、任意middlewareのHigh宣言、deploymentの仕組みは未実装です。大きいclassのJSON streamingや汎用のHigh streaming構文もありません。JSON responseはclassをVec<u8>へencodeしてBodyへ渡します。

Nagi 0.1のドキュメントこのページのソース