Hapi 是一款基于 Node.js 的后端 Web 开发框架,它具有易用、可扩展及高可靠的特性。 Hapi 对路由的处理非常灵活,支持路由的分组,可以帮助我们更好的管理和组织我们的路由。
什么是路由分组?
路由分组可以理解为,将一组相关的路由路径归为一个组,比如我们有一个路由处理用户操作如下:
// www.javascriptcn.com code example
server.route({
method: 'GET',
path: '/api/users',
handler: (request, h) => {
// 处理获取用户列表的请求
}
})
server.route({
method: 'GET',
path: '/api/users/{id}',
handler: (request, h) => {
// 处理获取某个用户信息的请求
}
})
server.route({
method: 'POST',
path: '/api/users',
handler: (request, h) => {
// 处理创建用户的请求
}
})
server.route({
method: 'PUT',
path: '/api/users/{id}',
handler: (request, h) => {
// 处理更新某个用户信息的请求
}
})
server.route({
method: 'DELETE',
path: '/api/users/{id}',
handler: (request, h) => {
// 处理删除某个用户的请求
}
})我们可以将这些路由按照功能分类为用户操作组,然后可以将它们组织为如下的形式:
// www.javascriptcn.com code example
server.route({
method: 'GET',
path: '/api/users',
handler: (request, h) => {
// 处理获取用户列表的请求
},
config: {
tags: ['api', 'users']
}
})
const usersRoutes = [
{
method: 'GET',
path: '/{id}',
handler: (request, h) => {
// 处理获取某个用户信息的请求
}
},
{
method: 'POST',
path: '/',
handler: (request, h) => {
// 处理创建用户的请求
}
},
{
method: 'PUT',
path: '/{id}',
handler: (request, h) => {
// 处理更新某个用户信息的请求
}
},
{
method: 'DELETE',
path: '/{id}',
handler: (request, h) => {
// 处理删除某个用户的请求
}
},
]
server.route({
path: '/api/users/{id}',
method: 'GET',
handler: (request, h) => {
// 路由重定向
return h.redirect(`/api/users/${request.params.id}`)
}
})
server.route({
path: '/api/users',
method: ['PUT', 'POST', 'DELETE'],
handler: (request, h) => {
return h.response({
message: '方法不允许'
}).code(405)
}
})
server.route({
path: '/api/users',
method: '*',
config: {
tags: ['api', 'users']
},
handler: (request, h) => {
return h.response({
message: '请求的资源不存在'
}).code(404)
}
})
server.route({
method: 'GET',
path: '/api/users',
handler: (request, h) => {
// 处理获取用户列表的请求
},
config: {
tags: ['api', 'users']
}
})可以看到,我们将获取某个用户信息、创建用户、更新某个用户信息、删除某个用户的请求归为一个组,并将组名标记为 users。
Hapi 框架中的路由分组
Hapi 提供了一个类似于 Express 中 app.use() 的方法 server.realm.modifiers.route.prefix 用于指定某个路由组的前缀。通过设置前缀,我们可以将相关的路由进行分组,这样可以更好地组织和管理路由。示例如下:
// www.javascriptcn.com code example
const Hapi = require('@hapi/hapi')
const server = Hapi.server({
port: 3000,
host: 'localhost'
})
const userRoutes = [
{
method: 'GET',
path: '/',
handler: (request, h) => {
// handle index of user list
}
},
// ...
]
server.realm.modifiers.route.prefix = '/api/users'
server.route(userRoutes)使用 server.realm.modifiers.route.prefix 可以限定路由的前缀,这样可以使我们的路由清晰可读,而且可以减少路由信息的重复。
另外,我们可以使用 config.tags 属性来标记路由的主题或分类,当然你也可以使用其他属性或自定义的属性来分类路由,这样便于我们管理 route.js 的代码,并且也易于生成 API 文档。
// www.javascriptcn.com code example
const userRoutes = [
{
method: 'GET',
path: '/',
handler: (request, h) => {
// handle index of user list
},
config: {
tags: ['api', 'users']
}
},
// ...
]
server.realm.modifiers.route.prefix = '/api'
server.route(userRoutes)Hapi 框架下如何对路由进行更严格的分类和管理
我们上面只是简单介绍了路由的分组,但是对于大型项目可以进一步将路由进行细分,对其进行管理。 Hapi 提供了 plugins 和 routes 两种把路由及相关业务逻辑组织起来的方式。
官方文档中描述 plugins 和 routes 的不同点:
Plugins
Plugins are collections of functionality and can range from single-purpose libraries to full featured, complex applications. A plugin can define one or more servers or connections, as well as exposing methods, handlers, and configuration.
Routes
Routes are a way to group and organize functionality for serving requests. They allow you to define the endpoints that the server exposes and provide a way to communicate the expected incoming payload and response payload.
简单来说:
plugins用于定义整个应用或者系统的功能和抽象,可以是多个路由的集合。常用于连接数据库和缓存,跨域处理和错误处理等。routes用于定义操作,即路由和对该路由对应的请求处理代码,是实现具体业务逻辑的位置。
示例代码
我们假设需要构建一个博客系统,我们可以将所有与博客相关的路由定义在 plugins 目录下的 blogs.js 中。
// www.javascriptcn.com code example
const postsController = require('../controllers/posts')
const tagsController = require('../controllers/tags')
const blogsRoutes = [
{
method: 'GET',
path: '/posts',
handler: (request, h) => {
return postsController.getPosts(request)
},
config: {
tags: ['api', 'blogs', 'posts']
}
},
{
method: 'POST',
path: '/posts',
handler: (request, h) => {
return postsController.createPost(request)
},
config: {
tags: ['api', 'blogs', 'posts']
}
},
{
method: 'GET',
path: '/posts/{postId}',
handler: (request, h) => {
return postsController.getPost(request)
},
config: {
tags: ['api', 'blogs', 'posts']
}
},
{
method: 'PUT',
path: '/posts/{postId}',
handler: (request, h) => {
return postsController.updatePost(request)
},
config: {
tags: ['api', 'blogs', 'posts']
}
},
{
method: 'DELETE',
path: '/posts/{postId}',
handler: (request, h) => {
return postsController.deletePost(request)
},
config: {
tags: ['api', 'blogs', 'posts']
}
},
{
method: 'GET',
path: '/tags',
handler: (request, h) => {
return tagsController.getTags(request)
},
config: {
tags: ['api', 'blogs', 'tags']
}
},
{
method: 'POST',
path: '/tags',
handler: (request, h) => {
return tagsController.createTag(request)
},
config: {
tags: ['api', 'blogs', 'tags']
}
},
{
method: 'GET',
path: '/tags/{tagId}',
handler: (request, h) => {
return tagsController.getTag(request)
},
config: {
tags: ['api', 'blogs', 'tags']
}
},
{
method: 'PUT',
path: '/tags/{tagId}',
handler: (request, h) => {
return tagsController.updateTag(request)
},
config: {
tags: ['api', 'blogs', 'tags']
}
},
{
method: 'DELETE',
path: '/tags/{tagId}',
handler: (request, h) => {
return tagsController.deleteTag(request)
},
config: {
tags: ['api', 'blogs', 'tags']
}
}
]
module.exports = {
name: 'blogs',
version: '0.0.1',
register: async (server, options) => {
server.realm.modifiers.route.prefix = '/api/blogs'
server.route(blogsRoutes)
}
}接下来,我们将这个路由放到 plugins 目录下的 plugins.js 中。
// www.javascriptcn.com code example
const Hapi = require('@hapi/hapi')
const blogsPlugin = require('./blogs')
const init = async () => {
const server = Hapi.server({
port: 3000,
host: 'localhost'
})
await server.register({
plugin: blogsPlugin
})
await server.start()
console.log(`Server running at: ${server.info.uri}`)
}
init()在 server.register 方法中,我们使用了我们定义的 blogsPlugin,也就是将所有与博客相关的路由注册到 Hapi 实例上。
最后,使用命令行运行 plugins.js 文件,即可启动 Hapi 服务,所有与博客相关的路由信息可以通过 /api/blogs/ 路径访问。
至此,路由分组的具体使用方法就讲解完了。在应用实现过程中,针对不同的业务场景可以有不同的路由处理策略,并且路由的分组,在维护和修改代码时都可以让代码更容易被理解和操作。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d793fba941bf7134d8dda5