共计 4537 个字符,预计需要花费 12 分钟才能阅读完成。
前端 er,什么时候,你想写一个 HTTP 服务器?
当你第一次接触工程化的我的项目时,看到我的项目控制台正在 building,过一会忽然跳出一个 URL 地址,你点开它竟然是你刚写好的网页,好神奇。
当你接后端伙伴的接口时,你把数据带去,接口居然给你返回 500 谬误;你去找后端,后端说这样传不行,你不晓得为啥不行,反正依照他说的改完,返回 200 胜利了。
有时候你的申请莫名其妙的就跨域了,后端说让你们本人解决,你就找呀找解决方案。然而为什么会跨域?后端怎么配置的,你也不分明。
终于有一天,你痛定思痛,决定改过自新,肯定要本人搭一个 HTTP 服务器,彻底理清这外面的弯弯绕绕,从此回绝被忽悠,回绝做只听命令的大头兵。
然而话说回来了,怎么动手呢?
别急,这都给您备好啦。写 HTTP 服务器须要后端语言,不用说,天然首选 Node.js。
上面咱们基于 Node.js 的 http
模块,一起搭建一个的 HTTP 服务器。
http 模块
一个超简略的 HTTP web 服务器的示例:
const http = require('http')
const server = http.createServer((request, response) => {
response.statusCode = 200
response.end('hello world')
})
server.listen(3000)
这里引入了 http 模块,提供了 createServer
办法,传入一个回调函数,创立了一个服务器。
当初把代码写进 index.js
,再超简略的把它运行起来:
$ node index.js
关上浏览器,输出 http://localhost:3000
,就能看到网页显示的 hello world
了。
代码分析
http.createServer 办法的参数是一个回调函数,这个回调函数有两个参数 —— 它们是 HTTP 服务器的外围。
第一个参数是申请对象 request
,第二个参数是响应对象 response
。你能够把它们看作两个袋子,一个袋子里装着申请相干的数据,一个袋子里装着响应相干的操作。
request
蕴含了具体的申请数据,也就是咱们前端调接口传递过去的数据。通过它能够获取申请头,申请参数,申请办法等等。
response
次要用于响应相干的设置和操作。什么是响应?就是我收到了客户端的申请,我能够设置状态码为 200 并返给前端数据;或者设置状态码为 500 并返给前端谬误。
总之一句话,调用接口返回什么,是由 response
决定的。
事实上,createServer 返回的是一个 EventEmitter,因而下面的写法等同于这样:
const http = require('http')
const server = http.createServer()
server.on('request', (request, response) => {
response.statusCode = 200
response.end('hello world')
}).listen(3000)
request 解析
用户发动申请的相干数据,都蕴含在 request 对象中。
这些数据蕴含罕用的申请办法,申请头,url,申请体等等数据。
const {method, url, headers} = request
method 示意申请办法可间接应用,headers 返回申请头对象,应用也比拟简便:
const {headers} = request
const userAgent = headers['user-agent'] // 申请头全是小写字母
唯独 url 字符串不好解析,外面蕴含了协定,hostname,path,query 等等。
所幸 Node.js 提供了 url
和 querystring
两个模块解析 url 字符串。
URL 解析
先看一个 url 模块的例子:
const url = require('url') // 解析 url 字符串
var string = 'http://localhost:8888/start?foo=bar&hello=world'
var url_object = url.parse(string)
// {protocol: 'http:', host:'localhost:8888', pathname: '/start', query: 'foo=bar&hello=world'}
看到了吧,url
模块能够将一个残缺的 URL 地址字符串,拆分成一个蕴含各局部属性的对象。
然而美中不足,其余局部都解析进去了,唯独 query 还是一个字符串。
query
须要二次解析。怎么办呢?这时候第二个模块 querystring
出场了:
const querystring = require('querystring') // 解析 query 字符串
var string = 'http://localhost:8888/start?foo=bar&hello=world'
var url_object = url.parse(string) // {query: 'foo=bar&hello=world'}
var query_object = querystring.parse(url_object.query)
// {foo: 'bar', hello: 'world'}
这下就完满了。用 url + querystring 组合,能够残缺解析你的 URL。
申请体解析
对于 POST
或者 PUT
申请,咱们须要接管申请体的数据。
这里申请体比拟非凡,它不是一次性传过来的数据,而是通过 Stream
流的形式流式传递来的,因而要通过监听 data 和 end 事件一点点的接管。
获取办法如下:
server.on('request', (request, response) => {let body = []
request.on('data', chunk => {
// 这里的 chunk 是一个 Buffer
body.push(chunk)
})
request.on('end', () => {body = Buffer.concat(body)
})
console.log(body.toString())
})
response 设置
服务器收到客户端申请,要通过 response 设置如何响应给客户端。
响应设置,次要就是状态码,响应头,响应体三局部。
首先是 状态码,比方 404:
response.statusCode = 404
再有是 响应头:
response.setHeader('Content-Type', 'text/plain')
最初是 响应体:
response.end('找不到数据')
这三局部也能够合在一起:
response
.writeHead(404, {
'Content-Type': 'text/plain',
'Content-Length': 49
})
.end('找不到数据')
发送 http 申请
http 模块除了承受客户端的申请,还能够作为客户端去发送申请。
发送 http 申请是指,在 Node.js 中申请其余接口获取数据。
发送申请次要通过 http.request
办法来实现。
GET
上面是一个发送 GET 申请的简略示例:
const http = require('http')
const options = {
hostname: 'nodejs.cn',
port: 80,
path: '/learn',
method: 'GET'
}
const req = http.request(options, res => {console.log(` 状态码: ${res.statusCode}`)
res.on('data', d => {process.stdout.write(d)
})
res.on('end', () => {})
})
req.on('error', error => {console.error(error)
})
req.end()
应用 http.request 发送申请后,必须显示调用 req.end()
来示意实现申请发送。
POST
与下面 GET 申请基本一致,区别是看 申请体怎么传:
const http = require('http')
const options = {
hostname: 'nodejs.cn',
port: 80,
path: '/learn',
method: 'POST'
}
const body = {
sex: 'man',
name: 'ruims'
}
const req = http.request(options, res => {console.log(` 状态码: ${res.statusCode}`)
res.on('data', d => {process.stdout.write(d)
})
res.on('end', () => {})
})
req.on('error', error => {console.error(error)
})
req.write(JSON.stringify(body)) // 传递 body 参数写法
req.end()
诡异之处
看到这里,如果你对 nodejs 了解不深,可能会发现几处诡异的中央。
比方,失常状况下 POST 申请传递 body
参数可能是这样的:
var body = {desc: '申请体参数'}
var req = http.request({
path: '/',
method: 'POST',
data: body
})
而下面说到的正确姿态是这样的:
var body = {desc: '申请体参数'}
var req = http.request({
path: '/',
method: 'POST'
})
req.write(JSON.stringify(body))
还有下面获取申请体也是如此。不能间接通过 request.body
获取,非得这样:
let body = []
request.on('data', chunk => {body.push(chunk)
})
request.on('end', () => {body = Buffer.concat(body)
})
这几处应该是大家了解 http 模块最困惑的中央。其实刨根问底,这不属于 http 的难点,而是 Node.js 中 Stream
流的特有语法。
事实上,http 模块的外围 ——— request
和 response
都属于 Stream
,一个是可读流,一个是可写流。
因而,彻底了解 http 模块,还须要深刻理解 Stream
流的相干常识。
总结
本篇基于最根底的 http 模块搭建了简略的 HTTP 服务器,并且实现了简略的 接管申请 和发送申请。
不过呢,真正的利用场景个别不会这么搭。社区有成熟稳固的 express 框架更适宜写 Node.js 服务;发送申请,能够用咱们最相熟的 axios ——— 没错,axios 也能够在 Node.js 中应用。
然而你可能不晓得,express 和 axios 的外围性能,都是基于 http 模块。
因而,根底很重要。地基不牢,地动山摇。把握了 http 模块,即便你在 express 中见到 Stream
的用法,也不至于不明所以。
这篇就到这里,下一篇咱们持续摸索 Stream
流,记得关注我哦。
往期精彩
本专栏会长期输入前端工程与架构方向的文章,已公布如下:
- 以 Vuex 为引,一窥状态治理全貌
- 前端架构师的 git 功力,你有几成火候?
- 前端架构师神技,三招对立代码格调
如果喜爱我的文章,请点赞反对我吧!也欢送关注我的专栏。
申明: 本文原创,如有转载需要,请加微信 ruidoc
分割受权。