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('500ServerError
')
return
}else{
for(letfileoffiles){
if(file==='index.html'){
constredirect=`http://${req.headers.host}${pathName}index.html`
redirectUrl(redirect,res)
}
html+=`${file} `
}
html+='
'
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
')
})
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}`,()=>{})
})
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。