使用nodejs服务器路由解析
上次学到的是简单的服务器,静态服务器,假设我想实现功能更复杂的服务器,比如说URL不止是定位一个文件,而是可以得到任何数据,或者说mock一些数据和前端进行交互,就需要复杂点的服务器了。
先来个最精简的代码:
var http = require('http')
var fs = require('fs') //用了上面两个模块
http.createServer(function(req,res){ //创建一个server,返回的是一个server对象,对象直接侦听8080端口。
switch(req.url){ //请求到了,需要处理对应的路由,路由就是localhost:8080这个域名后面的那一堆东西。路由的本质是后端根据路由去做对应的事情。
case '/getWeather': //req.url=/getWerther,发送JSON数据,并变成字符串
res.end(JSON.stringify({a:1,b:2}))
break; //停掉,这个相当于mock数据了
case '/user/123':
res.end(fs.readFileSync(__dirname + '/static/user.123')) //读这个文件,这个文件是什么不重要,记得路径能读到文件这个道理就行。
break;
default: //这两个都没匹配上,就认为是个静态文件
res.end(fs.readFileSync(__dirname + '/static' + req.url))
}
}).listen(8080) //当请求到了,只要是以localhost:8080作为前缀的都会到这个当前服务器上,请求到了。
文件的相对关系如图
static是html等文件数的文件夹。server.js就是服务器文件了,这里要记得__dirname就可以得出它的绝对路径了。
这时候,终端启动一下服务器,然后,具体操作就变得可控了,域名加路由。
我想访问文件就把约定好的文件的路由后缀加上,这个路由不是指路径的时候,就区别于静态服务器了,路由就是自己设置好的指令类似的,就能触发服务器的下一步具体操作。想mock天气数据,路由就改成约定好的那个,想怎样就怎样。当路由都不符合约定的指令了,就当成文件路径,静态服务器的那种操作了。
结合前面学到的ajax,可以自己模拟后端数据,然后做效果了。
假设一个网站没有登录功能,我们就可以用这个实现一个sserver,路由已经不是文件路径了,可以自行设置,然后服务器看到路由,就对应到设置好逻辑的模板上了,读取数据,用模板把数据拼装成字符串,发给前端。
但是它mock数据的时候,还是有瑕疵,我想传递不同的参数返回不同的数据,怎么办?
比如,在JS 文件的代码是:
var xhr = new XMLHttpRequest()
xhr.open('GET',"getWeather?city=beijing",true)
xhr.send()
xhr.onload = function(){
console.log(JSON.parse(xhr.responseText))
}
再把server的代码改一下:
var http = require('http')
var fs = require('fs')
var url = require('url') //加了URL模块
http.createServer(function(req,res){
var pathobj = url.parse(req.url,true)
console.log(pathobj)
//这里主要是在JS文件中的路由改成getweather?city=beijing,看看url的对象里,city=beijing是什么。
switch(req.url){
case '/getWeather':
res.end(JSON.stringify({a:1,b:2}))
break;
case '/user/123':
res.end(fs.readFileSync(__dirname + '/static/user.text'))
break;
default:
res.end(fs.readFileSync(__dirname + '/static' + req.url))
}
}).listen(8080)
如图:
这个过程是这样的,当发送getWeather?city=beijing这个请求时,设置的前两个路由都匹配不上,就当成本地文件路径处理,也找不到,所以报错了。
换句话说,对于我们的请求,匹配的时候,只要匹配pathname就行了,而不是url,我要得到的是那个query,重要的就是pathname,query了。
这时候,改下代码:
var http = require('http')
var fs = require('fs')
var url = require('url')
http.createServer(function(req,res){
var pathobj = url.parse(req.url,true)
console.log(pathobj)
//路由必须=url.pathname
//这里的城市判断就是url.query对象里的city。
switch(pathobj.pathname){
case '/getWeather':
var ret
if(pathobj.query.city=='beijing'){
ret = {
city:'beijing',
weather:'晴天'
}
}else{
ret={
city:pathobj.query.city,
weather:'不知道'
}
}
res.end(JSON.stringify(ret))
break;
case '/user/123':
res.end(fs.readFileSync(__dirname + '/static/user.text'))
break;
default:
res.end(fs.readFileSync(__dirname + '/static' + pathobj.pathname))
}
}).listen(8080)
这样就mock了数据了。路由就是需要自己一个一个去设置的。
一个比较完善的服务器代码
var http = require('http')
var path = require('path')
var fs = require('fs')
var url = require('url')
var routes = { //第一部分就是routes,匹配路由或者url的各种情况是key,value就是对应的处理了。
'/a': function(req, res){
res.end('match /a, query is:' + JSON.stringify(req.query))
},
'/b': function(req, res){
res.end('match /b')
},
'/a/c': function(req, res){
res.end('match /a/c')
},
'/search': function(req, res){
res.end('username='+req.body.username+',password='+req.body.password)
} //通过输入routes[key]得到对应的值
}
//主函数入口:
var server = http.createServer(function(req, res){
routePath(req, res) //处理方法
})
server.listen(8080)
console.log('visit http://localhost:8080' )
function routePath(req, res){
var pathObj = url.parse(req.url, true) //解析url,
console.log(pathObj)
var handleFn = routes[pathObj.pathname] //得到路由,从routes里去匹配,
if(handleFn){
req.query = pathObj.query
//参考 https://nodejs.org/en/docs/guides/anatomy-of-an-http-transaction/
// post json 解析
var body = ''
req.on('data', function(chunk){ //
body += chunk
}).on('end', function(){
req.body = parseBody(body)
handleFn(req, res)
})
}else {
staticRoot(path.resolve(__dirname, 'static'), req, res)
} //都匹配不上,当成静态文件处理。
}
function staticRoot(staticPath, req, res){
var pathObj = url.parse(req.url, true)
var filePath = path.join(staticPath, pathObj.pathname)
fs.readFile(filePath,'binary', function(err, content){
if(err){
res.writeHead('404', 'haha Not Found')
return res.end()
}
res.writeHead(200, 'Ok')
res.write(content, 'binary')
res.end()
})
}
//如果匹配上了,就进入这个逻辑。
function parseBody(body){
var obj = {}
body.split('&').forEach(function(str){
obj[str.split('=')[0]] = str.split('=')[1]
})
return obj
}
为什么不加端口就能访问
首先域名被购买了,域名本身有作用,在购买域名的网站上,它把域名绑定一个IP,IP 就是服务器地址,发请求,就会发到对应IP上,后面没写端口,就是默认的80端口,80端口到了服务器,这时候在逻辑上又启动了一个服务器,叫代理服务器,请求到了我写的代理服务器上,发现url是它,那我就转发到我当前机器的另外一个端口上,也就是它自己网站所启动的端口,到了它对应端口的服务器上。
上面做了很多例子,都是要输入端口的,这是因为在服务器里没有把域名和IP绑定,设置好了,就可以通过域名直接得到了。而野路子出来的域名没有备案,端口的意思就是防止服务器被好多野路子文件一起占用,到时候一个请求该如何对应一个正确的回应?开一个端口,就是一个使用名额,也更形象为一种约定,分流嘛。