Ratpack入門シリーズ
- Ratpack入門 (1) - Ratpackとは
- Ratpack入門 (2) - アーキテクチャー
- Ratpack入門 (3) - hello world 詳解
- Ratpack入門 (4) - ルーティング & 静的コンテンツ
- Ratpack入門 (5) - Json & Registry
- Ratpack入門 (6) - Promise
- Ratpack入門 (7) - Guice & Spring
- Ratpack入門 (8) - セッション
- Ratpack入門 (9) - Thymeleaf
ルーティング
前述したように、ルーティングはChain
クラスに各パスとマッピングするハンドラーを記述していく形で行います。
正式なドキュメントはChainのJavadocにありますので、ここで解説しきれない分はそちらを参照してください。
Handlerを追加するための各メソッド
Chain
における一番基本的なメソッドはall(Handler)
であり、これは全てのリクエストがこのハンドラーによって処理されることを示します。他の各メソッドは、実はこのメソッドに「リクエストを受け付ける条件」を追加した、コンビニエンスメソッドにすぎません。
内部的にはHandlers
の各staticユーティリティーメソッドで、渡されたHandler
をラップすることで処理しています。
Handler
のインスタンス自体ではなくClass<Handler>
を引数に取るものは、コンテキストのRegistry
から当該クラスを使用します。
あまり意味があるように思わないかもしれませんが、GuiceなどのDIコンテナと組み合わせるときに威力を発揮します。
以下、主要なメソッドを解説します(量が多いのですべては無理)。
メソッド | ハンドラーが呼び出される条件 |
---|---|
all(Handler) |
必ず |
files() |
後述 |
onlyIf(Predicate<Context>, Handler) |
Predicate がtrue になるとき |
when(boolean/Predicate, Action<Chain>) |
ブール値またはPredicate がtrue のとき、Action<Chain> に委譲 |
prefix(String, Action<Chain>) |
パスが指定文字列から始まるとき、相対パスで解決されるAction<Chain> に委譲 |
path(Handler) |
リクエストが指定のパスにマッチするとき |
get(String, Handler) |
指定のパスにマッチし、GETメソッドが呼ばれたとき |
post(String, Handler) |
指定のパスにマッチし、POSTメソッドが呼ばれたとき |
patch(String, Handler) |
指定のパスにマッチし、PATCHメソッドが呼ばれたとき |
put(String, Handler) |
指定のパスにマッチし、PUTメソッドが呼ばれたとき |
delete(String, Handler) |
指定のパスにマッチし、DELETEメソッドが呼ばれたとき |
chain.prefix( "hoge", innerChain -> {
innerChain.get( "piyo", handler ); // /hoge/piyo
} );
そのほか、Context
にはbyMethod()
およびbyContent
というコンビニエンスメソッドが用意されており、イディオマティックに分岐を記述できます。
// パス"hoge"がリクエストされたとき、GETとPOSTで分岐させる
chain.path( "hoge", ctx -> {
ctx.byMethod( m -> {
m.get( iCtx -> iCtx.render( "get" ) );
m.post( iCtx -> iCtx.render( "post" ) );
} );
} );
// "hoge"リクエストの、`Accept`ヘッダーの値で分岐する
chain.path( "hoge", ctx -> ctx.byContent( accept -> {
accept.json( iCtx -> iCtx.render("json") );
accept.xml( iCtx -> iCtx.render( "xml" ) );
accept.noMatch( iCtx -> iCtx.render( "no match" ) );
} ) );
ちなみにここでは変数名をベタ書きしていますが、メソッドチェーンで記述することもできます。
パスのリテラル
パスに指定する文字列には、正規表現などのリテラルが使え、パスパラメーターの設定なども可能です。
種類 | シンタックス | 例 |
---|---|---|
リテラル | foo |
"foo" |
正規表現リテラル | ::<<regex>> |
"foo/::\d+" |
オプションのパストークン | :<<token-name>>? |
"foo/:val?" |
必須パストークン | :<<token-name>> |
"foo/:val" |
オプションの正規表現パストークン | :<<token-name>>?:<<regex>> |
"foo/:val?:\d+" |
必須正規表現パストークン | :<<token-name>>:<<regex>> |
"foo/:val:\d+" |
(Javadocより翻訳)
パスパラメーターは、Context.getPathToken()
で取得できるPathTokens
クラス経由で取得できます。
chain.get( "hoge/:name", ctx -> ctx.render( "name: " + ctx.getPathTokens().get( "name" ) ) );
chain.get( "piyo/:id:\\d+", ctx -> ctx.render( "id: " + ctx.getPathTokens().asLong( "id" ) ) );
パス | 結果 |
---|---|
/hoge | 404 |
/hoge/John | "name: John" |
/piyo/hoge | 404 |
/piyo/123 | "id: 123" |
Context
の動作
Context
クラスは、ある一つのリクエストがどのように呼び出されているかの情報を保持しているクラスです。
Hello worldで見たように、Handler
はContext
を受け取るラムダ式です。従って、一連の処理の流れは、
Contextを受け取って -> いろいろな処理を行い -> Context.render()を呼び出し、レスポンスを作成する
という形になります。
クエリパラメーター
URLの?
から後の部分のやつです。
getRequest().getQueryParams()
から、マップとして取得できます。
パスパラメーター
上述したように、getPathToken()
を使用します。
FORMパラメーター
HTTPの<form>
タグなどから送信されるフォームパラメーターは、parse()
メソッドを使用して、Form
クラス経由で取得します。
chain.path( "hoge", ctx -> ctx.parse( Form.class ).then( form -> {
ctx.render( "Received: " + form.entrySet() );
} ) );
parse()
については後述します。
リクエストのボディー
getRequest().getBody()
から取得できます。
ここで注意するべきなのは、getBody()
メソッドはボディーの内容を返すのではなく、ボディーのPromise
を返す点です。
Promise
の使い方は今後の投稿で説明予定です。
ヘッダー
header(CharSequence)
もしくはgetRequest().getHeaders()
から取得できるHeaders
クラス経由で取得します。
クッキー
Request.getCookies()
で取得できます。
レスポンスを返す
レスポンスの設定は、getResponse()
からResponse
クラスを取得し、そのインスタンスに設定していく形で行います。実際の送信は、Context.render(Object)
や、Response.send()
で行います。
ステータスコード
status(int)
メソッドで指定できます。
クッキー
Cookie
クラスの作成はcookie()
メソッドの呼び出しで行われます。返されるCookie
オブジェクトを、あらためてgetCookies()
メソッドで返されるセットに追加する必要はありません。
ヘッダー
Response.getHeaders()
で、ヘッダーの可変セットが取得できます。contentType()
など、コンビニエンスメソッドも用意されています。
Context.redirect(String)
302リダイレクトを発生させ、指定のパスに遷移させます。
Context.clientError(int)
ステータスコード400番台のエラーを発生させたいときに使用します。
404に関しては、コンビニエンスメソッドnotFound()
が用意されています。
Context.render(Object)
レスポンスを返す最も基本的なメソッドです。
BaseDir
静的ファイルを提供するために、RatpackはBaseDir
という仕組みを提供しています。
まず、ServerConfig
にBaseDirのPath
を設定する必要があります。普通にJDKのPath
を作成してもよいのですが、クラスローダーからリソースを読み込むためのBaseDir.find()
メソッドが用意されています。ServerConfigBuilder.findBaseDir()
というコンビニエンスメソッドもあります。find()
は、指定されたパスにある.ratpack
という空のファイルを探します。見つかった場合、そのパスを内部的な基準となるパスとして、静的コンテンツの提供を行います。
次に、BaseDirからファイルを提供するハンドラーを作る必要があります。Chain.files()
メソッドは、上記で設定したBaseDirから、相対パスとして解決されたURLにリクエストがあった時、そのファイルの内容をレスポンスとして送信します。
具体例です。ここではリソース内にpublic
というフォルダを作り、そこをBaseDirに設定してみます。
リソースとして下記を作成します。
resources/public/.ratpack
resources/public/index.html
Path baseDir = BaseDir.find( "public/.ratpack" );
ServerConfig config = ServerConfig.builder()
.baseDir( baseDir )
.development( true ).build();
Action<Chain> handlers = chain -> {
chain.files();
};
RatpackServer.start( server -> server
.serverConfig( config )
.handlers( handlers ) );
http://localhost:5050/index.html
にアクセスすると、作成したHTMLファイルの内容が表示されるはずです。
独自のパスにファイルを結び付けたい場合は、Context.render()
メソッドにPath
を渡します。BaseDirからファイルを取得するために、Context.file()
メソッドを利用できます。
上記の例のハンドラーを、以下のように変更してみてください。
Action<Chain> handlers = chain -> {
chain.files();
chain.get( "hoge", ctx -> ctx.render( ctx.file( "index.html" ) ) );
};
http://localhost:5050/hoge
にアクセスすると、index.html
の内容が表示されます。