<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          前端er,什么時(shí)候,你想寫一個(gè) HTTP 服務(wù)器?

          共 7120字,需瀏覽 15分鐘

           ·

          2021-12-10 14:59

          作者:楊成功

          簡(jiǎn)介專注前端工程與架構(gòu)產(chǎn)出

          來源:SegmentFault  思否社區(qū)


          前端 er,什么時(shí)候,你想寫一個(gè) HTTP 服務(wù)器?


          當(dāng)你第一次接觸工程化的項(xiàng)目時(shí),看到項(xiàng)目控制臺(tái)正在 building,過一會(huì)突然跳出一個(gè) URL 地址,你點(diǎn)開它居然是你剛寫好的網(wǎng)頁,好神奇。


          當(dāng)你接后端同伴的接口時(shí),你把數(shù)據(jù)帶去,接口竟然給你返回 500 錯(cuò)誤;你去找后端,后端說這樣傳不行,你不知道為啥不行,反正按照他說的改完,返回 200 成功了。


          有時(shí)候你的請(qǐng)求莫名其妙的就跨域了,后端說讓你們自己處理,你就找呀找解決方案。但是為什么會(huì)跨域?后端怎么配置的,你也不清楚。


          終于有一天,你痛定思痛,決定痛改前非,一定要自己搭一個(gè) HTTP 服務(wù)器,徹底理清這里面的彎彎繞繞,從此拒絕被忽悠,拒絕做只聽命令的大頭兵。


          但是話說回來了,怎么入手呢?


          別急,這都給您備好啦。寫 HTTP 服務(wù)器需要后端語言,不用說,自然首選 Node.js。


          下面我們基于 Node.js 的 http 模塊,一起搭建一個(gè)的 HTTP 服務(wù)器。


          http 模塊



          一個(gè)超簡(jiǎn)單的 HTTP web 服務(wù)器的示例:


          const http = require('http')

          const server = http.createServer((request, response) => {
            response.statusCode = 200
            response.end('hello world')
          })

          server.listen(3000)


          這里引入了 http 模塊,提供了 createServer 方法,傳入一個(gè)回調(diào)函數(shù),創(chuàng)建了一個(gè)服務(wù)器。


          現(xiàn)在把代碼寫進(jìn) index.js ,再超簡(jiǎn)單的把它運(yùn)行起來:

          $ node index.js


          打開瀏覽器,輸入 http://localhost:3000,就能看到網(wǎng)頁顯示的 hello world 了。


          代碼剖析


          http.createServer 方法的參數(shù)是一個(gè)回調(diào)函數(shù),這個(gè)回調(diào)函數(shù)有兩個(gè)參數(shù) —— 它們是 HTTP 服務(wù)器的核心。


          第一個(gè)參數(shù)是請(qǐng)求對(duì)象 request,第二個(gè)參數(shù)是響應(yīng)對(duì)象 response。你可以把它們看作兩個(gè)袋子,一個(gè)袋子里裝著請(qǐng)求相關(guān)的數(shù)據(jù),一個(gè)袋子里裝著響應(yīng)相關(guān)的操作。


          request 包含了詳細(xì)的請(qǐng)求數(shù)據(jù),也就是我們前端調(diào)接口傳遞過來的數(shù)據(jù)。通過它可以獲取請(qǐng)求頭,請(qǐng)求參數(shù),請(qǐng)求方法等等。


          response 主要用于響應(yīng)相關(guān)的設(shè)置和操作。什么是響應(yīng)?就是我收到了客戶端的請(qǐng)求,我可以設(shè)置狀態(tài)碼為 200 并返給前端數(shù)據(jù);或者設(shè)置狀態(tài)碼為 500 并返給前端錯(cuò)誤。


          總之一句話,調(diào)用接口返回什么,是由 response 決定的。


          事實(shí)上,createServer 返回的是一個(gè) EventEmitter,因此上面的寫法等同于這樣:


          const http = require('http')
          const server = http.createServer()

          server.on('request', (request, response) => {
            response.statusCode = 200
            response.end('hello world')
          }).listen(3000)


          request 解析



          用戶發(fā)起請(qǐng)求的相關(guān)數(shù)據(jù),都包含在 request 對(duì)象中。


          這些數(shù)據(jù)包含常用的請(qǐng)求方法,請(qǐng)求頭,url,請(qǐng)求體等等數(shù)據(jù)。


          const { method, url, headers } = request


          method 表示請(qǐng)求方法可直接使用,headers 返回請(qǐng)求頭對(duì)象,使用也比較簡(jiǎn)便:


          const { headers } = request
          const userAgent = headers['user-agent'] // 請(qǐng)求頭全是小寫字母


          唯獨(dú) url 字符串不好解析,里面包含了協(xié)議,hostname,path,query 等等。


          所幸 Node.js 提供了 url 和 querystring 兩個(gè)模塊解析 url 字符串。


          URL 解析


          先看一個(gè) 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 模塊可以將一個(gè)完整的 URL 地址字符串,拆分成一個(gè)包含各部分屬性的對(duì)象。


          但是美中不足,其他部分都解析出來了,唯獨(dú) query 還是一個(gè)字符串。


          query 需要二次解析。怎么辦呢?這時(shí)候第二個(gè)模塊 querystring 出場(chǎng)了:


          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。


          請(qǐng)求體解析


          對(duì)于 POST 或者 PUT 請(qǐng)求,我們需要接收請(qǐng)求體的數(shù)據(jù)。


          這里請(qǐng)求體比較特殊,它不是一次性傳過來的數(shù)據(jù),而是通過 Stream 流的方式流式傳遞來的,因此要通過監(jiān)聽 data 和 end 事件一點(diǎn)點(diǎn)的接收。


          獲取方法如下:


          server.on('request', (request, response) => {
            let body = []
            request.on('data', chunk => {
              // 這里的 chunk 是一個(gè) Buffer
              body.push(chunk)
            })
            request.on('end', () => {
              body = Buffer.concat(body)
            })
            console.log(body.toString())
          })


          response 設(shè)置


          服務(wù)器收到客戶端請(qǐng)求,要通過 response 設(shè)置如何響應(yīng)給客戶端。


          響應(yīng)設(shè)置,主要就是狀態(tài)碼,響應(yīng)頭,響應(yīng)體三部分。


          首先是狀態(tài)碼,比如 404:


          response.statusCode = 404


          再有是響應(yīng)頭


          response.setHeader('Content-Type''text/plain')


          最后是響應(yīng)體


          response.end('找不到數(shù)據(jù)')


          這三部分也可以合在一起:


          response
            .writeHead(404, {
              'Content-Type''text/plain',
              'Content-Length': 49
            })
            .end('找不到數(shù)據(jù)')


          發(fā)送 http 請(qǐng)求



          http 模塊除了接受客戶端的請(qǐng)求,還可以作為客戶端去發(fā)送請(qǐng)求。


          發(fā)送 http 請(qǐng)求是指,在 Node.js 中請(qǐng)求其他接口獲取數(shù)據(jù)。


          發(fā)送請(qǐng)求主要通過 http.request 方法來實(shí)現(xiàn)。


          GET


          下面是一個(gè)發(fā)送 GET 請(qǐng)求的簡(jiǎn)單示例:


          const http = require('http')
          const options = {
            hostname: 'nodejs.cn',
            port: 80,
            path: '/learn',
            method: 'GET'
          }

          const req = http.request(options, res => {
            console.log(`狀態(tài)碼: ${res.statusCode}`)
            res.on('data', d => {
              process.stdout.write(d)
            })
            res.on('end', () => {})
          })

          req.on('error', error => {
            console.error(error)
          })

          req.end()


          使用 http.request 發(fā)送請(qǐng)求后,必須顯示調(diào)用 req.end() 來表示完成請(qǐng)求發(fā)送。


          POST


          與上面 GET 請(qǐng)求基本一致,區(qū)別是看請(qǐng)求體怎么傳:


          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(`狀態(tài)碼: ${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 參數(shù)寫法

          req.end()


          詭異之處



          看到這里,如果你對(duì) nodejs 理解不深,可能會(huì)發(fā)現(xiàn)幾處詭異的地方。


          比如,正常情況下 POST 請(qǐng)求傳遞 body 參數(shù)可能是這樣的:


          var body = { desc: '請(qǐng)求體參數(shù)' }
          var req = http.request({
            path: '/',
            method: 'POST',
            data: body
          })


          而上面說到的正確姿勢(shì)是這樣的:


          var body = { desc: '請(qǐng)求體參數(shù)' }
          var req = http.request({
            path: '/',
            method: 'POST'
          })
          req.write(JSON.stringify(body))


          還有上面獲取請(qǐng)求體也是如此。不能直接通過 request.body 獲取,非得這樣:


          let body = []
          request.on('data', chunk => {
            body.push(chunk)
          })
          request.on('end', () => {
            body = Buffer.concat(body)
          })


          這幾處應(yīng)該是大家理解 http 模塊最困惑的地方。其實(shí)刨根問底,這不屬于 http 的難點(diǎn),而是 Node.js 中 Stream 流的特有語法。


          事實(shí)上,http 模塊的核心 ——— request 和 response 都屬于 Stream,一個(gè)是可讀流,一個(gè)是可寫流。


          因此,徹底理解 http 模塊,還需要深入了解 Stream 流的相關(guān)知識(shí)。


          總結(jié)



          本篇基于最基礎(chǔ)的 http 模塊搭建了簡(jiǎn)單的 HTTP 服務(wù)器,并且實(shí)現(xiàn)了簡(jiǎn)單的接收請(qǐng)求發(fā)送請(qǐng)求


          不過呢,真正的應(yīng)用場(chǎng)景一般不會(huì)這么搭。社區(qū)有成熟穩(wěn)定的 express 框架更適合寫 Node.js 服務(wù);發(fā)送請(qǐng)求,可以用我們最熟悉的 axios ——— 沒錯(cuò),axios 也可以在 Node.js 中使用。


          但是你可能不知道,express 和 axios 的核心功能,都是基于 http 模塊。


          因此,基礎(chǔ)很重要。地基不牢,地動(dòng)山搖。掌握了 http 模塊,即使你在 express 中見到 Stream 的用法,也不至于不明所以。


          這篇就到這里,下一篇我們繼續(xù)探索 Stream 流,記得關(guān)注我哦。




          點(diǎn)擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開更多互動(dòng)和交流,掃描下方”二維碼“或在“公眾號(hào)后臺(tái)回復(fù)“ 入群 ”即可加入我們的技術(shù)交流群,收獲更多的技術(shù)文章~

          - END -


          瀏覽 43
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  视频国产91| 蜜桃无码AV | 黄色免费视频网站 | 日本日逼 | 小早川玲子一区二区88AV |