用 Node.js 能够快速搭建一个 http 服务器, 本文将会手把手从头搭建, 最后还会实现一个简易的 express 服务器, 开始吧~
javascript
首先, 搭建 http 服务器, 须要先引用 Node.js 的核心模块: http, 实际上这是一个对象, 利用 http 上的 createServer 方法来建立一个 http 服务器, createServer 方法须要接受一个事件函数, 用于处理请求和响应html
const http = require('http')
const server = http.createServer((req, res) => {
// req 是请求对象, 能够获取请求的一些方法路径数据等属性
// res 是响应对象, 能够进行设置响应数据等操做
})
复制代码
到如今, 这个 http 服务器的雏形已经基本搭建好了!
可是想一想还差点东西, 一个 url 里包含 协议 域名 端口, 咱们还没指定端口呢.前端
const http = require('http')
const server = http.createServer((req, res) => {
})
server.listen(8000) // 监听 8000 端口
复制代码
OK, 大功告成
java
如今这个 http 服务器的框架已经搭好了. 启动一下, 在浏览器输入localhost:8000
试一下吧
什么? 你说你试了一下, 没有响应?
固然, 咱们这里尚未返回任何数据呢, 若是没有访问数据, 浏览器端确定是显示无响应的.
git
这里咱们先随便返回点数据给浏览器, 而后重启服务器github
const http = require('http')
const server = http.createServer((req, res) => {
res.end('这是我返回的数据噢!')
})
server.listen(8000)
复制代码
相信你已经看到页面上显示的....一堆乱码了吧^_^, 是的, 由于 JavaScript 默认字符集对中文的支持很差. 咱们须要指定一下返回数据的 Content-Type
express
const http = require('http')
const server = http.createServer((req, res) => {
res.setHeader("Content-Type","text/html;charset=utf-8")
res.end('这是我返回的数据噢!')
})
server.listen(8000)
复制代码
接下来, 咱们须要对路由进行处理, 如今咱们无论访问什么路径, 都统一返回同样的数据.
接下来咱们实现一下, 访问 /a , /b, /c 三个路由, 返回不一样的数据
数组
以前说过, req 对象上存放着请求的一些属性. req.url
上记录着请求的路径, 获取后就能够判断访问路径来返回不一样的数据了浏览器
const {url} = req
复制代码
完整代码:服务器
const http = require('http')
const server = http.createServer((req, res) => {
const {url} = req
res.setHeader("Content-Type","text/html;charset=utf-8")
if(url === '/a') res.end(`访问a路由`)
else if(url === '/b') res.end(`访问b路由`)
else if(url === '/c') res.end(`访问c路由`)
})
server.listen(8000)
复制代码
接下来, 咱们对查询参数作一下处理, 这时候, 是否是想到了什么, 上面咱们的 url 没有考虑到查询参数的状况, 路由里应该滤除掉查询参数, 咱们来一并处理
咱们知道, 查询参数的形式是 a=x&b=x
, 这里为了方便使用, 咱们引用另外一个模块 querystring
, 他能够把查询参数字符串切割成键值对形式
const http = require('http')
const querystring = require('querystring')
const server = http.createServer((req, res) => {
const {url} = req
const path = url.split('?')[0]
const query = querystring.parse(url.split('?')[1]) // 保存着查询参数的对象
res.setHeader("Content-Type","text/html;charset=utf-8")
if(path === '/a') res.end(`访问a路由, 查询参数对象为${JSON.stringify(query)}`) // 返回序列化的查询参数
else if(path === '/b') res.end(`访问b路由`)
else if(path === '/c') res.end(`访问c路由`)
})
server.listen(8000)
复制代码
OK, 接下来要作啥呢? 想了想, 咱们目前好像只处理了 GET 请求, 那咱们来处理一下 POST 请求吧.
同样的, 请求的 method 能够经过req.method
获取
这里要注意: req 对象实现了 ReadableStream 接口, 咱们能够用信息流的方式读取传来的数据 (关于流的概念能够看后面个人文章)
let postData = ''
req.on('data', chunk => { // 接收数据流
postData += chunk.toString() // 拼接信息流, 注意chunk是二进制格式, 须要转为二进制
})
req.on('end', () => {
// 接收数据流完毕的回调函数
})
复制代码
完整代码 :
const http = require('http')
const querystring = require('querystring')
const server = http.createServer((req, res) => {
const {url, method} = req
const path = url.split('?')[0]
const query = querystring.parse(url.split('?')[1])
res.setHeader("Content-Type","text/html;charset=utf-8")
if(path === '/a' && method === 'GET') res.end(`访问a路由, 查询参数对象为${JSON.stringify(query)}`)
else if(path === '/b' && method === 'GET') res.end(`访问b路由`)
else if(path === '/c' && method === 'GET') res.end(`访问c路由`)
else if(path === 'p' && method === 'POST'){
let postData = ''
req.on('data', chunk => { // 接收数据流
postData += chunk.toString() // 拼接信息流, 注意chunk是二进制格式, 须要转为二进制
})
req.on('end', () => {
res.end(`我接受到了数据了:${postData}`)
})
}
})
server.listen(8000)
复制代码
OK, 来回顾一下咱们作了什么:
咱们如今来回顾一下本身的代码, 能够看到, 在路由处理的部分有一堆的 if else, 假如每多一个路由就多一个 if , 那就太冗余了.
这里咱们用一个数组来存放一个个路由对象, 路由对象里包含了路径, 方法, 回调等必要信息
const http = require('http')
const querystring = require('querystring')
const server = http.createServer((req, res) => {
const {url, method: realMethod} = req
const realPath= url.split('?')[0]
const query = querystring.parse(url.split('?')[1])
let router = [] // 存放路由信息
res.setHeader("Content-Type","text/html;charset=utf-8")
router.push({
path: '/a',
method: 'GET',
handler(req, res){
res.end(`访问a路由, 查询参数对象为${JSON.stringify(query)}`)
}
})
router.push({
path: '/b',
method: 'GET',
handler(req, res){
res.end(`访问b路由`)
}
})
router.push({
path: '/c',
method: 'GET',
handler(req, res){
res.end(`访问c路由`)
}
})
router.push({
path: '/p',
method: 'POST',
handler(req, res){
let postData = ''
req.on('data', chunk => {
postData += chunk.toString()
})
req.on('end', () => {
res.end(`我接受到了数据了:${postData}`)
})
}
})
// 统一处理路由
router.forEach(route => {
let {path, method, handler} = route
console.log(realPath, realMethod)
if(realPath === path && realMethod === method){
return handler()
}
})
})
server.listen(8000)
复制代码
是否是感受稍微好看一点了, 加一个路由就 push 一个路由对象.
咱们离最终目标很接近了, 接下来, 让咱们模仿 express , 写一个 express 形式的 http 服务器(^▽^)
先来看看 express 是怎么写的
const express = require("express");
const app = express();
app.get("/a",
(req, res) => {
res.end("a路由");
}
);
app.get("/b",
(req, res) => {
res.end('b路由');
});
app.listen(3000, () => {
console.log("Example app listen at 3000");
});
复制代码
能够看到, 导出的 express 是一个函数, 函数内部会 new 一个实例对象出来, 大概的架子即是这样.
const http = require('http')
class Express{
}
module.exports = function(){
return new Express()
}
复制代码
接下来让咱们实现完整代码:
const http = require('http')
class Express{
constructor(){
this.router = [] // 存放路由对象
}
get(path, handler){
this.router.push({
path,
method: 'GET',
handler
})
}
post(path, handler){
this.router.push({
path,
method: 'POST',
handler
})
}
listen(port, listenCallback){
const server = http.createServer((req,res) => {
const {url, method:realMethod} = req
const realPath = url.split('?')[0]
this.router.forEach((route) => { // 遍历路由对象
const {path, method, handler} = route
if(realPath === path && method === realMethod){
handler(req, res)
}
})
})
server.listen(port)
listenCallback()
}
}
module.exports = function(){
return new Express()
}
复制代码
到这里, 咱们已经简单将咱们的 http 服务器改写成 express 形式了, 不过考虑的细节还远远不够 express 那么完善.
撒花ヾ(◍°∇°◍)ノ゙
大三小前端一枚, 最近在写一些博客, 欢迎关注(^▽^)
我的博客地址 : github