网站首页 > 开源技术 正文
关注留言点赞,带你了解最流行的软件开发知识与最新科技行业趋势。
嘿,你有没有想过使用 NodeJS 创建你的 web 框架,以了解引擎盖下发生的事情、方法、中间件、控制器等等......
我知道我们有很多很棒的框架来创建很棒的应用程序,比如 ExpressJS、NestJS、Fastify 等。但是你知道他们在做什么,我认为大多数开发人员甚至不关心它,只想创建他们的应用程序,这没关系,但他们可能有更好的创建应用程序和理解框架正在做什么的经验,允许扩展框架的功能,修复一些意外错误,改善性能问题,总的来说,让他们的生活更轻松。
在创建我们的 Web 框架的过程中,我们将使用与 Javascript、NodeJS 和一般编程相关的几个概念,例如设计模式和正则表达式。
应用程序:今天我们将创建 Web 框架的第一部分,使我们能够创建路由并处理这些路由。
要求:
节点16.16版本
设置
我们将在这里创建一个基本设置,只是为了能够继续工作。
mkdir web-framework
cd web-framework
npm init -y
打开你喜欢的终端
创建一个具有所需名称的文件夹。
进入文件夹
开始一个新的 npm 项目
进口
src/app.js
const { createServer } = require('http')
const { match } = require('path-to-regexp')
第一行createServer从 Node.js 的内置http模块导入函数,这允许我们创建一个 HTTP 服务器。第二行从包中导入 match 函数path-to-regexp,它允许我们将 URL 匹配到特定的路由。
安装path-to-regex包:
npm install path-to-regex
应用
src/app.js
const App = () => {
const routes = new Map()
const createMyServer = () => createServer(serverHandler.bind(this))
该App函数是我们的 Web 应用程序框架的主要入口点。它创建了一个名为 routes 的新Map对象来存储我们定义的所有路由。它还定义了一个名为的辅助函数,createMyServer它使用该serverHandler函数创建一个 HTTP 服务器,我们将很快创建一个函数,它将是主要的处理程序。
路线定义
我无意涵盖所有 HTTP 方法,而只是展示其中的一些方法作为示例。
src/app.js
const get = (path, ...handlers) => {
const currentHandlers = routes.get(`${path}/GET`) || []
routes.set(`${path}/GET`, [...currentHandlers, ...handlers])
}
const post = (path, ...handlers) => {
const currentHandlers = routes.get(`${path}/POST`) || []
routes.set(`${path}/POST`, [...currentHandlers, ...handlers])
}
const put = (path, ...handlers) => {
const currentHandlers = routes.get(`${path}/PUT`) || []
routes.set(`${path}/PUT`, [...currentHandlers, ...handlers])
}
const patch = (path, ...handlers) => {
const currentHandlers = routes.get(`${path}/PATCH`) || []
routes.set(`${path}/PATCH`, [...currentHandlers, ...handlers])
}
const del = (path, ...handlers) => {
const currentHandlers = routes.get(`${path}/DELETE`) || []
routes.set(`${path}/DELETE`, [...currentHandlers, ...handlers])
}
这些是路由定义函数,它们定义新路由并为每个 HTTP 方法(GET、POST、PUT、PATCH、DELETE)指定处理程序。他们接受一个path参数和任意数量的handlers. 每个函数从对象中检索指定方法和路径的当前处理程序routes,添加任何新的处理程序,并将更新的处理程序设置回对象routes。
与路由匹配并找到正确处理程序的模式必须包含路由,最后是方法的名称。像下面的例子:
对于/test/1/GET路径是/test/1,方法是GET
对于/test1/2/test2/POST路径是/test1/2/test2,方法是POST
这Map是存储处理程序的好方法,因为在这里我们可以使用一些有用的函数,例如get和keys。
处理 URL
因为我们的路由系统运行良好,所以我们需要处理 URL,因为它们不是我们预期的那样。
src/app.js
const sanitizeUrl = (url, method) => {
// Get the last part of the URL, removing the domain
const urlParams = url.split('/').slice(1)
// Remove querystrings from the last parameter
const [lastParam] = urlParams[urlParams.length - 1].split('?')
urlParams.splice(urlParams.length - 1, 1)
// Create the URL with our pattern
const allParams = [...urlParams, lastParam].join('/')
const sanitizedUrl = `/${allParams}/${method.toUpperCase()}`
return sanitizedUrl
}
const matchUrl = (sanitizedUrl) => {
for (const path of routes.keys()) {
const urlMatch = match(path, {
decode: decodeURIComponent,
})
const found = urlMatch(sanitizedUrl)
if (found) {
return path
}
}
return false
}
这些是用于清理 URL 并将其匹配到相应路由的辅助函数。sanitizeUrl接受一个 URL 和一个 HTTP 方法并删除任何查询字符串,然后将参数连接成一个匹配路由结构的模式。matchUrl遍历路由,检查 URL 是否与我们当前的路由之一匹配。
处理服务器
src/app.js
const serverHandler = async (request, response) => {
const sanitizedUrl = sanitizeUrl(request.url, request.method)
const match = matchUrl(sanitizedUrl)
if (match) {
const middlewaresAndControllers = routes.get(match)
console.log(middlewaresAndControllers)
response.statusCode = 200
response.end('Found')
} else {
response.statusCode = 404
response.end('Not found')
}
}
该函数首先使用sanitizeUrl()和request.url作为request.method参数调用以创建经过清理的 URL,该 URL 用于检查框架中设置的路由。
然后,该函数matchUrl()使用经过清理的 URL 进行调用,以尝试找到在框架中设置的匹配路由。如果找到匹配项,它会从路由映射中检索与该路由关联的中间件和控制器列表。
如果有与路由关联的中间件和/或控制器,该函数会将它们记录到控制台并将响应状态代码设置为 200,表示请求成功。最后,它以消息结束响应'Found'。
如果未找到匹配项,该函数会将响应状态代码设置为 404,表示未找到所请求的资源。最后,它以消息结束响应'Not found'。
运行函数和导出
src/app.js
const run = (port) => {
const server = createMyServer()
server.listen(port)
}
return {
run,
get,
post,
patch,
put,
del
}
该run函数负责通过调用服务器实例的 listen 方法来启动服务器。该listen方法接受一个端口号作为参数,这是服务器将在其上侦听传入请求的端口。
createMyServer在此函数中,通过调用在代码前面定义的函数来创建服务器的新实例。然后,listen在服务器实例上调用该方法,传入提供给该run函数的端口参数。
最后,我们可以将所有公共函数导出为closure返回它们。
完整代码
src/app.js
const { createServer } = require('http')
const { match } = require('path-to-regexp')
const App = () => {
const routes = new Map()
const createMyServer = () => createServer(serverHandler.bind(this))
const get = (path, ...handlers) => {
const currentHandlers = routes.get(`${path}/GET`) || []
routes.set(`${path}/GET`, [...currentHandlers, ...handlers])
}
const post = (path, ...handlers) => {
const currentHandlers = routes.get(`${path}/POST`) || []
routes.set(`${path}/POST`, [...currentHandlers, ...handlers])
}
const put = (path, ...handlers) => {
const currentHandlers = routes.get(`${path}/PUT`) || []
routes.set(`${path}/PUT`, [...currentHandlers, ...handlers])
}
const patch = (path, ...handlers) => {
const currentHandlers = routes.get(`${path}/PATCH`) || []
routes.set(`${path}/PATCH`, [...currentHandlers, ...handlers])
}
const del = (path, ...handlers) => {
const currentHandlers = routes.get(`${path}/DELETE`) || []
routes.set(`${path}/DELETE`, [...currentHandlers, ...handlers])
}
const sanitizeUrl = (url, method) => {
const urlParams = url.split('/').slice(1)
// remove querystrings from the last parameter
const [lastParam] = urlParams[urlParams.length - 1].split('?')
urlParams.splice(urlParams.length - 1, 1)
// create the URL with our pattern
const allParams = [...urlParams, lastParam].join('/')
const sanitizedUrl = `/${allParams}/${method.toUpperCase()}`
return sanitizedUrl
}
const matchUrl = (sanitizedUrl) => {
for (const path of routes.keys()) {
const urlMatch = match(path, {
decode: decodeURIComponent,
})
const found = urlMatch(sanitizedUrl)
if (found) {
return path
}
}
return false
}
const serverHandler = async (request, response) => {
const sanitizedUrl = sanitizeUrl(request.url, request.method)
const match = matchUrl(sanitizedUrl)
if (match) {
const middlewaresAndControllers = routes.get(match)
console.log(middlewaresAndControllers)
response.statusCode = 200
response.end('Found')
} else {
response.statusCode = 404
response.end('Not found')
}
}
const run = (port) => {
const server = createMyServer()
server.listen(port)
}
return {
run,
get,
post,
patch,
put,
del
}
}
module.exports = App
使用和测试
index.js我在文件夹外创建了一个单独的文件src来测试我们的框架。
index.js
const App = require('./src/app')
const app = App()
app.get('/test/test2', function test() { }, function test2() { })
app.post('/test', (req, res) => console.log('test'))
app.patch('/test', (req, res) => console.log('test'))
app.put('/test', (req, res) => console.log('test'))
app.del('/test', (req, res) => console.log('test'))
const start = async () => {
app.run(3000)
}
start()
您可以执行此文件并使用您喜欢的 HTTP 客户端测试应用程序。
node index.js
下一步
在下一个教程中,我们将使用middlewares和controllers。如果您喜欢本教程,请毫不犹豫地发表您的看法,并关注我以第一手了解我的下一个教程。我试图每周至少写一篇教程。
猜你喜欢
- 2024-10-22 调用 Express API时出现奇怪的CORS错误怎么办?
- 2024-10-22 详解如何从零开始搭建Express+Vue开发环境
- 2024-10-22 从零开始学习nodejs+express--交互环境
- 2024-10-22 express开发(一)简介与搭建(express创建项目)
- 2024-10-22 express中间件原理connect(express和koa中间件原理的区别)
- 2024-10-22 蓝易云 - centos系统直接部署express教程。
- 2024-10-22 Node + Express + Mysql: Todo List项目让你成全栈
- 2024-10-22 我为 Express 开了外挂(cad中的express怎么显示为中文)
- 2024-10-22 七爪源码:让我们 Dockerize 一个 Node.js Express 应用程序
- 2024-10-22 Node实战篇:Express--jade模板引擎(七)
你 发表评论:
欢迎- 03-19基于layui+springcloud的企业级微服务框架
- 03-19开箱即用的前端开发模板,扩展Layui原生UI样式,集成第三方组件
- 03-19SpringMVC +Spring +Mybatis + Layui通用后台管理系统OneManageV2.1
- 03-19SpringBoot+LayUI后台管理系统开发脚手架
- 03-19layui下拉菜单form.render局部刷新方法亲测有效
- 03-19Layui 遇到的坑(记录贴)(layui chm)
- 03-19基于ASP.NET MVC + Layui的通用后台开发框架
- 03-19LayUi自定义模块的定义与使用(layui自定义表格)
- 最近发表
-
- 基于layui+springcloud的企业级微服务框架
- 开箱即用的前端开发模板,扩展Layui原生UI样式,集成第三方组件
- SpringMVC +Spring +Mybatis + Layui通用后台管理系统OneManageV2.1
- SpringBoot+LayUI后台管理系统开发脚手架
- layui下拉菜单form.render局部刷新方法亲测有效
- Layui 遇到的坑(记录贴)(layui chm)
- 基于ASP.NET MVC + Layui的通用后台开发框架
- LayUi自定义模块的定义与使用(layui自定义表格)
- Layui 2.9.11正式发布(layui2.6)
- Layui 2.9.13正式发布(layui2.6)
- 标签列表
-
- jdk (81)
- putty (66)
- rufus (78)
- 内网穿透 (89)
- okhttp (70)
- powertoys (74)
- windowsterminal (81)
- netcat (65)
- ghostscript (65)
- veracrypt (65)
- asp.netcore (70)
- wrk (67)
- aspose.words (80)
- itk (80)
- ajaxfileupload.js (66)
- sqlhelper (67)
- express.js (67)
- phpmailer (67)
- xjar (70)
- redisclient (78)
- wakeonlan (66)
- tinygo (85)
- startbbs (72)
- webftp (82)
- vsvim (79)
本文暂时没有评论,来添加一个吧(●'◡'●)