美文网首页大前端-万物皆可JS
Nodejs实现一个简单的服务器

Nodejs实现一个简单的服务器

作者: ltaoo | 来源:发表于2016-11-05 11:48 被阅读931次

    之前使用 nodejs 完成了简陋版的静态服务器,了解了服务器的运行机制:

    • 1、创建服务器对象并监听用户请求
    • 2、设置路由来根据用户请求返回不同的响应结果

    但这里仅仅是返回了静态的结果,而没有动态的数据。如果需要有动态的数据,整个服务器的运行机制是怎么样的?

    • 1、同样是创建服务器对象并监听用户请求
    • 2、用户请求分为静态文件请求和获取动态页面。比如
      get 127.0.0.1:3000/index.htmlget 127.0.0.1:3000/index的结果是不同的。
    • 3、当请求127.0.0.1:3000/index时,触发render()函数,传入index.html作为模板,传入{name: 'ltaoo'}作为页面数据。render()函数获取index.html文件内容,遇到{name}表示需要替换为ltaoo

    这里可以使用模板引擎来替代自己解析html并插入数据。

    简单实现

    尝试实现最简陋版本的服务端程序,只有index页面并显示固定数据,不从数据库中获取数据、没有静态服务器(意味着没有样式与js脚本)。

    var http = require('http')
    var url = require('url')
    var fs = require('fs')
    var path = require('path')
    
    http.createServer(function (req, res){
      //解析请求
      var pathname = url.parse(req.url).pathname
      if(pathname === '/index') {
        //只处理请求 index 这一种情况
        fs.readFile(path.join(__dirname, './index.html'), 'utf-8', function (err, file){
          if(err) console.log(err)
          //假数据
          var data = {
            "title": "MVC project",
            "author": "ltaoo",
            "content": "a project content"
          }
          //获取html 文件并将其中的 {{xx}} 和 data 数据进行匹配。
          var pattern = /({{)[a-z]+(}})/g;
          var ary = file.match(pattern);
          for (var i = ary.length - 1; i >= 0; i--) {
            ary[i] = ary[i].replace(/\W/g, ')
          }
          //console.log(ary)
          // 将 html 文件内的变量替换
          for (var j = ary.length - 1; j >= 0; j--) {
            file = file.replace('{{' + ary[j] + '}}', data[ary[j]])
          }
          // 渲染页面
          res.writeHead(200, {"Content-Type": "text/html"})
          res.write(file)
          res.end()
        }) 
      }
    }).listen(3000)
    
    console.log('server is listening at port 3000');
    
    

    对应的模板文件index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Nodejs study</title>
    </head>
    <body>
      <article>
        <h1>{{title}}</h1>
        <p>{{author}}</p>
        <p>{{content}}</p>
      </article>
    </body>
    </html>
    

    运行后页面成功显示预期的数据。


    如何从该程序拓展成一个“比较”通用的服务器程序?

    拓展

    服务器的工作原理即接收请求,返回数据。查看express官方文档:

    app.get('/', function (req, res) {
        res.render('index', {title: "Express", content: "this is a express project"});
    });
    

    这里等同于

    if(pathname === '/index') {
        //coding
    }
    

    可以从上面的简陋版程序中看到,这一段代码属于响应请求的核心部分,代码量比较多且逻辑相同。所以抽象为函数。即 render() 函数。
    该函数接收模板、数据作为参数,并在函数内渲染页面。

    render 函数

    根据 pathname 来调用 template。比如

    • get index, index.html
    • get articles, articles.html
    • get article/2016/07/26/first, 调用article.html
    function render(template, data) {
        // 这个地方的代码和简陋版的原理差不多,同样是先获取到模板文件,可以是 jade,然后将数据替换掉模板中的预留位置。最后渲染页面。这个地方是请求的终点。
    }
    

    这里涉及到模板引擎,如果使用 express 内置引擎,需要现在项目开始位置配置 views 的路径,则 render 函数将会在 views 目录下查找模板。

    MVC 架构

    OK,上面讲到了渲染页面,但是问题在于数据是怎么得到的,又是怎么传递给 render 函数的。如果 render 函数属于 V,则 M 和 C 都暂时还没有出现。

    MVC 模式是软件工程的一种软件架构模式。...专业人员可以通过自身的专长分组:

    • 控制器(Controller)-负责转发请求,对请求进行处理。
    • 视图(View)-界面设计人员进行图形界面设计。
    • 模型(Model)-程序员编写程序应有功能(实现算法等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。

    OK,按照维基百科的说明,MVC架构能够让专业人员分组进行工作,各自负责自己擅长的方面。是否能理解成这是属于三个方面,通过函数调用来实现联系。

    比如说,用户请求 /index ,这个请求将交给 Controller 来处理,Controller 知道了用户在请求 /index,就让 Model 从数据库中调取数据,调取完成后将数据交给 Controller,Controller拿到这个数据后,把数据和 View 进行组合,最后返回页面给用户。即

    V -> C -> M -> C -> V

    所以 View 应该仅仅是模板,没有任何的逻辑。上面代码中, render 函数应该是属于 Controller,因为它接受到了请求,并且处理 data 和 template,现在缺的是 Model。 路由是属于 Controller ,即 if(pathname === '/html') 开始,因为这里接收到请求并根据请求来处理数据,即逻辑部分。

    Model

    Model 属于和数据库交互的部分,在这里定义了方法来实现增删改查,并封装成模块将方法暴露给外部使用。假设:

    
    // 连接数据库
    
    function fetch() {
    
        //从数据库中获取全部数据
    
    
    }
    
    function fetchItem(id) {
    
        // 根据id 从数据库中获取到指定数据
    
    
    }
    
    exports.fetch = fetch
    
    exports.fetchItem = fetchItem
    
    

    然后就可以在 Controller 内调用方法获取数据,这样 Controller 就将 Model 和 View 联系在一起了。

    var http = require('http'),
        url = require('url'),
        fs = require('fs'),
        path = require('path')
    
    http.createServer(function (req, res){
      //解析请求
      var pathname = url.parse(req.url).pathname
      if(pathname === '/index') {
        //只处理这一种情况
        fs.readFile(path.join(__dirname, './index.html'), 'utf-8', function (err, file){
          if(err) console.log(err)
          /*var data = {
            "title": "MVC project",
            "author": "litao",
            "content": "a project content"
          }*/
    
          //将 Model 加载进来
          var model = require('model.js');
          // 获取数据
          var data = model.fetch();
          //parse html code and insert data
          var pattern = /({{)[a-z]+(}})/g;
          var ary = file.match(pattern);
          for (var i = ary.length - 1; i >= 0; i--) {
            ary[i] = ary[i].replace(/\W/g, ')
          }
          //console.log(ary)
          // 将 html 文件内的变量替换
          for (var j = ary.length - 1; j >= 0; j--) {
            file = file.replace('{{' + ary[j] + '}}', data[ary[j]])
          }
          // output 
          res.writeHead(200, {"Content-Type": "text/html"})
          res.write(file)
          res.end()
        }) 
      }
    }).listen(3000)
    
    console.log('server is listening at port 3000');
    
    

    代码整理

    我们有了 M ,有了 V,也有 C,但 C 没有分离出来。

    var http = require('http'),
        url = require('url'),
        fs = require('fs'),
        path = require('path')
    
    http.createServer(function (req, res){
      //解析请求
      var pathname = url.parse(req.url).pathname
      if(pathname === '/index') {
        //只处理这一种情况
        var index = require('index');
    
        index.index();
    
    
      }
    }).listen(3000)
    
    console.log('server is listening at port 3000');
    
    

    将业务逻辑拿出来单独作为一个文件

    
    exports.index = function () {    
    
        fs.readFile(path.join(__dirname, './index.html'), 'utf-8', function (err, file){
          if(err) console.log(err)
    
          //表示将 Model 加载进来
          var model = require('model.js');
          // 加载数据
          var data = model.fetch();
          //parse html code and insert data
          var pattern = /({{)[a-z]+(}})/g;
          var ary = file.match(pattern);
          for (var i = ary.length - 1; i >= 0; i--) {
            ary[i] = ary[i].replace(/\W/g, ')
          }
          //console.log(ary)
          // 将 html 文件内的变量替换
          for (var j = ary.length - 1; j >= 0; j--) {
            file = file.replace('{{' + ary[j] + '}}', data[ary[j]])
          }
          // output 
          res.writeHead(200, {"Content-Type": "text/html"})
          res.write(file)
          res.end()
        }) 
    
    }
    
    

    这样是否实现了一个 MVC 架构的服务端程序?

    总结

    对 nodeJs 的 http 模块有了一个基本的了解,对之后学习 koa 应该有很大的帮助。

    参考

    相关文章

      网友评论

        本文标题:Nodejs实现一个简单的服务器

        本文链接:https://www.haomeiwen.com/subject/tptguttx.html