千家信息网

Node.js 中怎么利用原生API 搭建一个Web 服务器

发表于:2024-11-17 作者:千家信息网编辑
千家信息网最后更新 2024年11月17日,这期内容当中小编将会给大家带来有关Node.js 中怎么利用原生API 搭建一个Web 服务器,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1、静态 web 服务器
千家信息网最后更新 2024年11月17日Node.js 中怎么利用原生API 搭建一个Web 服务器

这期内容当中小编将会给大家带来有关Node.js 中怎么利用原生API 搭建一个Web 服务器,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

1、静态 web 服务器

'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'  // 创建 http 服务 let httpServer = http.createServer(processStatic) // 设置监听端口 httpServer.listen(port, hostname, () => {     console.log(`app is running at port:${port}`)     console.log(`url: http://${hostname}:${port}`)   cp.exec(`explorer http://${hostname}:${port}`, () => {}) }) // 处理静态资源 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 对应的资源文件路径   let filePath = path.resolve(__dirname + pathName)     // 设置 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('

404 Not Found

') return } // 处理文件 if (stats.isFile()) { readFile(filePath, contentType, res) } // 处理目录 if (stats.isDirectory()) { let html = "
    " // 遍历文件目录,以超链接返回,方便用户选择 fs.readdir(filePath, (err, files) => { if (err) { res.writeHead(500, { 'content-type': contentType }) res.end('

    500 Server Error

    ') return } else { for (let file of files) { if (file === 'index.html') { const redirect = `http://${req.headers.host}${pathName}index.html` redirectUrl(redirect, res) } html += `
  • ${file}
  • ` } html += '
' 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('

500 Server Error

') }) 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、完整版

服务器接收到 http 请求,首先处理代理列表 proxyTable,然后再处理静态资源。虽然这里面只有二个步骤,但如果按照先后顺序编码,这种方式显然不够灵活,不利于以后功能的扩展。koa 框架的中间件就是一个很好的解决方案。完整代码如下:

'use strict'  const http = require('http') const url = require('url') const fs = require('fs') const path = require('path') const cp = require('child_process') // 处理静态资源 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 对应的资源文件路径   let filePath = path.resolve(__dirname + pathName)     // 设置 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('

404 Not Found

') return } // 处理文件 if (stats.isFile()) { readFile(filePath, contentType, res) } // 处理目录 if (stats.isDirectory()) { let html = "
    " // 遍历文件目录,以超链接返回,方便用户选择 fs.readdir(filePath, (err, files) => { if (err) { res.writeHead(500, { 'content-type': contentType }) res.end('

    500 Server Error

    ') return } else { for (let file of files) { if (file === 'index.html') { const redirect = `http://${req.headers.host}${pathName}index.html` redirectUrl(redirect, res) } html += `
  • ${file}
  • ` } html += '
' 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('

500 Server Error

') }) 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) { // 记录上一次执行中间件的位置 let index = -1 return dispatch(0) function dispatch (i) { // 理论上 i 会大于 index,因为每次执行一次都会把 i递增, // 如果相等或者小于,则说明next()执行了多次 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) }) // 创建 http 服务 let httpServer = http.createServer(appRouter.callback()) // 设置监听端口 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 服务器了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注行业资讯频道。

0