溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Node.js 中怎么利用原生API 搭建一個Web 服務(wù)器

發(fā)布時間:2021-07-21 09:21:12 來源:億速云 閱讀:177 作者:Leah 欄目:服務(wù)器

這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)Node.js 中怎么利用原生API 搭建一個Web 服務(wù)器,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

1、靜態(tài) web 服務(wù)器

'use strict'  const http = require('http') const url = require('url') const fs = require('fs') const path = require('path') const cp = require('child_process')  const port = 8080 const hostname = 'localhost'  // 創(chuàng)建 http 服務(wù) let httpServer = http.createServer(processStatic) // 設(shè)置監(jiān)聽端口 httpServer.listen(port, hostname, () => {     console.log(`app is running at port:${port}`)     console.log(`url: http://${hostname}:${port}`)   cp.exec(`explorer http://${hostname}:${port}`, () => {}) }) // 處理靜態(tài)資源 function processStatic(req, res) {     const mime = {     css: 'text/css',     gif: 'image/gif',     html: 'text/html',     ico: 'image/x-icon',     jpeg: 'image/jpeg',     jpg: 'image/jpeg',     js: 'text/javascript',     json: 'application/json',     pdf: 'application/pdf',     png: 'image/png',     svg: 'image/svg+xml',     woff: 'application/x-font-woff',     woff2: 'application/x-font-woff',     swf: 'application/x-shockwave-flash',     tiff: 'image/tiff',     txt: 'text/plain',     wav: 'audio/x-wav',     wma: 'audio/x-ms-wma',     wmv: 'video/x-ms-wmv',     xml: 'text/xml'   }     const requestUrl = req.url     let pathName = url.parse(requestUrl).pathname     // 中文亂碼處理   pathName = decodeURI(pathName)     let ext = path.extname(pathName)     // 特殊 url 處理   if (!pathName.endsWith('/') && ext === '' && !requestUrl.includes('?')) {     pathName += '/'     const redirect = `http://${req.headers.host}${pathName}`     redirectUrl(redirect, res)   }     // 解釋 url 對應(yīng)的資源文件路徑   let filePath = path.resolve(__dirname + pathName)     // 設(shè)置 mime    ext = ext ? ext.slice(1) : 'unknown'   const contentType = mime[ext] || 'text/plain'    // 處理資源文件   fs.stat(filePath, (err, stats) => {         if (err) {       res.writeHead(404, { 'content-type': 'text/html;charset=utf-8' })       res.end('<h2>404 Not Found</h2>')             return     }         // 處理文件     if (stats.isFile()) {       readFile(filePath, contentType, res)     }         // 處理目錄     if (stats.isDirectory()) {             let html = "<head><meta charset = 'utf-8'/></head><body><ul>"       // 遍歷文件目錄,以超鏈接返回,方便用戶選擇       fs.readdir(filePath, (err, files) => {                 if (err) {           res.writeHead(500, { 'content-type': contentType })           res.end('<h2>500 Server Error</h2>')           return         } else {                     for (let file of files) {                         if (file === 'index.html') {                             const redirect = `http://${req.headers.host}${pathName}index.html`               redirectUrl(redirect, res)             }             html += `<li><a href='${file}'>${file}</a></li>`           }           html += '</ul></body>'           res.writeHead(200, { 'content-type': 'text/html' })           res.end(html)         }       })     }   }) } // 重定向處理 function redirectUrl(url, res) {   url = encodeURI(url)   res.writeHead(302, {     location: url   })   res.end() } // 文件讀取 function readFile(filePath, contentType, res) {   res.writeHead(200, { 'content-type': contentType })   const stream = fs.createReadStream(filePath)   stream.on('error', function() {     res.writeHead(500, { 'content-type': contentType })     res.end('<h2>500 Server Error</h2>')   })   stream.pipe(res) }

2、代理功能

// 代理列表 const proxyTable = {   '/api': {     target: 'http://127.0.0.1:8090/api',     changeOrigin: true   } } // 處理代理列表 function processProxy(req, res) {     const requestUrl = req.url     const proxy = Object.keys(proxyTable)     let not_found = true   for (let index = 0; index < proxy.length; index++) {           const k = proxy[index]           const i = requestUrl.indexOf(k)           if (i >= 0) {         not_found = false         const element = proxyTable[k]               const newUrl = element.target + requestUrl.slice(i + k.length)               if (requestUrl !== newUrl) {                  const u = url.parse(newUrl, true)                   const options = {             hostname : u.hostname,              port     : u.port || 80,             path     : u.path,                    method   : req.method,             headers  : req.headers,             timeout  : 6000           }                   if(element.changeOrigin){             options.headers['host'] = u.hostname + ':' + ( u.port || 80)           }                   const request = http           .request(options, response => {                         // cookie 處理             if(element.changeOrigin && response.headers['set-cookie']){               response.headers['set-cookie'] = getHeaderOverride(response.headers['set-cookie'])             }             res.writeHead(response.statusCode, response.headers)             response.pipe(res)           })           .on('error', err => {                        res.statusCode = 503             res.end()           })         req.pipe(request)       }             break     }   }     return not_found } function getHeaderOverride(value){     if (Array.isArray(value)) {          for (var i = 0; i < value.length; i++ ) {      value[i] = replaceDomain(value[i])    }   } else {     value = replaceDomain(value)   }     return value } function replaceDomain(value) {     return value.replace(/domain=[a-z.]*;/,'domain=.localhost;').replace(/secure/, '') }

3、完整版

服務(wù)器接收到 http 請求,首先處理代理列表  proxyTable,然后再處理靜態(tài)資源。雖然這里面只有二個步驟,但如果按照先后順序編碼,這種方式顯然不夠靈活,不利于以后功能的擴展。koa  框架的中間件就是一個很好的解決方案。完整代碼如下:

'use strict'  const http = require('http') const url = require('url') const fs = require('fs') const path = require('path') const cp = require('child_process') // 處理靜態(tài)資源 function processStatic(req, res) {     const mime = {     css: 'text/css',     gif: 'image/gif',     html: 'text/html',     ico: 'image/x-icon',     jpeg: 'image/jpeg',     jpg: 'image/jpeg',     js: 'text/javascript',     json: 'application/json',     pdf: 'application/pdf',     png: 'image/png',     svg: 'image/svg+xml',     woff: 'application/x-font-woff',     woff2: 'application/x-font-woff',     swf: 'application/x-shockwave-flash',     tiff: 'image/tiff',     txt: 'text/plain',     wav: 'audio/x-wav',     wma: 'audio/x-ms-wma',     wmv: 'video/x-ms-wmv',     xml: 'text/xml'   }     const requestUrl = req.url     let pathName = url.parse(requestUrl).pathname     // 中文亂碼處理   pathName = decodeURI(pathName)     let ext = path.extname(pathName)     // 特殊 url 處理   if (!pathName.endsWith('/') && ext === '' && !requestUrl.includes('?')) {     pathName += '/'     const redirect = `http://${req.headers.host}${pathName}`     redirectUrl(redirect, res)   }     // 解釋 url 對應(yīng)的資源文件路徑   let filePath = path.resolve(__dirname + pathName)     // 設(shè)置 mime    ext = ext ? ext.slice(1) : 'unknown'   const contentType = mime[ext] || 'text/plain'    // 處理資源文件   fs.stat(filePath, (err, stats) => {        if (err) {       res.writeHead(404, { 'content-type': 'text/html;charset=utf-8' })       res.end('<h2>404 Not Found</h2>')             return     }    // 處理文件     if (stats.isFile()) {       readFile(filePath, contentType, res)     }    // 處理目錄     if (stats.isDirectory()) {             let html = "<head><meta charset = 'utf-8'/></head><body><ul>"       // 遍歷文件目錄,以超鏈接返回,方便用戶選擇       fs.readdir(filePath, (err, files) => {                 if (err) {           res.writeHead(500, { 'content-type': contentType })           res.end('<h2>500 Server Error</h2>')           return         } else {                     for (let file of files) {                         if (file === 'index.html') {                            const redirect = `http://${req.headers.host}${pathName}index.html`               redirectUrl(redirect, res)             }             html += `<li><a href='${file}'>${file}</a></li>`           }           html += '</ul></body>'           res.writeHead(200, { 'content-type': 'text/html' })           res.end(html)         }       })     }   }) } // 重定向處理 function redirectUrl(url, res) {   url = encodeURI(url)   res.writeHead(302, {     location: url   })   res.end() } // 文件讀取 function readFile(filePath, contentType, res) {   res.writeHead(200, { 'content-type': contentType })   const stream = fs.createReadStream(filePath)   stream.on('error', function() {     res.writeHead(500, { 'content-type': contentType })     res.end('<h2>500 Server Error</h2>')   })   stream.pipe(res) } // 處理代理列表 function processProxy(req, res) {   const requestUrl = req.url   const proxy = Object.keys(proxyTable)   let not_found = true   for (let index = 0; index < proxy.length; index++) {         const k = proxy[index]         const i = requestUrl.indexOf(k)         if (i >= 0) {       not_found = false       const element = proxyTable[k]       const newUrl = element.target + requestUrl.slice(i + k.length)        if (requestUrl !== newUrl) {         const u = url.parse(newUrl, true)         const options = {           hostname : u.hostname,            port     : u.port || 80,           path     : u.path,                  method   : req.method,           headers  : req.headers,           timeout  : 6000         };         if(element.changeOrigin){           options.headers['host'] = u.hostname + ':' + ( u.port || 80)         }         const request =            http.request(options, response => {                             // cookie 處理             if(element.changeOrigin && response.headers['set-cookie']){               response.headers['set-cookie'] = getHeaderOverride(response.headers['set-cookie'])             }             res.writeHead(response.statusCode, response.headers)             response.pipe(res)           })           .on('error', err => {             res.statusCode = 503             res.end()           })         req.pipe(request)       }       break     }   }   return not_found } function getHeaderOverride(value){   if (Array.isArray(value)) {       for (var i = 0; i < value.length; i++ ) {                  value[i] = replaceDomain(value[i])       }   } else {       value = replaceDomain(value)   }   return value} function replaceDomain(value) {   return value.replace(/domain=[a-z.]*;/,'domain=.localhost;').replace(/secure/, '') } function compose (middleware) {   if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')     for (const fn of middleware) {         if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')   }     return function (context, next) {     // 記錄上一次執(zhí)行中間件的位置         let index = -1     return dispatch(0)        function dispatch (i) {       // 理論上 i 會大于 index,因為每次執(zhí)行一次都會把 i遞增,       // 如果相等或者小于,則說明next()執(zhí)行了多次            if (i <= index) return Promise.reject(new Error('next() called multiple times'))             index = i       let fn = middleware[i]             if (i === middleware.length) fn = next       if (!fn) return Promise.resolve()            try {                return Promise.resolve(fn(context, function next () {              return dispatch(i + 1)         }))       } catch (err) {                  return Promise.reject(err)       }     }   } } function Router(){     this.middleware = [] } Router.prototype.use = function (fn){     if (typeof fn !== 'function') throw new TypeError('middleware must be a function!')   this.middleware.push(fn)    return this} Router.prototype.callback= function() {     const fn = compose(this.middleware)     const handleRequest = (req, res) => {     const ctx = {req, res}     return this.handleRequest(ctx, fn)   }   return handleRequest } Router.prototype.handleRequest= function(ctx, fn) {   fn(ctx) }  // 代理列表 const proxyTable = {   '/api': {     target: 'http://127.0.0.1:8090/api',     changeOrigin: true   } }  const port = 8080 const hostname = 'localhost' const appRouter = new Router()  // 使用中間件 appRouter.use(async(ctx,next)=>{   if(processProxy(ctx.req, ctx.res)){     next()   } }).use(async(ctx)=>{   processStatic(ctx.req, ctx.res) })  // 創(chuàng)建 http 服務(wù) let httpServer = http.createServer(appRouter.callback())  // 設(shè)置監(jiān)聽端口 httpServer.listen(port, hostname, () => {   console.log(`app is running at port:${port}`)   console.log(`url: http://${hostname}:${port}`)   cp.exec(`explorer http://${hostname}:${port}`, () => {}) })

上述就是小編為大家分享的Node.js 中怎么利用原生API 搭建一個Web 服務(wù)器了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI