在本指南中,我们将重现 Express.js 文档 中的许多示例,向您展示如何使用 h3 做同样的事情。
如果您不熟悉 Express.jNumber.isNaNr.isNaNr.isNaNu,可以放心跳过本指南。 目的是向您展示 h3 与 Express.js 有多么相似。一旦您了解了相似之处,如果您熟悉 Express.js,您将能够毫无问题地使用 h3。
即使 h3 看起来与 Express.js 相似,但这并不意味着 Express.js 仍然可行。Express.js 是一个很久没有发展的老框架。对于新项目来说,这不是一个好的选择,因为它很容易导致安全问题和内存泄漏。
使用 h3,您还可以使用 unjs/listhen 无需任何配置即可开箱即用地重新加载。
您可以使用 npx --yes listhen -w ./app.ts
运行每个 h3 示例。
:
Hello World
Express.js 文档中的第一个示例是 Hello World。
代码非常简单:
/**
* Express.js 示例应用程序。
*/
var express = require('express')
var app = express()
app.get('/', (req, res) => {
res.send('Hello World')
})
app.listen(3000)
console.log('Express 在端口 3000 上启动')
让我们看看如何使用 h3 做同样的事情:
/**
* h3 示例应用程序。
*/
import { createApp, defineEventHandler } from 'h3'
export const app = createApp()
app.use(
'/',
defineEventHandler((event) => {
return 'Hello World'
}),
)
然后,您可以使用 npx --yes listhen -w ./app.ts
启动服务器并转到 http://localhost:3000 查看结果。
多路由器
第二个示例是 多路由器。在这个例子中,我们创建了多个路由器来拆分逻辑。
/**
* Express.js example app.
*/
var express = require('express')
var app = express()
var apiv1 = express.Router()
apiv1.get('/', (req, res) => {
res.send('Hello from APIv1 root route.')
})
apiv1.get('/users', (req, res) => {
res.send('List of APIv1 users.')
})
const apiv2 = express.Router()
apiv2.get('/', (req, res) => {
res.send('Hello from APIv2 root route.')
})
apiv2.get('/users', (req, res) => {
res.send('List of APIv2 users.')
})
app.use('/api/v1', apiv1)
app.use('/api/v2', apiv2)
app.get('/', (req, res) => {
res.send('Hello from root route.')
})
app.listen(3000)
console.log('Express started on port 3000')
对于某些设施,我们将每个文件归入同一个组。
使用 h3,我们可以做同样的事情:
/**
* h3 example app.
*/
import { createApp, createRouter, defineEventHandler, useBase } from 'h3'
export const app = createApp()
const apiv1 = createRouter()
.get(
'/',
defineEventHandler(() => {
return 'Hello from APIv1 root route.'
}),
)
.get(
'/users',
defineEventHandler(() => {
return 'List of APIv1 users.'
}),
)
const apiv2 = createRouter()
.get(
'/',
defineEventHandler(() => {
return 'Hello from APIv2 root route.'
}),
)
.get(
'/users',
defineEventHandler(() => {
return 'List of APIv2 users.'
}),
)
app.use('/api/v1/**', useBase('/api/v1', apiv1.handler))
app.use('/api/v2/**', useBase('/api/v2', apiv2.handler))
非常相似。主要区别在于我们必须使用“useBase”来定义路由器的基本路径。
参数
第三个例子是 Params。在这个例子中,我们在路由中使用参数。
/**
* Express.js example app.
*/
var createError = require('http-errors')
var express = require('express')
var app = express()
var users = [
{ name: 'tj' },
{ name: 'tobi' },
{ name: 'loki' },
{ name: 'jane' },
{ name: 'bandit' },
]
app.param(['to', 'from'], (req, res, next, num, name) => {
req.params[name] = Number.parseInt(num, 10)
if (Number.isNaN(req.params[name])) {
next(createError(400, `failed to parseInt ${num}`))
}
else {
next()
}
})
app.param('user', (req, res, next, id) => {
if ((req.user === users[id])) {
next()
}
else {
next(createError(404, 'failed to find user'))
}
})
app.get('/', (req, res) => {
res.send('Visit /user/0 or /users/0-2')
})
app.get('/user/:user', (req, res) => {
res.send(`user ${req.user.name}`)
})
app.get('/users/:from-:to', (req, res) => {
var from = req.params.from
var to = req.params.to
var names = users.map((user) => {
return user.name
})
res.send(`users ${names.slice(from, to + 1).join(', ')}`)
})
app.listen(3000)
console.log('Express started on port 3000')
使用 h3,我们可以做同样的事情:
/**
* h3 example app.
*/
import {
createApp,
createError,
createRouter,
defineEventHandler,
getRouterParam,
getValidatedRouterParams,
} from 'h3'
import { z } from 'zod'
const users = [
{ name: 'tj' },
{ name: 'tobi' },
{ name: 'loki' },
{ name: 'jane' },
{ name: 'bandit' },
]
export const app = createApp()
const router = createRouter()
router.get(
'/',
defineEventHandler(() => {
return 'Visit /users/0 or /users/0/2'
}),
)
router.get(
'/user/:user',
defineEventHandler(async (event) => {
const { user } = await getValidatedRouterParams(
event,
z.object({
user: z.number({ coerce: true }),
}).parse,
)
if (!users[user]) {
throw createError({
status: 404,
statusMessage: 'User Not Found',
})
}
return `user ${user}`
}),
)
router.get(
'/users/:from/:to',
defineEventHandler(async (event) => {
const { from, to } = await getValidatedRouterParams(
event,
z.object({
from: z.number({ coerce: true }),
to: z.number({ coerce: true }),
}).parse,
)
const names = users.map((user) => {
return user.name
})
return `users ${names.slice(from, to).join(', ')}`
}),
)
app.use(router)
使用 h3,我们没有 param
方法。相反,我们使用 getRouterParam
或 getValidatedRouterParams
来验证参数。
它更明确且更易于使用。在此示例中,我们使用 Zod
,但您可以自由使用任何其他验证库。
Cookies
第四个示例是 Cookies。在此示例中,我们使用 cookies。
/**
* Express.js example app.
*/
var express = require('express')
var app = express()
var cookieParser = require('cookie-parser')
app.use(cookieParser('my secret here'))
app.use(express.urlencoded({ extended: false }))
app.get('/', (req, res) => {
if (req.cookies.remember) {
res.send('Remembered :). Click to <a href="/forget">forget</a>!.')
}
else {
res.send(
'<form method="post"><p>Check to <label>'
+ '<input type="checkbox" name="remember"/> remember me</label> '
+ '<input type="submit" value="Submit"/>.</p></form>',
)
}
})
app.get('/forget', (req, res) => {
res.clearCookie('remember')
res.redirect('back')
})
app.post('/', (req, res) => {
var minute = 60000
if (req.body.remember)
res.cookie('remember', 1, { maxAge: minute })
res.redirect('back')
})
app.listen(3000)
console.log('Express started on port 3000')
使用 h3,我们可以做同样的事情:
import {
createApp,
createRouter,
defineEventHandler,
getCookie,
getHeader,
readBody,
sendRedirect,
setCookie,
} from 'h3'
export const app = createApp()
const router = createRouter()
router.get(
'/',
defineEventHandler((event) => {
const remember = getCookie(event, 'remember')
if (remember) {
return 'Remembered :). Click to <a href="/forget">forget</a>!.'
}
else {
return `<form method="post"><p>Check to <label>
<input type="checkbox" name="remember"/> remember me</label>
<input type="submit" value="Submit"/>.</p></form>`
}
}),
)
router.get(
'/forget',
defineEventHandler((event) => {
deleteCookie(event, 'remember')
const back = getHeader(event, 'referer') || '/'
return sendRedirect(event, back)
}),
)
router.post(
'/',
defineEventHandler(async (event) => {
const body = await readBody(event)
if (body.remember)
setCookie(event, 'remember', '1', { maxAge: 60 * 60 * 24 * 7 })
const back = getHeader(event, 'referer') || '/'
return sendRedirect(event, back)
}),
)
app.use(router)
使用 h3,我们没有 cookieParser
中间件。相反,我们使用 getCookie
和 setCookie
来获取和设置 cookie。它更明确,更易于使用。
中间件
使用 express
时,我们通常使用 middleware
来处理请求。
例如,这里我们使用 morgan
来处理请求日志记录。
var express = require('express')
var morgan = require('morgan')
var app = express()
app.use(morgan('combined'))
app.get('/', (req, res) => {
res.send('hello, world!')
})
app.listen(3000)
console.log('Express started on port 3000')
在 h3
中,我们还可以直接使用来自 express
生态系统的中间件。
通过使用 fromNodeMiddleware
进行包装,可以轻松实现这一点。
import morgan from 'morgan'
import { createApp, defineEventHandler, fromNodeMiddleware } from 'h3'
export const app = createApp()
app.use(fromNodeMiddleware(morgan('combined')))
app.use(
'/',
defineEventHandler((event) => {
return 'Hello World'
}),
)