使用nodejs服务器路由解析

2017-09-22  本文已影响0人  流着万条永远的河

上次学到的是简单的服务器,静态服务器,假设我想实现功能更复杂的服务器,比如说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这个请求时,设置的前两个路由都匹配不上,就当成本地文件路径处理,也找不到,所以报错了。

但是,我们可以看很多东西,先看看显示的url对象的内容,如图:
换句话说,对于我们的请求,匹配的时候,只要匹配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绑定,设置好了,就可以通过域名直接得到了。而野路子出来的域名没有备案,端口的意思就是防止服务器被好多野路子文件一起占用,到时候一个请求该如何对应一个正确的回应?开一个端口,就是一个使用名额,也更形象为一种约定,分流嘛。

上一篇下一篇

猜你喜欢

热点阅读