라우팅
Hono의 라우팅은 유연하고 직관적이다. 한번 살펴보자.
기본
ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
// HTTP 메서드
app.get('/', (c) => c.text('GET /'))
app.post('/', (c) => c.text('POST /'))
app.put('/', (c) => c.text('PUT /'))
app.delete('/', (c) => c.text('DELETE /'))
// 와일드카드
app.get('/wild/*/card', (c) => {
return c.text('GET /wild/*/card')
})
// 모든 HTTP 메서드
app.all('/hello', (c) => c.text('모든 메서드 /hello'))
// 커스텀 HTTP 메서드
app.on('PURGE', '/cache', (c) => c.text('PURGE 메서드 /cache'))
// 여러 메서드
app.on(['PUT', 'DELETE'], '/post', (c) =>
c.text('PUT 또는 DELETE /post')
)
// 여러 경로
app.on('GET', ['/hello', '/ja/hello', '/en/hello'], (c) =>
c.text('Hello')
)경로 파라미터
ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/user/:name', async (c) => {
const name = c.req.param('name')
// ^?
// ...
})또는 모든 파라미터를 한 번에 가져올 수 있다:
ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/posts/:id/comment/:comment_id', async (c) => {
const { id, comment_id } = c.req.param()
// ^?
// ...
})선택적 파라미터
ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
// `/api/animal`과 `/api/animal/:type` 모두 매칭
app.get('/api/animal/:type?', (c) => c.text('Animal!'))정규 표현식
ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/post/:date{[0-9]+}/:title{[a-z]+}', async (c) => {
const { date, title } = c.req.param()
// ^?
// ...
})슬래시 포함하기
ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/posts/:filename{.+\\.png}', async (c) => {
//...
})체인형 라우트
ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app
.get('/endpoint', (c) => {
return c.text('GET /endpoint')
})
.post((c) => {
return c.text('POST /endpoint')
})
.delete((c) => {
return c.text('DELETE /endpoint')
})그룹화
Hono 인스턴스를 사용해 라우트를 그룹화하고, route 메서드로 메인 앱에 추가할 수 있다.
ts
import { Hono } from 'hono'
// ---cut---
const book = new Hono()
book.get('/', (c) => c.text('List Books')) // GET /book
book.get('/:id', (c) => {
// GET /book/:id
const id = c.req.param('id')
return c.text('Get Book: ' + id)
})
book.post('/', (c) => c.text('Create Book')) // POST /book
const app = new Hono()
app.route('/book', book)기본 경로를 유지한 채 그룹화하기
기본 경로를 변경하지 않고도 여러 인스턴스를 그룹화할 수 있다.
ts
import { Hono } from 'hono'
// ---cut---
const book = new Hono()
book.get('/book', (c) => c.text('List Books')) // GET /book
book.post('/book', (c) => c.text('Create Book')) // POST /book
const user = new Hono().basePath('/user')
user.get('/', (c) => c.text('List Users')) // GET /user
user.post('/', (c) => c.text('Create User')) // POST /user
const app = new Hono()
app.route('/', book) // /book 처리
app.route('/', user) // /user 처리기본 경로
기본 경로를 지정할 수 있다.
ts
import { Hono } from 'hono'
// ---cut---
const api = new Hono().basePath('/api')
api.get('/book', (c) => c.text('List Books')) // GET /api/book호스트명을 이용한 라우팅
호스트명을 포함하면 정상적으로 동작한다.
ts
import { Hono } from 'hono'
// ---cut---
const app = new Hono({
getPath: (req) => req.url.replace(/^https?:\/([^?]+).*$/, '$1'),
})
app.get('/www1.example.com/hello', (c) => c.text('hello www1'))
app.get('/www2.example.com/hello', (c) => c.text('hello www2'))host 헤더 값을 활용한 라우팅
Hono에서는 getPath() 함수를 Hono 생성자에 설정하면 host 헤더 값을 처리할 수 있다.
ts
import { Hono } from 'hono'
// ---cut---
const app = new Hono({
getPath: (req) =>
'/' +
req.headers.get('host') +
req.url.replace(/^https?:\/\/[^/]+(\/[^?]*)/, '$1'),
})
app.get('/www1.example.com/hello', (c) => c.text('hello www1'))
// 다음과 같은 요청이 라우트와 일치한다:
// new Request('http://www1.example.com/hello', {
// headers: { host: 'www1.example.com' },
// })이를 활용하면, 예를 들어 User-Agent 헤더를 기반으로 라우팅을 변경할 수 있다.
라우팅 우선순위
핸들러나 미들웨어는 등록된 순서대로 실행된다.
ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/book/a', (c) => c.text('a')) // a
app.get('/book/:slug', (c) => c.text('common')) // commonGET /book/a ---> `a`
GET /book/b ---> `common`핸들러가 실행되면 프로세스는 중단된다.
ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('*', (c) => c.text('common')) // common
app.get('/foo', (c) => c.text('foo')) // fooGET /foo ---> `common` // foo는 실행되지 않음실행하고 싶은 미들웨어가 있다면 핸들러 위에 코드를 작성한다.
ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
const app = new Hono()
// ---cut---
app.use(logger())
app.get('/foo', (c) => c.text('foo'))"fallback" 핸들러를 사용하려면 다른 핸들러 아래에 코드를 작성한다.
ts
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/bar', (c) => c.text('bar')) // bar
app.get('*', (c) => c.text('fallback')) // fallbackGET /bar ---> `bar`
GET /foo ---> `fallback`라우트 그룹화와 순서
라우트를 그룹화할 때 순서를 잘못 지정하면 쉽게 눈치채기 어려운 실수를 저지를 수 있다. route() 함수는 두 번째 인자로 전달된 라우트(예: three 또는 two)를 자신의 라우트(two 또는 app)에 추가한다.
ts
three.get('/hi', (c) => c.text('hi'))
two.route('/three', three)
app.route('/two', two)
export default app이 경우 200 응답을 반환한다.
GET /two/three/hi ---> `hi`하지만 순서가 잘못되면 404 오류가 발생한다.
ts
import { Hono } from 'hono'
const app = new Hono()
const two = new Hono()
const three = new Hono()
// ---cut---
three.get('/hi', (c) => c.text('hi'))
app.route('/two', two) // `two`에 라우트가 없음
two.route('/three', three)
export default appGET /two/three/hi ---> 404 Not Found