Node.js原生api搭建web服务器的方法步骤
node.js实现一个简单的web服务器还是比较简单的,以前利用express框架实现过『nodeJS搭一个简单的(代理)web服务器』。代码量很少,可是使用时需要安装依赖,多处使用难免有点不方便。于是便有了完全使用原生api来重写的想法,也当作一次node.js复习。
1、静态web服务器
'usestrict' consthttp=require('http') consturl=require('url') constfs=require('fs') constpath=require('path') constcp=require('child_process') constport=8080 consthostname='localhost' //创建http服务 lethttpServer=http.createServer(processStatic) //设置监听端口 httpServer.listen(port,hostname,()=>{ console.log(`appisrunningatport:${port}`) console.log(`url:http://${hostname}:${port}`) cp.exec(`explorerhttp://${hostname}:${port}`,()=>{}) }) //处理静态资源 functionprocessStatic(req,res){ constmime={ 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' } constrequestUrl=req.url letpathName=url.parse(requestUrl).pathname //中文乱码处理 pathName=decodeURI(pathName) letext=path.extname(pathName) //特殊url处理 if(!pathName.endsWith('/')&&ext===''&&!requestUrl.includes('?')){ pathName+='/' constredirect=`http://${req.headers.host}${pathName}` redirectUrl(redirect,res) } //解释url对应的资源文件路径 letfilePath=path.resolve(__dirname+pathName) //设置mime ext=ext?ext.slice(1):'unknown' constcontentType=mime[ext]||'text/plain' //处理资源文件 fs.stat(filePath,(err,stats)=>{ if(err){ res.writeHead(404,{'content-type':'text/html;charset=utf-8'}) res.end('404NotFound
') return } //处理文件 if(stats.isFile()){ readFile(filePath,contentType,res) } //处理目录 if(stats.isDirectory()){ lethtml="
- "
//遍历文件目录,以超链接返回,方便用户选择
fs.readdir(filePath,(err,files)=>{
if(err){
res.writeHead(500,{'content-type':contentType})
res.end('
${file} `
}
html+='
500ServerError
') return }else{ for(letfileoffiles){ if(file==='index.html'){ constredirect=`http://${req.headers.host}${pathName}index.html` redirectUrl(redirect,res) } html+=`500ServerError
') }) stream.pipe(res) }2、代理功能
//代理列表 constproxyTable={ '/api':{ target:'http://127.0.0.1:8090/api', changeOrigin:true } } //处理代理列表 functionprocessProxy(req,res){ constrequestUrl=req.url constproxy=Object.keys(proxyTable) letnot_found=true for(letindex=0;index=0){ not_found=false constelement=proxyTable[k] constnewUrl=element.target+requestUrl.slice(i+k.length) if(requestUrl!==newUrl){ constu=url.parse(newUrl,true) constoptions={ 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) } constrequest=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 } } returnnot_found } functiongetHeaderOverride(value){ if(Array.isArray(value)){ for(vari=0;i 3、完整版
服务器接收到http请求,首先处理代理列表proxyTable,然后再处理静态资源。虽然这里面只有二个步骤,但如果按照先后顺序编码,这种方式显然不够灵活,不利于以后功能的扩展。koa框架的中间件就是一个很好的解决方案。完整代码如下:
'usestrict' consthttp=require('http') consturl=require('url') constfs=require('fs') constpath=require('path') constcp=require('child_process') //处理静态资源 functionprocessStatic(req,res){ constmime={ 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' } constrequestUrl=req.url letpathName=url.parse(requestUrl).pathname //中文乱码处理 pathName=decodeURI(pathName) letext=path.extname(pathName) //特殊url处理 if(!pathName.endsWith('/')&&ext===''&&!requestUrl.includes('?')){ pathName+='/' constredirect=`http://${req.headers.host}${pathName}` redirectUrl(redirect,res) } //解释url对应的资源文件路径 letfilePath=path.resolve(__dirname+pathName) //设置mime ext=ext?ext.slice(1):'unknown' constcontentType=mime[ext]||'text/plain' //处理资源文件 fs.stat(filePath,(err,stats)=>{ if(err){ res.writeHead(404,{'content-type':'text/html;charset=utf-8'}) res.end('404NotFound
') return }//处理文件 if(stats.isFile()){ readFile(filePath,contentType,res) }//处理目录 if(stats.isDirectory()){ lethtml="" //遍历文件目录,以超链接返回,方便用户选择 fs.readdir(filePath,(err,files)=>{ if(err){ res.writeHead(500,{'content-type':contentType}) res.end('
' res.writeHead(200,{'content-type':'text/html'}) res.end(html) } }) } }) } //重定向处理 functionredirectUrl(url,res){ url=encodeURI(url) res.writeHead(302,{ location:url }) res.end() } //文件读取 functionreadFile(filePath,contentType,res){ res.writeHead(200,{'content-type':contentType}) conststream=fs.createReadStream(filePath) stream.on('error',function(){ res.writeHead(500,{'content-type':contentType}) res.end('500ServerError
') return }else{ for(letfileoffiles){ if(file==='index.html'){ constredirect=`http://${req.headers.host}${pathName}index.html` redirectUrl(redirect,res) } html+=`- ` } html+='
${file} 500ServerError
') }) stream.pipe(res) } //处理代理列表 functionprocessProxy(req,res){ constrequestUrl=req.url constproxy=Object.keys(proxyTable) letnot_found=true for(letindex=0;index=0){ not_found=false constelement=proxyTable[k] constnewUrl=element.target+requestUrl.slice(i+k.length) if(requestUrl!==newUrl){ constu=url.parse(newUrl,true) constoptions={ 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) } constrequest= 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 } } returnnot_found } functiongetHeaderOverride(value){ if(Array.isArray(value)){ for(vari=0;i { constctx={req,res} returnthis.handleRequest(ctx,fn) } returnhandleRequest } Router.prototype.handleRequest=function(ctx,fn){ fn(ctx) } //代理列表 constproxyTable={ '/api':{ target:'http://127.0.0.1:8090/api', changeOrigin:true } } constport=8080 consthostname='localhost' constappRouter=newRouter() //使用中间件 appRouter.use(async(ctx,next)=>{ if(processProxy(ctx.req,ctx.res)){ next() } }).use(async(ctx)=>{ processStatic(ctx.req,ctx.res) }) //创建http服务 lethttpServer=http.createServer(appRouter.callback()) //设置监听端口 httpServer.listen(port,hostname,()=>{ console.log(`appisrunningatport:${port}`) console.log(`url:http://${hostname}:${port}`) cp.exec(`explorerhttp://${hostname}:${port}`,()=>{}) })
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。