关于javascript:前端er什么时候你想写一个-HTTP-服务器

13次阅读

共计 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 提供了 urlquerystring 两个模块解析 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 流的形式流式传递来的,因而要通过监听 dataend 事件一点点的接管。

获取办法如下:

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 模块的外围 ——— requestresponse 都属于 Stream,一个是可读流,一个是可写流。

因而,彻底了解 http 模块,还须要深刻理解 Stream 流的相干常识。

总结

本篇基于最根底的 http 模块搭建了简略的 HTTP 服务器,并且实现了简略的 接管申请 发送申请

不过呢,真正的利用场景个别不会这么搭。社区有成熟稳固的 express 框架更适宜写 Node.js 服务;发送申请,能够用咱们最相熟的 axios ——— 没错,axios 也能够在 Node.js 中应用。

然而你可能不晓得,express 和 axios 的外围性能,都是基于 http 模块。

因而,根底很重要。地基不牢,地动山摇。把握了 http 模块,即便你在 express 中见到 Stream 的用法,也不至于不明所以。

这篇就到这里,下一篇咱们持续摸索 Stream 流,记得关注我哦。

往期精彩

本专栏会长期输入前端工程与架构方向的文章,已公布如下:

  • 以 Vuex 为引,一窥状态治理全貌
  • 前端架构师的 git 功力,你有几成火候?
  • 前端架构师神技,三招对立代码格调

如果喜爱我的文章,请点赞反对我吧!也欢送关注我的专栏。

申明: 本文原创,如有转载需要,请加微信 ruidoc 分割受权。

正文完
 0