# 路由
关于什么是路由,我就不过多的介绍了,在学习 vue 的时候有做过详细的笔记。
在编写调试 Node.js 项目的时候,如果修改了代码,则需要频繁的手动 close 掉,然后再重启,非常繁琐。
当前,我们可以使用 nodemon 或 node-dev 解决这一问题:
# 1、安装
npm i nodemon -g
# 2、使用
nodemon <文件名>
提示
如果不想看后面的升级过程,直接看结果的,可以直接 ✍ 查看路由案例 (opens new window)
# 一、路由示例
思考打印结果:
http
.createServer((req, res) => {
console.log(req.url)
res.end()
})
.listen(3000, '127.0.0.1', function () {
console.log('Server running at http://127.0.0.1:3000')
})
# 1、简单路由
- server.js
const http = require('http')
const fs = require('fs')
http
.createServer((req, res) => {
const myURL = new URL(req.url, 'http://127.0.0.1')
switch (myURL.pathname) {
case '/home':
res.writeHead(200, { 'Content-Type': 'text/html;charset=utf8' })
res.write(fs.readFileSync('./static/home.html'), 'utf-8')
break
case '/login':
res.writeHead(200, { 'Content-Type': 'text/html;charset=utf8' })
res.write(fs.readFileSync('./static/login.html'), 'utf-8')
break
default:
res.writeHead(404, { 'Content-Type': 'text/html;charset=utf8' })
res.write(fs.readFileSync('./static/404.html'), 'utf-8')
}
res.end()
})
.listen(3000, '127.0.0.1', function () {
console.log('Server running at http://127.0.0.1:3000')
})
# 2、升级(降低耦合度)
- server.js
const http = require('http')
const route = require('./route')
http
.createServer((req, res) => {
const myURL = new URL(req.url, 'http://127.0.0.1')
route(req, res, myURL.pathname)
res.end()
})
.listen(3000, '127.0.0.1', function () {
console.log('Server running at http://127.0.0.1:3000')
})
- route.js
const fs = require('fs')
function route(req, res, pathname) {
switch (pathname) {
case '/home':
res.writeHead(200, { 'Content-Type': 'text/html;charset=utf8' })
res.write(fs.readFileSync('./static/home.html'), 'utf-8')
break
case '/login':
res.writeHead(200, { 'Content-Type': 'text/html;charset=utf8' })
res.write(fs.readFileSync('./static/login.html'), 'utf-8')
break
default:
res.writeHead(404, { 'Content-Type': 'text/html;charset=utf8' })
res.write(fs.readFileSync('./static/404.html'), 'utf-8')
}
}
module.exports = route
# 3、升级(switch 方式 改为 对象方式)
- server.js
const http = require('http')
const route = require('./route')
http
.createServer((req, res) => {
const myURL = new URL(req.url, 'http://127.0.0.1')
// 1、细节一
// 使用try...catch达到类似switch中default🚩的效果
try {
route[myURL.pathname](req, res)
} catch (error) {
route['/404'](req, res)
}
res.end()
})
.listen(3000, '127.0.0.1', function () {
console.log('Server running at http://127.0.0.1:3000')
})
- route.js
const fs = require('fs')
const route = {
'/home': (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html;charset=utf8' })
res.write(fs.readFileSync('./static/home.html'), 'utf-8')
},
'/login': (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html;charset=utf8' })
res.write(fs.readFileSync('./static/login.html'), 'utf-8')
},
'/404': (req, res) => {
res.writeHead(404, { 'Content-Type': 'text/html;charset=utf8' })
res.write(fs.readFileSync('./static/404.html'), 'utf-8')
},
// 解决图标请求
'/favicon.ico': (req, res) => {
// 2、细节二✨
res.writeHead(200, { 'Content-Type': 'image/x-icon;charset=utf8' })
res.write(fs.readFileSync('./static/favicon.ico'))
}
}
module.exports = route
在浏览器上按
Ctrl + F5
强制刷新,才会显示图标。
# 4、升级(二次解耦、封装思想)
- server.js
const http = require('http')
const route = require('./route')
http
.createServer((req, res) => {
const myURL = new URL(req.url, 'http://127.0.0.1')
try {
route[myURL.pathname](req, res)
} catch (error) {
route['/404'](req, res)
}
})
.listen(3000, '127.0.0.1', function () {
console.log('Server running at http://127.0.0.1:3000')
})
- route.js
const fs = require('fs')
function render(res, path, type = '') {
res.writeHead(200, {
'Content-Type': `${type ? type : 'text/html'};charset=utf8`
})
res.write(fs.readFileSync(path), 'utf-8')
res.end()
}
const route = {
'/home': (req, res) => {
render(res, './static/login.html')
},
'/login': (req, res) => {
render(res, './static/login.html')
},
'/404': (req, res) => {
res.writeHead(404, {
'Content-Type': 'text/html;charset=utf8'
})
res.write(fs.readFileSync('./static/404.html'), 'utf-8')
res.end()
},
'/favicon.ico': (res) => {
render(req, res, './static/favicon.ico', 'image/x-icon')
}
}
module.exports = route
# 5、升级(封装思想)
- index.js(启动入口)
const server = require('./server')
server.start()
- server.js
const http = require('http')
const route = require('./route')
function start() {
http
.createServer((req, res) => {
const myURL = new URL(req.url, 'http://127.0.0.1')
try {
route[myURL.pathname](req, res)
} catch (error) {
route['/404'](req, res)
}
})
.listen(3000, '127.0.0.1', function () {
console.log('Server running at http://127.0.0.1:3000')
})
}
exports.start = start
# 二、api 接口路由示例
为了降低耦合度,下面 api 部分的代码不会写到 route.js 中,会单独抽离出来 api.js。
普通路由:text/html + static 文件
api 路由:application/json + data 数据
# 1、api 接口
- api.js
const fs = require('fs')
function render(res, data, type = '') {
res.writeHead(200, {
'Content-Type': `${type ? type : 'application/json'};charset=utf8`
})
res.write(data)
res.end()
}
const apiRoute = {
'/api/login': (req, res) => {
render(res, `{"ok":1}`)
}
}
module.exports = apiRoute
# 2、合并路由
使用 ES6 语法(浅拷贝)实现
- server.js
const http = require('http')
const route = require('./route')
const apiRoute = require('./apiRoute')
// 实现路由合并
const Router = {}
Object.assign(Router, route)
Object.assign(Router, apiRoute)
function start() {
http
.createServer((req, res) => {
const myURL = new URL(req.url, 'http://127.0.0.1')
try {
Router[myURL.pathname](req, res)
} catch (error) {
Router['/404'](req, res)
}
})
.listen(3000, '127.0.0.1', function () {
console.log('Server running at http://127.0.0.1:3000')
})
}
exports.start = start
# 3、升级(降低耦合度)
- index.js
const server = require('./server')
const route = require('./route')
const api = require('./api')
// 提前注册(合并)路由
server.use(route)
server.use(api)
server.start()
- server.js
const http = require('http')
const Router = {}
function use(obj) {
Object.assign(Router, obj)
}
function start() {
http
.createServer((req, res) => {
const myURL = new URL(req.url, 'http://127.0.0.1')
try {
Router[myURL.pathname](req, res)
} catch (error) {
Router['/404'](req, res)
}
})
.listen(3000, '127.0.0.1', function () {
console.log('Server running at http://127.0.0.1:3000')
})
}
exports.start = start
exports.use = use
# 三、路由的简单应用
# 1、数据请求(get、post)
① 前端:
- index.html
<body>
<!-- 注册 -->
<div>
<div>
用户名:
<input id="username" />
</div>
<div>
密码:
<input type="password" id="password" />
</div>
<div>
<button id="login_get">登录1</button>
<button id="login_post">登录2</button>
</div>
</div>
<script>
var username = document.querySelector('#username')
var password = document.querySelector('#password')
var login_get = document.querySelector('#login_get')
var login_post = document.querySelector('#login_post')
login_get.onclick = () => {
// 思考:为什么http://127.0.0.1:3000可以省略
fetch(
`http://127.0.0.1:3000/api/login_get?username=${username.value}&password=${password.value}`
)
.then((res) => res.json())
.then((res) => {
console.log(res)
})
}
login_post.onclick = () => {
fetch('http://127.0.0.1:3000/api/login_post', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
// 字符串化
body: JSON.stringify({
username: username.value,
password: password.value
})
})
// json格式化
.then((response) => response.json())
.then((res) => {
console.log(res)
})
.catch((error) => console.log(error))
}
</script>
</body>
② 后端:
- api.js
const fs = require('fs')
function render(res, data, type = '') {
res.writeHead(200, {
'Content-Type': `${type ? type : 'application/json'};charset=utf8`
})
res.write(data)
res.end()
}
const apiRoute = {
'/api/login_get': (req, res) => {
// render(res, `{"ok":1}`)
const myURL = new URL(req.url, 'http://127.0.0.1')
// get🚩方法
if (
myURL.searchParams.get('username') === 'lencamo' &&
myURL.searchParams.get('password') === '123'
) {
render(res, `{"ok":1}`)
} else {
render(res, `{"ok":0}`)
}
},
'/api/login_post': (req, res) => {
// 收集数据
var post = ''
req.on('data', (chunk) => {
post += chunk
})
req.on('end', () => {
console.log(post)
// 将数据还原为json格式
post = JSON.parse(post)
if (post.username === 'lencamo' && post.password === '123') {
render(res, `{"ok":1}`)
} else {
render(res, `{"ok":0}`)
}
})
}
}
module.exports = apiRoute
# 2、不同类型的静态资源访问(升级)
为了获取 Content-Type 类型,我们可以使用 mime 包来自动识别并获取。
- route.js
const fs = require('fs')
const path = require('path')
const mime = require('mime')
function render(res, path, type = '') {
res.writeHead(200, {
'Content-Type': `${type ? type : 'text/html'};charset=utf8`
})
res.write(fs.readFileSync(path), 'utf-8')
res.end()
}
const route = {
'/home': (req, res) => {
render(res, './static/login.html')
},
'/': (req, res) => {
render(res, './static/login.html')
},
'/login': (req, res) => {
render(res, './static/login.html')
},
'/404': (req, res) => {
// 1、🚩静态资源处理
if (readStaticFile(req, res)) {
return
}
// 2、非静态资源处理
res.writeHead(404, {
'Content-Type': 'text/html;charset=utf8'
})
res.write(fs.readFileSync('./static/404.html'), 'utf-8')
res.end()
}
// ico的内容也可以同时处理了,不用写
// '/favicon.ico': (res) => {
// render(req, res, './static/favicon.ico', 'image/x-icon')
// }
}
// 3、静态处理函数
function readStaticFile(req, res) {
const myURL = new URL(req.url, 'http://127.0.0.1:3000')
// 使用绝对路径、mime包
const pathname = path.join(__dirname, '/static/', myURL.pathname)
// 对pathname路径处理
if (myURL.pathname === '/') return false
// 处理静态资源
if (fs.existsSync(pathname)) {
// Content-Type类型自动识别(需要扩展名文件)
console.log(mime.getType(myURL.pathname.split('.')[1]))
render(res, pathname, mime.getType(myURL.pathname.split('.')[1]))
return true
} else {
return false
}
}
module.exports = route