编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

ExpressJS入门(第 1 部分)- 基础知识、方法和路由

wxchong 2024-10-22 17:54:19 开源技术 8 ℃ 0 评论

关注留言点赞,带你了解最流行的软件开发知识与最新科技行业趋势。

嘿,你有没有想过使用 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。如果您喜欢本教程,请毫不犹豫地发表您的看法,并关注我以第一手了解我的下一个教程。我试图每周至少写一篇教程。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表