在实现了路由以后,就能够以此为基础实现服务器了。css
实现服务器分为如下几个步骤:html
示例代码:/lesson28/lib/http.js前端
接下来,按步骤实现每部分代码。node
// 引入建立服务器所需的模块
const http = require('http')
const url = require('url')
const querystring = require('querystring')
const zlib = require('zlib')
const fs = require('fs')
const { Form } = require('multiparty')
// 引入服务器配置
const {
HTTP_PORT,
HTTP_ROOT,
HTTP_UPLOAD
} = require('../config')
// 引入路由模块的查找路由方法
const { findRouter } = require('./router')
const server = http.createServer((req, res) => {
// 服务器代码
})
// 监听配置的端口
server.listen(HTTP_PORT)
// 打印建立服务器成功信息
console.log(`Server started at ${HTTP_PORT}`)
复制代码
要处理全部请求接口,须要的参数为method(请求方法)、pathname(请求接口路径)、query(query数据)、post(post数据)、files(文件数据)。git
首先,根据method(请求方法)、pathname(请求接口路径),获取在路由配置时,已经配置好的相应接口的回调函数。github
其次,若回调函数存在,则直接将参数传入回调函数处理。浏览器
最后,若回调函数不存在,则默认为请求一个静态文件,便可将文件读取以后发送给前端。bash
// 引入建立服务器所需的模块
...
// 引入服务器配置
...
// 引入路由模块的查找路由方法
...
const server = http.createServer((req, res) => {
// 经过路由处理请求数据的公共方法
async function processData(method, pathname, query, post, files) {
const callback = findRouter(method, pathname) // 获取处理请求的回调函数
// 若回调函数存在,则表示路由有配置相应的数据处理,即该请求不是获取静态文件。
if (callback) {
try {
// 根据路由处理接口数据
await callback(res, query, post, files)
} catch (error) {
// 出现错误的处理
res.writeHead(500)
res.write('Internal Server Error')
res.end()
}
} else {
// 若回调函数不存在,则表示该请求为请求一个静态文件,如html、css、js等
...
}
}
})
// 监听配置的端口
server.listen(HTTP_PORT)
// 打印建立服务器成功信息
console.log(`Server started at ${HTTP_PORT}`)
复制代码
根据请求的method,将请求分为POST请求、GET请求。服务器
若为POST请求,则须要进一步判断是普通数据请求,仍是文件请求,并分别进行处理。app
而GET请求,只须要将数据传入processData方法进行处理,在processData方法中,区分GET请求获取数据,仍是获取静态文件。
// 引入建立服务器所需的模块
...
// 引入服务器配置
...
// 引入路由模块的查找路由方法
...
const server = http.createServer((req, res) => {
// 解析请求数据
// 获取请求路径及query数据
const method = req.method
const {
pathname,
query
} = url.parse(req.url, true)
// 处理POST请求
if (method === 'POST') {
// POST请求分为数据请求、文件上传请求,区分并进行处理
...
} else { // 处理GET请求
// 经过路由处理数据,由于此时是GET请求,只有query数据
processData(method, url, query, {}, {})
}
// 经过路由处理请求数据的公共方法
async function processData(method, pathname, query, post, files) {
...
}
})
// 监听配置的端口
server.listen(HTTP_PORT)
// 打印建立服务器成功信息
console.log(`Server started at ${HTTP_PORT}`)
复制代码
判断请求头的content-type为application/x-www-form-urlencoded时,表示该请求只是单纯传输数据,能够直接当作字符串处理。
若请求头的content-type不对,则表示该请求是上传文件,能够用multiparty进行处理。
// 引入建立服务器所需的模块
...
// 引入服务器配置
...
// 引入路由模块的查找路由方法
...
const server = http.createServer((req, res) => {
// 解析请求数据
// 获取请求路径及query数据
const method = req.method
const {
pathname,
query
} = url.parse(req.url, true)
// 处理POST请求
if (method === 'POST') {
// 根据请求头的content-type属性值,区分是普通POST请求,仍是文件请求。
// content-type为application/x-www-form-urlencoded时,表示是普通POST请求
// 普通POST请求直接进行处理,文件请求使用multiparty处理
if (req.headers['content-type'].startsWith('application/x-www-form-urlencoded')) {
// 普通POST请求
let arr = [] // 存储Buffer数据
// 接收数据
req.on('data', (buffer) => {
arr.push(buffer)
})
// 数据接收完成
req.on('end', () => {
const data = Buffer.concat(arr) // 合并接收到的数据
const post = querystring.parse(data.toString()) // 将接收到的数据转换为JSON
// 经过路由处理数据,由于此时是普通POST请求,不存在文件数据
processData(method, pathname, query, post, {})
})
} else {
// 文件POST请求
const form = new Form({
uploadDir: HTTP_UPLOAD // 指定文件存储目录
})
// 处理请求数据
form.parse(req)
let post = {} // 存储数据参数
let files = {} // 存储文件数据
// 经过field事件处理普通数据
form.on('field', (name, value) => {
post[name] = value
})
// 经过file时间处理文件数据
form.on('file', (name, file) => {
files[name] = file
})
// 处理错误
form.on('error', (error) => {
console.error(error)
})
// 数据传输完成时,触发close事件
form.on('close', () => {
// 经过路由处理数据,由于此时是POST文件请求,query、post、files数据都存在
processData(method, pathname, query, post, files)
})
}
} else { // 处理GET请求
// 经过路由处理数据,由于此时是GET请求,只有query数据
processData(method, url, query, {}, {})
}
// 经过路由处理请求数据的公共方法
async function processData(method, pathname, query, post, files) {
...
}
})
// 监听配置的端口
server.listen(HTTP_PORT)
// 打印建立服务器成功信息
console.log(`Server started at ${HTTP_PORT}`)
复制代码
GET请求能够直接用processData方法统一处理,若路由中未配置处理数据的方法,则表示该请求为获取静态文件,须要进行单独处理,不然只须要调用路由配置的回调函数处理便可。
// 引入建立服务器所需的模块
...
// 引入服务器配置
...
// 引入路由模块的查找路由方法
...
const server = http.createServer((req, res) => {
// 解析请求数据
// 获取请求路径及query数据
const method = req.method
const {
pathname,
query
} = url.parse(req.url, true)
// 处理POST请求
if (method === 'POST') {
...
} else { // 处理GET请求
// 经过路由处理数据,由于此时是GET请求,只有query数据
processData(method, url, query, {}, {})
}
// 经过路由处理请求数据的公共方法
async function processData(method, pathname, query, post, files) {
const callback = findRouter(method, pathname) // 获取处理请求的回调函数
// 若回调函数存在,则表示路由有配置相应的数据处理,即该请求不是获取静态文件。
if (callback) {
try {
// 根据路由处理接口数据
await callback(res, query, post, files)
} catch (error) {
// 出现错误的处理
res.writeHead(500)
res.write('Internal Server Error')
res.end()
}
} else {
// 若回调函数不存在,则表示该请求为请求一个静态文件,如html、css、js等
const filePath = HTTP_ROOT + pathname
// 检查文件是否存在
fs.stat(filePath, (error, stat) => {
if (error) {
// 出现错误表示文件不存在
res.writeHead(404)
res.write('Not Found')
res.end()
} else {
// 文件存在则进行读取
// 建立一个可读流。
const readStream = fs.createReadStream(filePath)
// 建立一个Gzip对象,用于将文件压缩成
const gz = zlib.createGzip()
// 向浏览器发送通过gzip压缩的文件,设置响应头,不然浏览器没法识别,会自动进行下载。
res.setHeader('content-encoding', 'gzip')
// 将读取的内容,经过gzip压缩以后,在经过管道推送到res中,因为res继承自Stream流,所以也能够接收管道的推送。
readStream.pipe(gz).pipe(res)
readStream.on('error', (error) => {
console.error(error)
})
}
})
}
}
})
// 监听配置的端口
server.listen(HTTP_PORT)
// 打印建立服务器成功信息
console.log(`Server started at ${HTTP_PORT}`)
复制代码
在server.js中引入封装的http模块:
const http = require('./lib/http')
复制代码
再使用node server.js启动服务器,就能够在浏览器中访问http://localhost:8080/index.html,看到html页面。