Hapi 的二次封装实现 RESTful 风格 API 接口

在现代的 Web 应用中,使用 RESTful 风格的 API 接口已经成为了一种标准。但是,在实现 RESTful 风格的 API 接口时,需要考虑到很多细节问题,包括路由、参数验证、错误处理等等,这对于开发人员来说是一项很大的工程。

Hapi 是一种流行的 Node.js Web 框架,它提供了丰富的功能和插件来简化 Web 应用的开发。但是,为了满足 RESTful 风格的 API 接口的需求,我们还需要对 Hapi 进行二次封装。在本文中,我们将介绍如何使用 Hapi 进行二次封装,实现 RESTful 风格的 API 接口。

为什么需要二次封装

如前所述,Hapi 提供了很多功能和插件来简化 Web 应用的开发。但是,在实现 RESTful 风格的 API 接口时,我们需要处理的问题比较多,包括路由、参数验证、错误处理等等,这些细节问题需要耗费大量的时间和精力。

此外,如果我们要实现多个 RESTful 风格的 API 接口,每个接口都需要进行重复的工作,这也是一种浪费。

因此,使用 Hapi 进行二次封装可以大大简化我们的工作,减少错误发生的可能性,同时也可以提高开发效率。

二次封装的实现

接下来,我们将介绍如何使用 Hapi 进行二次封装,实现 RESTful 风格的 API 接口。

首先,我们需要定义一些规则,以便我们可以快速而准确地生成 RESTful 风格的 API 接口。

基本规则

在 RESTful 风格的 API 接口中,我们通常会使用 HTTP 动词来定义操作。常见的 HTTP 动词有 GET、POST、PUT、DELETE 等等。因此,我们需要定义一个操作与 HTTP 动词对应的映射关系。

const methodsMap = {
  'GET': 'get',
  'POST': 'post',
  'PUT': 'put',
  'DELETE': 'delete',
};

为了方便路由的处理,我们还需要定义一个路由的基本格式。

const routeFormat = {
  method: '',
  path: '',
  handler: ()=>{
    // todo
  },
  options: {
    auth: false,
    // ...
  },
};

在这个格式中,method 表示 HTTP 动词,path 表示路由路径,handler 表示路由处理程序,options 表示其他选项。

接下来,我们需要定义一些函数来生成各种类型的路由。

生成列表路由

RESTful 风格的 API 接口中,通常会有一种类型的路由是用来获取列表数据的。例如,我们可以通过 GET 请求获取用户列表:

GET /users

因此,我们需要定义一个函数来生成列表路由。

function getListRoute(url, handler) {
  return {
    ...routeFormat,
    method: methodsMap.GET,
    path: url,
    handler: (request, h) => handler(request.query),
  };
}

在这个函数中,url 表示路由路径,handler 表示用于处理请求的函数。在处理函数中,我们可以通过 request.query 参数来获取查询参数,然后进行相应的处理。

生成详情路由

另一种常见的路由类型是详情路由,用于获取单个资源的详细信息。例如,我们可以通过 GET 请求获取某个用户的详细信息:

GET /users/:id

因此,我们需要定义一个函数来生成详情路由。

function getDetailRoute(url, handler) {
  return {
    ...routeFormat,
    method: methodsMap.GET,
    path: url,
    handler: (request, h) => handler(request.params.id),
  };
}

在这个函数中,url 表示路由路径,handler 表示用于处理请求的函数。在处理函数中,我们可以通过 request.params.id 参数来获取查询参数,然后进行相应的处理。

生成创建路由

创建路由用于新建资源。例如,我们可以通过 POST 请求创建一个新的用户:

POST /users

因此,我们需要定义一个函数来生成创建路由。

function getCreateRoute(url, handler) {
  return {
    ...routeFormat,
    method: methodsMap.POST,
    path: url,
    handler: async (request, h) => handler(request.payload),
  };
}

在这个函数中,url 表示路由路径,handler 表示用于处理请求的函数。在处理函数中,我们可以通过 request.payload 参数来获取提交的数据,然后进行相应的处理。

生成更新路由

更新路由用于更新资源。例如,我们可以通过 PUT 请求更新某个用户的信息:

PUT /users/:id

因此,我们需要定义一个函数来生成更新路由。

function getUpdateRoute(url, handler) {
  return {
    ...routeFormat,
    method: methodsMap.PUT,
    path: url,
    handler: async (request, h) => handler(request.params.id, request.payload),
  };
}

在这个函数中,url 表示路由路径,handler 表示用于处理请求的函数。在处理函数中,我们可以通过 request.params.id 参数来获取更新的对象id和通过 request.payload 参数来获取提交的更新数据,然后进行相应的处理。

生成删除路由

删除路由用于删除资源。例如,我们可以通过 DELETE 请求删除某个用户的信息:

DELETE /users/:id

因此,我们需要定义一个函数来生成删除路由。

function getDeleteRoute(url, handler) {
  return {
    ...routeFormat,
    method: methodsMap.DELETE,
    path: url,
    handler: async (request, h) => handler(request.params.id),
  };
}

在这个函数中,url 表示路由路径,handler 表示用于处理请求的函数。在处理函数中,我们可以通过 request.params.id 参数来获取删除对象id,然后进行相应的处理。

生成路由表

有了上面的路由生成函数,我们可以很容易地生成各种类型的路由了。在实际应用中,我们可能需要定义多个路由表,用于不同的API接口

const userRoutes = [
  getListRoute('/users', userListHandler), // 获取列表
  getDetailRoute('/users/{id}', userDetailHandler), // 获取详情
  getCreateRoute('/users', createUserHandler), // 创建资源
  getUpdateRoute('/users/{id}', updateUserHandler), // 更新资源
  getDeleteRoute('/users/{id}', deleteUserHandler), // 删除资源
];

const orderRoutes = [
  getListRoute('/orders', orderListHandler), // 获取列表
  getDetailRoute('/orders/{id}', orderDetailHandler), // 获取详情
  getCreateRoute('/orders', createOrderHandler), // 创建资源
  getUpdateRoute('/orders/{id}', updateOrderHandler), // 更新资源
  getDeleteRoute('/orders/{id}', deleteOrderHandler), // 删除资源
];

注册路由

有了路由表,我们还需要将路由表注册到 Hapi 中。

const server = Hapi.server({
  port: 3000,
  host: 'localhost',
});

await server.register([
  { plugin: require('hapi-auth-jwt2') },
  { plugin: require('hapi-pagination') },
  { plugin: require('hapi-cors') },
  { plugin: require('inert') },
  { plugin: require('vision') },
  { plugin: require('hapi-swagger') },
]);

// 注册路由
server.route([...userRoutes, ...orderRoutes]);

// 启动服务
await server.start();

在上面的代码中,server.route 函数将路由表注册到 Hapi 中。在实际应用中,我们可能需要注册多个路由表,具体视情况而定。

总结

使用 Hapi 进行二次封装可以大大简化我们的工作,同时还可以提高开发效率。在本文中,我们介绍了如何使用 Hapi 进行二次封装,实现 RESTful 风格的 API 接口,包括路由、参数验证、错误处理等细节问题。希望对您有所帮助。

完整示例代码:

const Hapi = require('@hapi/hapi');

// HTTP 动词与操作名称的映射关系
const methodsMap = {
  'GET': 'get',
  'POST': 'post',
  'PUT': 'put',
  'DELETE': 'delete',
};

// 路由格式
const routeFormat = {
  method: '',
  path: '',
  handler: ()=>{
    // todo
  },
  options: {
    auth: false,
    // ...
  },
};

// 获取列表路由
function getListRoute(url, handler) {
  return {
    ...routeFormat,
    method: methodsMap.GET,
    path: url,
    handler: (request, h) => handler(request.query),
  };
}

// 获取详情路由
function getDetailRoute(url, handler) {
  return {
    ...routeFormat,
    method: methodsMap.GET,
    path: url,
    handler: (request, h) => handler(request.params.id),
  };
}

// 创建资源路由
function getCreateRoute(url, handler) {
  return {
    ...routeFormat,
    method: methodsMap.POST,
    path: url,
    handler: async (request, h) => handler(request.payload),
  };
}

// 更新资源路由
function getUpdateRoute(url, handler) {
  return {
    ...routeFormat,
    method: methodsMap.PUT,
    path: url,
    handler: async (request, h) => handler(request.params.id, request.payload),
  };
}

// 删除资源路由
function getDeleteRoute(url, handler) {
  return {
    ...routeFormat,
    method: methodsMap.DELETE,
    path: url,
    handler: async (request, h) => handler(request.params.id),
  };
}

// 定义路由表
const userRoutes = [
  getListRoute('/users', userListHandler), // 获取列表
  getDetailRoute('/users/{id}', userDetailHandler), // 获取详情
  getCreateRoute('/users', createUserHandler), // 创建资源
  getUpdateRoute('/users/{id}', updateUserHandler), // 更新资源
  getDeleteRoute('/users/{id}', deleteUserHandler), // 删除资源
];

const orderRoutes = [
  getListRoute('/orders', orderListHandler), // 获取列表
  getDetailRoute('/orders/{id}', orderDetailHandler), // 获取详情
  getCreateRoute('/orders', createOrderHandler), // 创建资源
  getUpdateRoute('/orders/{id}', updateOrderHandler), // 更新资源
  getDeleteRoute('/orders/{id}', deleteOrderHandler), // 删除资源
];

// 创建 Hapi 服务并注册插件
const server = Hapi.server({
  port: 3000,
  host: 'localhost',
});

await server.register([
  { plugin: require('hapi-auth-jwt2') },
  { plugin: require('hapi-pagination') },
  { plugin: require('hapi-cors') },
  { plugin: require('inert') },
  { plugin: require('vision') },
  { plugin: require('hapi-swagger') },
]);

// 注册路由
server.route([...userRoutes, ...orderRoutes]);

// 启动服务
await server.start();

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a628caadd4f0e0ffed9340


纠错反馈