溫馨提示×

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

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

Node.js原生api搭建web服務(wù)器的案例

發(fā)布時(shí)間:2021-02-18 09:48:26 來(lái)源:億速云 閱讀:153 作者:小新 欄目:web開發(fā)

這篇文章給大家分享的是有關(guān)Node.js原生api搭建web服務(wù)器的案例的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過(guò)來(lái)看看吧。

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 對(duì)應(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 請(qǐng)求,首先處理代理列表 proxyTable,然后再處理靜態(tài)資源。雖然這里面只有二個(gè)步驟,但如果按照先后順序編碼,這種方式顯然不夠靈活,不利于以后功能的擴(kuò)展。koa 框架的中間件就是一個(gè)很好的解決方案。完整代碼如下:

'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 對(duì)應(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 會(huì)大于 index,因?yàn)槊看螆?zhí)行一次都會(huì)把 i遞增, 
   // 如果相等或者小于,則說(shuō)明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}`, () => {}) 
})

感謝各位的閱讀!關(guān)于“Node.js原生api搭建web服務(wù)器的案例”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!

向AI問(wèn)一下細(xì)節(jié)

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

AI