ルーティング

ルーティング

ウェブサイトやウェブアプリケーションのユーザーは、//about、または/posts/1など、さまざまなURLにアクセスできます。これらのURLを機能させるには、ルートを定義する必要があります。

AdonisJSでは、ルートはstart/routes.tsファイル内で定義されます。ルートは、URIパターンとその特定のルートのリクエストを処理するハンドラの組み合わせです。

例:

import router from '@adonisjs/core/services/router'
router.get('/', () => {
return 'ホームページからこんにちは世界。'
})
router.get('/about', () => {
return 'これはaboutページです。'
})
router.get('/posts/:id', ({ params }) => {
return `この投稿のIDは${params.id}です。`
})

上記の例の最後のルートでは、動的なURIパターンが使用されています。:idは、ルーターにIDの任意の値を受け入れるように指示する方法です。これをルートパラメータと呼びます。

登録されたルートの一覧を表示する

list:routesコマンドを実行すると、アプリケーションによって登録されたルートの一覧を表示できます。

node ace list:routes

また、公式のVSCode拡張機能を使用している場合、VSCodeのアクティビティバーからルートの一覧を表示することもできます。

ルートパラメータ

ルートパラメータを使用すると、動的な値を受け入れることができるURIを定義できます。各パラメータはURIセグメントの値をキャプチャし、ルートハンドラ内でこの値にアクセスすることができます。

ルートパラメータは常にコロン:で始まり、その後にパラメータの名前が続きます。

import router from '@adonisjs/core/services/router'
router.get('/posts/:id', ({ params }) => {
return params.id
})
URLId
/posts/11
/posts/100100
/posts/foo-barfoo-bar

URIは複数のパラメータも受け入れることができます。各パラメータは一意の名前を持つ必要があります。

import router from '@adonisjs/core/services/router'
router.get('/posts/:id/comments/:commentId', ({ params }) => {
console.log(params.id)
console.log(params.commentId)
})
URLIdComment Id
/posts/1/comments/414
/posts/foo-bar/comments/22foo-bar22

オプションのパラメータ

ルートパラメータは、パラメータ名の末尾に疑問符?を追加することでオプションにすることもできます。オプションのパラメータは必須のパラメータの後に配置する必要があります。

import router from '@adonisjs/core/services/router'
router.get('/posts/:id?', ({ params }) => {
if (!params.id) {
return 'すべての投稿を表示しています。'
}
return `IDが${params.id}の投稿を表示しています。`
})

ワイルドカードパラメータ

ワイルドカードパラメータを使用すると、URIのすべてのセグメントをキャプチャできます。ワイルドカードパラメータは特別な*キーワードを使用して指定され、最後の位置で定義する必要があります。

import router from '@adonisjs/core/services/router'
router.get('/docs/:category/*', ({ params }) => {
console.log(params.category)
console.log(params['*'])
})
URLCategoryWildcard param
/docs/http/contexthttp['context']
/docs/api/sql/ormapi['sql', 'orm']

パラメータのマッチャー

ルーターは、受け入れるパラメータデータの形式を把握していません。例えば、URIが/posts/foo-bar/posts/1の場合、同じルートにマッチします。ただし、パラメータの値を明示的に検証するために、パラメータマッチャーを使用することができます。

マッチャーはroute.whereメソッドを使用して登録されます。最初の引数はパラメータ名であり、2番目の引数はマッチャーオブジェクトです。

次の例では、idが有効な数値であることを検証するための正規表現を定義しています。検証に失敗した場合、ルートはスキップされます。

import router from '@adonisjs/core/services/router'
router
.get('/posts/:id', ({ params }) => {})
.where('id', {
match: /^[0-9]+$/,
})

match正規表現の他に、パラメータの値を正しいデータ型に変換するためのcast関数を定義することもできます。この例では、idを数値に変換しています。

import router from '@adonisjs/core/services/router'
router
.get('/posts/:id', ({ params }) => {
console.log(typeof params.id)
})
.where('id', {
match: /^[0-9]+$/,
cast: (value) => Number(value),
})

組み込みのマッチャー

ルーターには、よく使用されるデータ型に対して以下のヘルパーメソッドが用意されています。

import router from '@adonisjs/core/services/router'
// idを数値として検証 + 数値データ型にキャスト
router.where('id', router.matchers.number())
// idが有効なUUIDであることを検証
router.where('id', router.matchers.uuid())
// slugが指定されたスラグの正規表現に一致することを検証: regexr.com/64su0
router.where('slug', router.matchers.slug())

グローバルなマッチャー

ルートマッチャーは、ルーターインスタンスでグローバルに定義することもできます。ルートレベルで明示的にオーバーライドされない限り、グローバルマッチャーはすべてのルートに適用されます。

import router from '@adonisjs/core/services/router'
// グローバルマッチャー
router.where('id', router.matchers.uuid())
router
.get('/posts/:id', () => {})
// ルートレベルでオーバーライド
.where('id', router.matchers.number())

HTTPメソッド

router.getメソッドは、GET HTTPメソッドに応答するルートを作成します。同様に、異なるHTTPメソッドのためのルートを登録するために以下のメソッドを使用できます。

// GETメソッド
router.get('users', () => {})
// POSTメソッド
router.post('users', () => {})
// PUTメソッド
router.put('users/:id', () => {})
// PATCHメソッド
router.patch('users/:id', () => {})
// DELETEメソッド
router.delete('users/:id', () => {})

すべての標準的なHTTPメソッドに応答するルートを作成するには、route.anyメソッドを使用できます。

router.any('reports', () => {})

最後に、route.routeメソッドを使用してカスタムHTTPメソッド用のルートを作成することもできます。

router.route('/', ['TRACE'], () => {})

ルーターハンドラ

ルートハンドラは、レスポンスを返すか例外を発生させることでリクエストを処理します。

ハンドラは、インラインのコールバック(このガイドで見たようなもの)またはコントローラメソッドへの参照であることができます。

router.post('users', async () => {
})

次の例では、UsersControllerクラスをインポートし、ルートにバインドしています。HTTPリクエスト時、AdonisJSはIoCコンテナを使用してコントローラクラスのインスタンスを作成し、storeメソッドを実行します。

参照:コントローラに関する専用ガイドも参照してください。

import UsersController from '#controllers/users_controller'
router.post('users', [UsersController, 'store'])

ルートミドルウェア

route.useメソッドを呼び出すことで、ルートにミドルウェアを定義できます。メソッドはインラインのコールバックまたは名前付きミドルウェアへの参照を受け入れます。

以下は、ルートミドルウェアを定義する最小の例です。すべての利用可能なオプションとミドルウェアの実行フローについては、ミドルウェアの専用ガイドを参照してください。

router
.get('posts', () => {
console.log('ルートハンドラ内')
return 'すべての投稿を表示しています。'
})
.use((_, next) => {
console.log('ミドルウェア内')
return next()
})

ルート識別子

すべてのルートには一意の識別子があり、この識別子を使用してアプリケーションの他の場所でルートを参照できます。たとえば、URLビルダーを使用してルートへのURLを生成したり、response.redirectメソッドを使用してルートにリダイレクトしたりできます。

デフォルトでは、ルートパターンがルート識別子です。ただし、route.asメソッドを使用して一意で覚えやすい名前をルートに割り当てることもできます。

router.get('users', () => {}).as('users.index')
router.post('users', () => {}).as('users.store')
router.delete('users/:id', () => {}).as('users.delete')

これで、テンプレート内でルート名を使用してURLを構築したり、URLビルダーを使用してURLを作成したりできます。

const url = router.builder().make('users.delete', [user.id])
<form
method='POST'
action="{{
route('users.delete', [user.id], { formAction: 'delete' })
}}"
></form>

ルートグループ

ルートグループは、ネストされたルートを一括で設定するための便利な機能を提供します。router.groupメソッドを使用してルートグループを作成できます。

router.group(() => {
/**
* コールバック内で登録されたすべてのルートは、周囲のグループの一部です
*/
router.get('users', () => {})
router.post('users', () => {})
})

ルートグループはネストすることもでき、AdonisJSは適用された設定の動作に基づいてプロパティをマージまたはオーバーライドします。

router.group(() => {
router.get('posts', () => {})
router.group(() => {
router.get('users', () => {})
})
})

ルートグループ内のプレフィックス

グループ内のルートのURIパターンにプレフィックスを付けることができます。次の例では、/api/usersおよび/api/paymentsのURIパターンのルートが作成されます。

router
.group(() => {
router.get('users', () => {})
router.get('payments', () => {})
})
.prefix('/api')

ネストされたグループの場合、プレフィックスは外側から内側のグループに適用されます。次の例では、/api/v1/usersおよび/api/v1/paymentsのURIパターンのルートが作成されます。

router
.group(() => {
router
.group(() => {
router.get('users', () => {})
router.get('payments', () => {})
})
.prefix('v1')
})
.prefix('api')

グループ内のルートの名前付け

ルートパターンにプレフィックスを付けるだけでなく、group.asメソッドを使用してグループ内のルートの名前にもプレフィックスを付けることができます。

グループ内のルートには、プレフィックスを付ける前に名前を付ける必要があります。

router
.group(() => {
route
.get('users', () => {})
.as('users.index') // 最終的な名前 - api.users.index
})
.prefix('api')
.as('api')

ネストされたグループの場合、名前は外側から内側のグループにプレフィックスが付けられます。

router
.group(() => {
route
.get('users', () => {})
.as('users.index') // api.users.index
router
.group(() => {
route
.get('payments', () => {})
.as('payments.index') // api.commerce.payments.index
})
.as('commerce')
})
.prefix('api')
.as('api')

グループ内のルートにミドルウェアを適用する

group.useメソッドを使用して、グループ内のルートにミドルウェアを割り当てることができます。グループのミドルウェアは、グループ内の個々のルートに適用されるミドルウェアよりも先に実行されます。

ネストされたグループの場合、もっとも外側のグループのミドルウェアが最初に実行されます。つまり、グループのミドルウェアはルートのミドルウェアスタックに先行します。

参照:ミドルウェアガイドも参照してください。

router
.group(() => {
router
.get('posts', () => {})
.use((_, next) => {
console.log('ルートミドルウェアからのログ')
return next()
})
})
.use((_, next) => {
console.log('グループミドルウェアからのログ')
return next()
})

特定のドメインに対してルートを登録する

AdonisJSでは、特定のドメイン名の下にルートを登録できます。これは、アプリケーションが複数のドメインにマップされ、各ドメインに異なるルートが必要な場合に便利です。

次の例では、2つのセットのルートを定義しています。

  • 任意のドメイン/ホスト名に対して解決されるルート。
  • ドメイン/ホスト名が事前に定義されたドメイン名の値と一致する場合に一致するルート。
router.group(() => {
router.get('/users', () => {})
router.get('/payments', () => {})
})
router.group(() => {
router.get('/articles', () => {})
router.get('/articles/:id', () => {})
}).domain('blog.adonisjs.com')

アプリケーションをデプロイすると、明示的なドメインを持つグループ内のルートは、リクエストのホスト名が blog.adonisjs.com の場合にのみ一致します。

ダイナミックなサブドメイン

group.domainメソッドを使用して、ダイナミックなサブドメインを指定できます。ルートパラメータと同様に、ドメインのダイナミックセグメントはコロン : で始まります。

次の例では、tenantセグメントが任意のサブドメインを受け入れるように定義されており、HttpContext.subdomainsオブジェクトを使用してその値にアクセスできます。

router
.group(() => {
router.get('users', ({ subdomains }) => {
return `${subdomains.tenant}のユーザーをリスト表示しています。`
})
})
.domain(':tenant.adonisjs.com')

ルートからビューをレンダリングする

ルートハンドラがビューをレンダリングするだけの場合、router.on.renderメソッドを使用できます。これは、明示的なハンドラを定義せずにビューをレンダリングするための便利なショートカットです。

レンダリングメソッドは、レンダリングするエッジテンプレートの名前を受け入れます。オプションでテンプレートデータを第二引数として渡すこともできます。

route.on.renderメソッドは、Edgeサービスプロバイダを設定している場合にのみ存在します。

import router from '@adonisjs/core/services/router'
router.on('/').render('home')
router.on('about').render('about', { title: 'About us' })
router.on('contact').render('contact', { title: 'Contact us' })

ルートからリダイレクトする

リクエストを別のパスやルートにリダイレクトするためのルートハンドラを定義する場合、router.on.redirectまたはrouter.on.redirectToPathメソッドを使用できます。

redirectメソッドはルート識別子を受け入れます。一方、redirectToPathメソッドは静的なパス/URLを受け入れます。

import router from '@adonisjs/core/services/router'
// ルートにリダイレクト
router.on('/posts').redirect('/articles')
// URLにリダイレクト
router.on('/posts').redirectToPath('https://medium.com/my-blog')

パラメータの転送

次の例では、元のリクエストのidの値を使用して/articles/:idルートを構築します。したがって、/posts/20のリクエストは/articles/20にリダイレクトされます。

import router from '@adonisjs/core/services/router'
router.on('/posts/:id').redirect('/articles/:id')

パラメータを明示的に指定する

第二引数としてルートパラメータを明示的に指定することもできます。この場合、現在のリクエストのパラメータは無視されます。

import router from '@adonisjs/core/services/router'
// 常に/ articles / 1にリダイレクトする
router.on('/posts/:id').redirect('/articles/:id', {
id: 1
})

クエリ文字列を含める

リダイレクトURLのクエリ文字列は、オプションオブジェクト内で定義できます。

import router from '@adonisjs/core/services/router'
router.on('/posts').redirect('/articles', {
qs: {
limit: 20,
page: 1,
}
})

現在のリクエストのルート

現在のリクエストのルートは、HttpContext.routeプロパティを使用してアクセスできます。これには、ルートパターン名前ミドルウェアストアへの参照、およびルートハンドラへの参照が含まれます。

router.get('payments', ({ route }) => {
console.log(route)
})

また、request.matchesRouteメソッドを使用して、現在のリクエストが特定のルートかどうかを確認することもできます。メソッドはルートURIパターンまたはルート名を受け入れます。

router.get('/posts/:id', ({ request }) => {
if (request.matchesRoute('/posts/:id')) {
}
})
router
.get('/posts/:id', ({ request }) => {
if (request.matchesRoute('posts.show')) {
}
})
.as('posts.show')

複数のルートに一致することもできます。メソッドは最初に一致した場合にtrueを返します。

if (request.matchesRoute(['/posts/:id', '/posts/:id/comments'])) {
// 何かを実行する
}

AdonisJSのルートのマッチング方法

ルートは、ルートファイル内で登録された順序で一致します。一致は、一番上のルートから開始し、最初に一致したルートで停止します。

似たような2つのルートがある場合、もっとも具体的なルートを最初に登録する必要があります。

次の例では、URL /posts/archived のリクエストは、最初のルート(つまり /posts/:id)で処理されます。なぜなら、動的パラメータ idarchived の値をキャプチャするからです。

import router from '@adonisjs/core/services/router'
router.get('posts/:id', () => {})
router.get('posts/archived', () => {})

この動作は、もっとも具体的なルートを動的パラメータを持つルートの前に配置することで修正できます。

router.get('posts/archived', () => {})
router.get('posts/:id', () => {})

404リクエストの処理

AdonisJSは、現在のリクエストのURLに一致するルートが見つからない場合、404の例外を発生させます。

ユーザーに404ページを表示するには、グローバル例外ハンドラE_ROUTE_NOT_FOUND例外をキャッチし、テンプレートをレンダリングできます。

import { errors } from '@adonisjs/core'
import { HttpContext, ExceptionHandler } from '@adonisjs/core/http'
export default class HttpExceptionHandler extends ExceptionHandler {
async handle(error: unknown, ctx: HttpContext) {
if (error instanceof errors.E_ROUTE_NOT_FOUND) {
return ctx.view.render('errors/404')
}
return super.handle(error, ctx)
}
}

URLビルダー

アプリケーション内の事前定義されたルートのURLを作成するために、URLビルダーを使用できます。たとえば、Edgeテンプレート内のフォームアクションURLを作成したり、リクエストを別のルートにリダイレクトするためのURLを作成したりできます。

router.builderメソッドは、URLビルダークラスのインスタンスを作成し、ビルダーのフルエントAPIを使用してルートを検索し、URLを作成できます。

import router from '@adonisjs/core/services/router'
const PostsController = () => import('#controllers/posts_controller')
router
.get('posts/:id', [PostsController, 'show'])
.as('posts.show')

posts.showルートのURLを生成するには、次のようにします。

import router from '@adonisjs/core/services/router'
router
.builder()
.params([1])
.make('posts.show') // /posts/1
router
.builder()
.params([20])
.make('posts.show') // /posts/20

パラメータは、位置引数の配列として指定することも、キーと値のペアとして定義することもできます。

router
.builder()
.params({ id: 1 })
.make('posts.show') // /posts/1

クエリパラメータの定義

クエリパラメータは、builder.qsメソッドを使用して定義できます。メソッドはキーと値のペアのオブジェクトを受け入れ、それをクエリ文字列にシリアライズします。

router
.builder()
.qs({ page: 1, sort: 'asc' })
.make('posts.index') // /posts?page=1&sort=asc

クエリ文字列は、qs npmパッケージを使用してシリアライズされます。config/app.tsファイルのhttpオブジェクトの下にあるqs設定することができます。

// config/app.js
http: defineConfig({
qs: {
stringify: {
//
}
}
})

URLのプレフィックス

builder.prefixUrlメソッドを使用して、出力にベースURLをプレフィックスすることができます。

router
.builder()
.prefixUrl('https://blog.adonisjs.com')
.params({ id: 1 })
.make('posts.show')

署名付きURLの生成

署名付きURLは、署名クエリ文字列が追加されたURLです。署名は、URLが生成された後に改ざんされていないかを検証するために使用されます。

たとえば、ニュースレターからユーザーの登録解除のためのURLを持っているとします。URLにはuserIdが含まれており、次のようになるかもしれません。

/unsubscribe/231

ユーザーが231のユーザーIDを別の値に変更することを防ぐために、このURLに署名を付け、リクエストを処理する際に署名を検証することができます。

router.get('unsubscribe/:id', ({ request, response }) => {
if (!request.hasValidSignature()) {
return response.badRequest('無効または期限切れのURLです')
}
// 登録解除
}).as('unsubscribe')

makeSignedメソッドを使用して署名付きURLを作成できます。

router
.builder()
.prefixUrl('https://blog.adonisjs.com')
.params({ id: 231 })
// ハイライト開始
.makeSigned('unsubscribe')
// ハイライト終了

署名付きURLの有効期限

expiresInオプションを使用して、指定された期間後に期限切れになる署名付きURLを生成することができます。値はミリ秒単位の数値または時間表現文字列で指定できます。

router
.builder()
.prefixUrl('https://blog.adonisjs.com')
.params({ id: 231 })
// ハイライト開始
.makeSigned('unsubscribe', {
expiresIn: '3 days'
})
// ハイライト終了

ルートの検索を無効にする

URLビルダーは、makeおよびmakeSignedメソッドに与えられたルート識別子でルートの検索を実行します。

AdonisJSアプリケーションの外部で定義されたルートのURLを作成する場合は、ルートの検索を無効にし、makeおよびmakeSignedメソッドにルートパターンを指定することができます。

router
.builder()
.prefixUrl('https://your-app.com')
.disableRouteLookup()
.params({ token: 'foobar' })
.make('/email/verify/:token') // /email/verify/foobar

ドメインの下のルートのURLを作成する

特定のドメインに登録されたルートのURLを作成するには、router.builderForDomainメソッドを使用できます。このメソッドは、ルートを定義する際に使用したルートパターンを受け入れます。

import router from '@adonisjs/core/services/router'
const PostsController = () => import('#controllers/posts_controller')
router.group(() => {
router
.get('/posts/:id', [PostsController, 'show'])
.as('posts.show')
}).domain('blog.adonisjs.com')

次のようにして、blog.adonisjs.comドメインのposts.showルートのURLを作成できます。

router
.builderForDomain('blog.adonisjs.com')
.params({ id: 1 })
.make('posts.show')

テンプレート内でのURLの生成

テンプレート内でURLを生成するためには、routeメソッドとsignedRouteメソッドを使用できます。これには、URLビルダーを使用してURLを生成するための便利なヘルパーメソッドが含まれます。

参照:Edgeヘルパーリファレンスも参照してください。

<a href="{{ route('posts.show', [post.id]) }}">
投稿を表示する
</a>
<a href="{{
signedRoute('unsubscribe', [user.id], {
expiresIn: '3 days',
prefixUrl: 'https://blog.adonisjs.com'
})
}}">
登録解除
</a>

ルーターの拡張

マクロとゲッターを使用して、さまざまなルータークラスにカスタムプロパティを追加することができます。マクロの概念については、AdonisJSの拡張ガイドを先に読んでください。

以下は、拡張できるクラスのリストです。

Router

Routerクラスには、ルート、ルートグループ、またはルートリソースを作成するためのトップレベルのメソッドが含まれています。このクラスのインスタンスは、ルーターサービスを介して利用可能になります。

import { Router } from '@adonisjs/core/http'
Router.macro('property', function (this: Router) {
return value
})
Router.getter('propertyName', function (this: Router) {
return value
})
declare module '@adonisjs/core/http' {
export interface Router {
property: valueType
}
}

Route

Routeクラスは、単一のルートを表します。Routeクラスのインスタンスは、router.getrouter.postなどのメソッドを呼び出すと作成されます。

import { Route } from '@adonisjs/core/http'
Route.macro('property', function (this: Route) {
return value
})
Router.getter('property', function (this: Route) {
return value
})
declare module '@adonisjs/core/http' {
export interface Route {
property: valueType
}
}

RouteGroup

RouteGroupクラスは、複数のルートのグループを表します。router.groupメソッドを呼び出すと、RouteGroupクラスのインスタンスが作成されます。

マクロまたはゲッターの実装内で、this.routesプロパティを使用してグループのルートにアクセスできます。

import { RouteGroup } from '@adonisjs/core/http'
RouteGroup.macro('property', function (this: RouteGroup) {
return value
})
RouteGroup.getter('property', function (this: RouteGroup) {
return value
})
declare module '@adonisjs/core/http' {
export interface RouteGroup {
property: valueType
}
}

RouteResource

RouteResourceクラスは、リソースの複数のルートのグループを表します。router.resourceメソッドを呼び出すと、RouteResourceクラスのインスタンスが作成されます。

マクロまたはゲッターの実装内で、this.routesプロパティを使用してリソースのルートにアクセスできます。

import { RouteResource } from '@adonisjs/core/http'
RouteResource.macro('property', function (this: RouteResource) {
return value
})
RouteResource.getter('property', function (this: RouteResource) {
return value
})
declare module '@adonisjs/core/http' {
export interface RouteResource {
property: valueType
}
}

BriskRoute

BriskRouteクラスは、明示的なハンドラを持たないルートを表します。router.onメソッドを呼び出すと、BriskRouteクラスのインスタンスが作成されます。

マクロまたはゲッターの実装内で、this.setHandlerメソッドを呼び出してルートハンドラを割り当てることができます。

import { BriskRoute } from '@adonisjs/core/http'
BriskRoute.macro('property', function (this: BriskRoute) {
return value
})
BriskRouter.getter('property', function (this: BriskRoute) {
return value
})
declare module '@adonisjs/core/http' {
export interface BriskRoute {
property: valueType
}
}