使用 Node.js http 模块做一个 node-server
1. 最简单的服务器
http.createServer
返回 http.Server
的新实例,http.Server
类提供了一些可用的事件和方法,如 request
事件、listen
方法 等。
request
事件,在每次有请求时触发,它的事件监听函数接受两个参数:request
:<http.IncomingMessage>response
:<http.ServerResponse>
listen
方法,用于启动 HTTP 服务器监听连接。
// index.js
const http = require('http')
const server = http.createServer()
server.on('request', (request, response) => {
response.end('hello')
})
server.listen('8888', () => {
console.log('服务启动成功,请使用 8888 端口打开')
})
使用浏览器访问 http://127.0.0.1:8888/
:
2. 根据 url 返回不同的内容
// index.js(部分代码)
const http = require('http')
const server = http.createServer()
const fs = require('fs')
const path = require('path')
const publicDir = path.resolve(__dirname, 'public')
server.on('request', (request, response) => {
switch (request.url) {
case '/':
case '/index.html':
response.setHeader('Content-Type', 'text/html; charset=utf-8')
fs.readFile(path.resolve(publicDir, 'index.html'), (error, data) => {
if (error) throw error
response.end(data.toString())
})
break
case '/style.css':
response.setHeader('Content-Type', 'text/css; charset=utf-8')
fs.readFile(path.resolve(publicDir, 'style.css'), (error, data) => {
if (error) throw error
response.end(data.toString())
})
break
case '/main.js':
response.setHeader('Content-Type', 'text/javascript; charset=utf-8')
fs.readFile(path.resolve(publicDir, 'main.js'), (error, data) => {
if (error) throw error
response.end(data.toString())
})
break
default:
response.statusCode = 404
response.end()
}
})
现在,浏览器输入不同的 url,服务器就会返回不同的内容给浏览器,如:
但是,当访问不存在的路径,或路径中含有查询参数时,浏览器会显示 404 页面,这是因为查询参数也被包含在了 request.url
中。
3. 处理查询参数
使用 url
模块的 parse
方法,可以将字符串形式的 url 转成对象形式,如:
// index.js(部分代码)
const url = require('url')
server.on('request', (request, response) => {
const { pathname } = url.parse(request.url) // 关键代码
switch (pathname) {
case '/':
case '/index.html':
// ...
case '/style.css':
// ...
case '/main.js':
// ...
default:
// ...
}
})
现在,使用 pathname
去匹配 url 路径,即使 url 中带有查询参数,也能正确匹配。
4. 匹配任意文件
现在,这个 node-server 只能匹配到已经设置好的 url,如果新增文件,那么就要新增相应的 url,这明显是不好的。我们希望新增文件时,可以不用修改服务器配置,浏览器也能访问到新增的文件。
// index.js(部分代码)
server.on('request', (request, response) => {
const { pathname } = url.parse(request.url)
// 从路径中取到文件的名字,如果是空字符串就默认显示 index.html
const filename = pathname.substr(1) || 'index.html'
fs.readFile(path.resolve(publicDir, filename), (error, data) => {
if (error) throw error
response.end(data.toString())
})
})
服务器中新增 jquery.js 并直接在浏览器中访问:
5. 解决 bug
此时,当在浏览器中访问 main.js 时,会发现出现了乱码,这是由于没有设置 Content-Type
引起的。
解决方案就是根据访问的文件给 Content-Type
设置不同的值:
// index.js(部分代码)
const typeMap = {
'.html': 'text/html; charset=utf-8',
'.css': 'text/css; charset=utf-8',
'.js': 'text/javascript; charset=utf-8'
}
server.on('request', (request, response) => {
const { pathname } = url.parse(request.url)
const filename = pathname.substr(1) || 'index.html'
const endType = '.' + filename.split('.')[1] // 获取文件后缀
fs.readFile(path.resolve(publicDir, filename), (error, data) => {
if (error) {
throw error
} else {
response.setHeader('Content-Type', typeMap[endType])
response.end(data.toString())
}
})
})
此时再访问 main.js,就没有乱码了:
6. 处理不存在的文件
// index.js(部分代码)
server.on('request', (request, response) => {
// ...
fs.readFile(path.resolve(publicDir, filename), (error, data) => {
if (error) {
if (error.errno === -4058) { // 访问的资源不存在
response.statusCode = 404
fs.readFile(path.resolve(publicDir, '404.html'), (error, data) => {
response.end(data.toString())
})
} else if (error.errno === -4068) { // 访问目录
response.statusCode = 403
response.setHeader('Content-Type', 'text/plain; charset=utf-8')
response.end('无权查看目录内容')
} else {
response.statusCode = 500
response.setHeader('Content-Type', 'text/plain; charset=utf-8')
response.end('服务器发生未知错误,请稍后重试')
}
} else {
// ...
}
})
})
当访问不存在的资源:
当访问的是某个目录:
7. 处理非 GET 请求
因为这是一个静态服务器,所以我们要求它只能接受 GET 请求,不允许其他请求。
上图中,当在页面上使用表单发送 POST 请求时,由于服务器不存在对应的资源,所以会返回 404。
// index.js(部分代码)
server.on('request', (request, response) => {
// 处理非 GET 请求
if (request.method !== 'GET') {
response.statusCode = 405
response.setHeader('Content-Type', 'text/plain; charset=utf-8')
response.end('这是一个假的响应')
return
}
})
再次使用表单发送 POST 请求时,就会返回 405:
8. 添加缓存选项
没有添加缓存之前:
// index.js(部分代码)
server.on('request', (request, response) => {
fs.readFile(path.resolve(publicDir, filename), (error, data) => {
if (error) {
// ...
} else {
response.setHeader('Content-Type', typeMap[endType])
response.setHeader('Cache-Control', 'public, max-age=31536000') // 设置缓存
response.end(data.toString())
}
})
})
添加缓存之后:
转载自:https://juejin.cn/post/7107594929958256671