zoukankan      html  css  js  c++  java
  • Express 入门

      用Express写一个hello world 程序。在合适的地方打开命令行窗口(使用的git bash),mkdir express-tut && cd express-tut && npm init -y, 初始化项目。再 touch server.js,  用于项目入口。由于express 是第三方框架,我们要先安装它,npm install express -S,  最后code . (安装vs code 的时候,加入到path, 所以code . 就可以打开vscode)。server.js 代码如下:

    const express = require("express");
    const app = express();
    
    app.use((request, response) => {
        response.writeHead(200, { "Content-Type": "text/plain" });
        response.end("Hello, world!");
    });
    
    app.listen(8080, () => {
        console.log(`server listen at 8080`)
    });

      在git bash 中,输入node server 开启服务器,控制台显示‘server listen at 8080’ 表示开启成功。浏览器中输入localhost:8080 可以看到Hello World, 简单吧。项目虽然简单,但也看到了express 项目开发时的核心,app.use() 方法,我们在它的回调函数中来处理请求,app.use()最大的好处就是它可以调用多次,这样,我们就可以把请求处理放到不同的app.use 的回调函数进行,一个函数只做一个功能,依次调用app.use() 方法,完成大的功能。比如,想在响应之前输出请求日志,直接在真正的响应之前添加一个app.use()方法,它接受的函数处理日志,就可以了

    const express = require("express");
    const app = express();
    
    //  记录请求日志
    app.use((request, response, next) => {
        console.log("Incomes a " + request.method + " to " + request.url);
       next();
    });
    
    // 对请求做出响应
    app.use((request, response) => {
        response.writeHead(200, { "Content-Type": "text/plain" });
        response.end("Hello, world!");
    });
    
    app.listen(8080, () => {
        console.log(`server listen at 8080`)
    });

      记得重启服务器(node server), 然后刷新浏览器,控制台就可以看到日志了。依次调用两个函数对http 请求进行处理,一个记录日志,一个负责响应,这就是Express 的中间件思想,不是把http请求放到一个大的函数中进行处理,而是把请求进行分解,放到每一个函数中进行处理,

    一个函数只做一件事件,Express 则按照函数的书写顺序从上到下依次执行。这些处理函数称为中间件。

      再回头看看记录日志的回调函数,你会发现多了一个next参数,当记录完日志后,调用了next() 方法。next() 表示,我这个中间件已经处理完了,可以到下一个中间件了,如果有下一个中间件,下一个中间件会接着处理,这时到了响应请求的中间件。而 响应请求的回调中并没有next 参数,这是因为服务器已经响应完客户端请求了,无需下一步操作了,终止整个响应流程就可以了,所以也就无需调用next().

      可以看到中间件的主要功能就是拦截http 服务器提供的请求和响应对象,执行逻辑,或者结束响应,或者把它传递给下一个中间件组件,所以中间件都会接受两个参数:请求对象(req),响应对象(res), 还有一个可选的参数next 函数, 调用next 函数可以传递给下一个中间件组件. 

      自己创中间件的时候,只要按照这个模式创建就可以了。不过,业界有一种更通用的方法,就是创建一个函数来返回中间件函数,这样有利于创建可配置 的中间件。日志记录中间件修改如下:

    const express = require("express");
    const app = express();
    
    // 日志记录中间件
    function logger (format) {
        return function(req,res, next) {
            console.log(req[format]);
            next();
        }
    }
    
    // 日志调用
    app.use(logger('url'));
    
    // 对请求做出响应
    app.use((request, response) => {
        response.writeHead(200, { "Content-Type": "text/plain" });
        response.end("Hello, world!");
    });
    
    app.listen(8080, () => {
        console.log(`server listen at 8080`)
    });

      实际上,由于中间件是一个个独立的功能,为此有很多第三方中间件可供使用, 比如日志功能 中间件morgan,npm install morgan -S 安装,然后在文件中替换掉我们自己的函数

    const express = require("express");
    const logger = require("morgan");  // 引入morgan
    
    const app = express();
    app.use(logger("short"))   // 替换掉自己的函数
    
    // 对请求做出响应
    app.use((request, response) => {
        response.writeHead(200, { "Content-Type": "text/plain" });
        response.end("Hello, world!");
    });
    
    app.listen(8080, () => {
        console.log(`server listen at 8080`)
    });

      Express 也内置了唯一的内置中间件static,它提供静态文件服务,例如浏览器请求的图片。在express-tut 文件夹下新建public文件夹,存放静态资源,比如放一张图片(如:flower.png) . static 中间件使用也非常简单,它只接受一个参数,就是我们静态文件放置的目录。

    const express = require("express");
    const logger = require("morgan");  // 引入morgan
    const path = require('path');
    
    const app = express();
    app.use(logger("short"))   // 替换掉自己的函数
    app.use(express.static(path.join(__dirname, 'public'))) // 静态资源服务
    
    // 对请求做出响应
    app.use((request, response) => {
        response.writeHead(200, { "Content-Type": "text/plain" });
        response.end("Hello, world!");
    });
    
    app.listen(8080, () => {
        console.log(`server listen at 8080`)
    });

      重启服务器,在浏览器中输入localhost:8080/flower.png的时候,网页显示一张图片。但当我们输入localhost:8080  时候,Hello, world! 当有静态文件服务的时候,如果我们请求的静态文件正好和服务器上的资源相匹配,它就会返回静态资源,程序也不会继续执行。 如果没有相匹配的静态资源,程序就会继续执行.

      在使用中间件的时候,一定要注意顺序,比如把logger 中间件放到响应中间件的后面,它就不会输出logger了。还有一种高级的用法,那就是中间件的挂载。就是在使用中间件的时候,在它前面添加一个路径,只有在请求的url 中带有此路径,才会调用这个中间件,那么app.use 方法就需要接受两个参数,第一个参数就是路径,第二个参数就是中间件,如 app.use("/admin", admin) ,添加一个admin 中间件, 随便在server.js中添加一下

    app.use('/admin',(req, res) => {
        switch (req.url){
            case '/':
                res.end("try user")
            break;
            case '/user' :
               res.end("hello user");
            break;
        }
    }); 

      当输入localhost:8080/admin时,可以看到try user ,表明它执行了admin中间件,且是第一个case, 再输入localhost:8080/admin/user,可以看到 hello user. 它还是执行了admin 中间件,第二个case.  挂载,就是给这个中间件定义一个路径,只有在访问该路径的时候,它才执行, 最常用的地方在于路由

      Express路由

      路由就是不同的url 响应不同内容,例如用户点击about , 我们就要返回 about 页面,用户点击home,我们就要返回首页。最简单的实现方式是app.method(path, function) . method就是指的get, post 请求,path 指的就是/about, 后面的函数就是对这个请求的处理。如用户点击about, 服务器该怎么返回呢? 它是一个get 请求,请求的是about, 我们要返回about, 就可以这么写,app.get('/about', function(req,res){res.end("about")}). 再写几个路由加深一下理解, 整个server.js  修改如下

    let express = require('express')
    let app = express(); 
    
    // 首页路由
    app.get("/", function(request, response) {
        response.end("Welcome to my homepage!");
    });
    // about 路由
    app.get("/about", function(request, response) {
        response.end("Welcome to the about page!");
    });
    // weather 路由
    app.get("/weather", function(request, response) {
        response.end("The current weather is NICE.");
    });
    // 404 页面
    app.use(function(request, response) {
        response.statusCode = 404;
        response.end("404!");
    });
    app.listen(8080)

      我们在浏览器中输入localhost:8080/about, 就看到 Welcome to the about page。

      Express模版引擎

      Express增加了对许多模板引擎的支持,如pug(jade), ejs等,可以动态输出html. 使用模版引擎有三个步骤,安装,注册,配置, 用ejs 演示一下

      安装很简单 npm install ejs -S; 注册是对express 不支的模版如handlebars 而言的,由于Express 原生支持ejs, 所以不需要注册;配置,就是告诉Express要使用哪个模版引擎,模版文件放在什么地方,以便Express在渲染的时候知道从哪里去寻找模版。

      在express-tut(根目录) 中新建views文件夹,用于放置模版文件, 在views文件夹中新建一个模版文件,如index.ejs

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <title>Hello, world!</title>
    </head>
    <body>
      <%= message %>
    </body>
    </html>

      在server.js中配置模版引擎

    app.set("view engine",'ejs');  // 设置 view engine, 使用ejs模版引擎
    app.set("views", path.resolve(__dirname,'views'))  // 设置views,模版放置的地方 

      动态渲染模版,输出html。 要调用res.render()方法,render 方法,接受一个参数就是我们要渲染的模版的名称,还有一个可选的参数,就是向模版中输入的数据 来替换模版中的变量。调用render方法的时候,express 就向views 文件夹中寻找对应的模版,这也是我们 app.set("views",...) 的原因。

    app.get('/', function(req,res){
        res.render('index',{
        message:"this is a ejs view"    
      }) })

      完成的server.js 如下,把其他的日志,static 都删除了,在浏览器中输入localhost:8080, 就看到 this is a ejs view。

    const express = require("express");
    const path = require('path');
    
    const app = express();
    
    app.set("view engine",'ejs');  // 设置 view engine, 使用ejs模版引擎
    app.set("views", path.resolve(__dirname,'views'))  // 设置views,模版放置的地方 
    
    // 对请求做出响应, 渲染模板
    app.get('/', function(req,res){
        res.render('index', {
        message:"this is a ejs view"    
      })
    })
    
    app.listen(8080, () => {
        console.log(`server listen at 8080`)
    });

      Express 的基本知识就差不多了,通过一个留言本实例来加深Express 的认识 ,再多加一个中间件 body-parser,用来解析post请求 npm install  body-parser --save,

      server.js 文件如下:

    // 引入各种模块依赖
    const express = require('express');
    const logger = require('morgan');
    const bodyParser = require('body-parser');
    const path = require('path');
    
    // 利用express 创建应用
    const app = express();
    
    // 设置模版引擎为ejs
    app.set('views', path.join(__dirname, 'views'));
    app.set('view engine','ejs');
    
    // 创建一个数组对象,保存用户通过表单上传的内容
    var entries = [];
    
    // app.locals,它是一个对象,提供整个app应用所需要的数据,这时属性entries就可以用在模版中
    app.locals.entries = entries;
    
    // 使用morgan 中间件记录请求日志
    app.use(logger('dev'))
    
    // 利用body-parser 中间件获取用户上传的数据,通过这个中间件,用户上传的数据都会附在req.body的属性上。
    app.use(bodyParser.urlencoded({extended: false}))
    
    //路由的设置
    app.get('/', (req,res) => {
        res.render('index');
    })
    app.get('/new-entry', (req,res) => {
        res.render('new-entry');
    })
    
    app.post('/new-entry', (req,res) => {
        if(!req.body.title || !req.body.body) {
            res.status(400).send('Entris have a title and body')
            return;
        }
        entries.push({
            title:req.body.title,
            content: req.body.body,
            published: new Date()
        })
        res.redirect('/')
    })
    
    // 当用户访问页面,我们的路由都不匹配时,提供回退404页面
    app.use((req,res) => {
        res.status(404).render('404')
    })
    
    // 监听3000端口,启动服务
    app.listen(3000, () => {
        console.log('server started on port 3000')
    })

      这时需要提供几个页面,index.ejs, new-entries.ejs, 404.ejs, 由于几个页面都有共同的footer和header, 所以我们可以把header 和footer 单独提取出来,形成header.ejs 和footer.ejs,其它页面直接引进就可以了。在views 文件夹中新建这几个页面

      header.ejs

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title>留言板</title>
            <!--引入bootstrap 提供简单的样式-->
            <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
        </head>
        <body class="container">
            <h1>
                Express 留言板
                <a href="/new-entry" class="btn btn-primary pull-right">
                写一个留言
                </a>
            </h1>

      footer.ejs

    </body>
    </html>

      index.ejs 页面主要显示我们留言的内容,而我们所有的留言都存在的entries数组中,所以要用ejs中的循环对entries 进行遍历,对内容进行输出。由于第一次进入页面,我们并没有任何留言,所以要对entries 进行判断,如果没有内容,要让用户添加留言

    <!--引入header-->
    <% include header %>
    
    <!--对entries进行判断-->
    <% if (entries.length) { %>
        <!--有内容,进行循环遍历,显示内容-->
        <% entries.forEach(function(entry) { %>
            <div class="panel panel-default">
                <div class="panel-heading">
                    <div class="text-muted pull-right">
                        <%= entry.published %>
                    </div>
                    <%= entry.title %>
                </div>
                <div class="panel-body">
                    <%= entry.content %>
                </div>
            </div>
        <% }) %>
        <!--没有内容,提示用户添加留言-->
    <% } else { %>
        没有留言内容! <a href="/new-entry">添加一个留言</a>
    <% } %>
    
    <!--引入footer-->
    <% include footer %>

      所以当第一次进入页面时显示以下内容

      当用户点击添加一个留言, 我们要跳转到添加留言的页面,就是/new-entry页面,这时添加new-entry页面,它就是一个form 表单

    <% include header %>
    
    <h2>书写一个留言</h2>
    <form method="post" role="form">
        <div class="form-group">
            <label for="title">标题</label>
            <input type="text" class="form-control" id="title"
            name="title" placeholder="标题" required>
        </div>
        <div class="form-group">
            <label for="content">内容</label>
            <textarea class="form-control" id="body" name="body"
            placeholder="内容" rows="3" required></textarea>
        </div>
        <div class="form-group">
            <input type="submit" value="提交留交" class="btn btn-primary">
        </div>
    </form>
    
    <% include footer %>

      当提交订单的时候,我们并没有给form 表单添加action, 表单内容提交到什么地方? 原来表单中没有指定action时,它会提交到当前页面/new-entry, 正好对应server.js中的app.post(‘/new-entry’), 所以当提交订单时,服务端会收到我们传过去的内容。

      页面内容展示如下:

      最后提供一个404.ejs页面

    <% include header %>
    <h2>404! Page not found.</h2>
    <% include footer %>

      好了,在命令行中输入node server.js 开启服务器,然后在浏览器中输入localhost:8080体验一下。

      如果我们想使用handlebars 作为模板呢?也没有问题,不过 需要安装第三方模块express-handlebars 进行支持,并且还要注册模板引擎。npm install express express-handlebars -S, 安装完成,在server.js 中引入express-handlebars. 

    const exphbs = require("express-handlebars")

      调用app.engine() 进行注册,内容如下

    app.engine("handlebars", exphbs({
        defaultLayout: "main",
    layoutDir: app.get('views') + '/layouts',
        partialsDir:[app.get('views') + '/partials']
    }))

      app.engine 接受两个参数,一个是名字,这很好理解,注册肯定要给它起个名字,以便后面使用。第二个是handlebars 的配置,exphbs 是一个函数,接受对象作为参数进行配置。default Layout:默认布局文件名称, layoutDir, 布局文件所在的目录,partialsDir局部文件所在的目录,为什么这么定义呢?需要了解一下handlebars 中的基本概念 : 布局,局部, 还有视图。

      视图(views): 就是我们定义的任何的模板片段

    <div class="panel panel-primary">
      <div class="panel-heading">
        <h3 class="panel-title">欢迎使用handlebars</h3>
      </div>
    </div>

      布局:也是一种模版,不过作用比较特殊,所以单独列出来。想一下我们的网站,主要分为header ,main, footer 三个部分,通常header ,footer 部分是不变的,只有main 是经常改变的, 程序在运行的过程中,只要动态的替换掉main 就可以了。这时我们就可以定义一个文件,包含不变的header, footer 和 可变的main,这个文件就是布局,所以在注册handlebars的时候,我们设置 defaultLayout 为main,layoutDir文件夹

      局部文件(partial):有些时候,有几个页面要共用相同的部分,如侧边栏这,我们通常都会封装为组件,然而在handlebars 中,它们称之为局部文件partial,可以知道局部文件也是代码片段。我们在注册模版引擎的时候,设置 partialsDir

       好了,注册成功了,就要告诉express来使用 handlerbars, 调用app.set 方法,第二个参数是我们注册的模板的名字,就是app.engine中第一个参数,他们要保证一致

    app.set(“view engine”, 'handlebars' )

      最后还要告诉express 我们的模版文件放在什么地方,以便express 去查找使用,还是要调用app.set() 方法

    app.set("views", __dirname+ "/views"); // 第一个参数views是复数,一定不忘记后面的还有一个s, 要不然会报 View is not a construction 

      现在可以使用handlebars 了,不过根据handlebars  在注册时的配置,还需要views 文件夹下面再建两个文件夹 layouts 和partials, layouts 下面建立main.handlebars 作为布局 文件。注意,文件要以handlebars为后缀名,因为我们注册的模板就是handlebars。简单写一个布局文件

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Express Handlebars 使用</title>
        <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" >
    </head>
    <body>
        <div class="container">
            <div class="row">
                <div class="col-sm-8">
                    {{{ body }}}   
                </div>
                <div class="col-sm-4">
                    {{> partial }}
                </div>
            </div>
        </div>
    </body>

      他就是定义整个网页的骨架,可以看到里面有两个特殊的地方{{{body}}}, 我们的view 就会渲染在{{{body}}}占位符所在的地方。{{> partial }} 局部文件的名称,  把局部文件partial.handlebar 放到这个地方。在views 文件夹中定义一个index.handlebars, 还是上面的视图吧

    <div class="panel panel-primary">
      <div class="panel-heading">
        <h3 class="panel-title">欢迎使用handlebars</h3>
      </div>
    </div>

      在partials 文件下,建一个partial.handlebar 文件

    <div class="panel panel-default">
      <div class="panel-heading">
        <h3 class="panel-title">侧边栏</h3>
      </div>
    </div>

      定义的视图,布局,局部文件,我们怎么使用呢?这要用到render方法,去渲染模版。render 方法是定义在res 响应对象上的,所以我们浏览器端发起一个请求,让它输出动态模版,所以我们在  server.js 中定义一个路由,整个server.js如下

    var express = require('express'),
        app = express();
    
    // 引入express-handlebars 
    var exphbs = require("express-handlebars")
    
    // 设置模板存放路径
    app.set("views", __dirname+ "/views")
    
    // 注册handlebars模板引擎
    var hbs = exphbs.create({ 
        defaultLayout: "main",
        layoutDir: app.get('views') + '/layouts',
        partialsDir:[app.get('views') + '/partials']
    });
    app.engine('handlebars', hbs.engine);
    
    // 告诉express使用handlebars模板,
    // 第二个参数是我们注册的模板的名字,就是app.engine中第一个参数,他们要保证一致
    app.set('view engine', 'handlebars' ) 
    
    // render 渲染模板
    app.get('/', function (req,res) {
        
        res.render('index')
    })
    app.listen(8080);

      render方法,接受一个参数,那就是要渲染的模版(视图)的名字,当exrpess 执行render 的时候,它会从views 文件夹下找到我们指定的文件,然后再找到布局文件,替换掉里面的{{{body}}},如果有partial, 它还会从particals 文件夹下找到局部文件,进行合并,形在一个完整的html文件进行输出。这也就是我们上面一系列设置文件夹的原因。

      我们每次渲染一个视图文件时,都会结合layout 布局模版渲染, 有时我们并不需要layout布局模版,这时可以在render 方法中进行设置 layout: false

    app.use(function (req,res) {
        res.render('404', {
          layout: false
        });
    })

      在设置模版引擎的时候,我们只指定了一个默认的布局视图,如果我们带想使用其他视图,怎么办? 调用render 方法的时候,指定layout,当然要确保这个layout文件在layout文件夹中。

    app.get('/foo', function(req, res){ 
        res.render('foo', { layout: 'microsite' }); 
    })

      那么我们在渲染的时候要给time传递参数, render 方法可以接受第二个可选参数,它是一个对象, 就是我们向模版中传递的数据,对象的属性就是我们在模版中定义的表达式,如time

    res.render('index' , {
        time: Date.now()
    })

      这时我们发现,页面中显示的日期,但它是日期毫秒数,我们想把他转化成年月日的形式,这就要执行一定的逻辑操作,但是handlebars不支持在模版中使用逻辑的,这时要用到 helper 助手。它其实是一个函数,对模版中的表达式执行逻辑操作, helper 在模版中使用之前要先注册。这时有两种方法,

      一种是 在注册handlebars 模版引擎的时候,直接给它配置helpers

    var hbs = exphbs({ 
        defaultLayout: "main",
        layoutDir: app.get('views') + '/layouts',
        partialsDir:[app.get('views') + '/partials'],
        // 增加helpers
        helpers: {
            timeFormate: function (time) {
                var dateTime = new Date(time)
                return dateTime.getFullYear() +"年"+ (dateTime.getMonth()+1)+ "月" + 
                dateTime.getDay() +'日'
            }
        }
    });

      一种是在调用render 方法的时候给它配置helpers

    app.get('/', function (req,res) {
        
        res.render('index' , {
            time: Date.now(),
            //配置helpers
            helpers : {
                timeFormate: function (time) {
                    var dateTime = new Date(time)
                    return dateTime.getFullYear() +"年"+ (dateTime.getMonth()+1)+ "月" + 
                    dateTime.getDay() +'日'
                }
            }
        })
    })

      上面的第一种配置方式,可以叫做全局配置,因为它在所有的模版文件中都可以使用,下面的一种则只能在 index模版中使用,可以叫做局部配置。

    <div class="panel panel-primary">
        <div class="panel-heading">
            <h3 class="panel-title">
            <!-- 增加 timeFormate helper -->
                欢迎使用handlebars模板 <small>{{timeFormate time}}<small>
            </h3>
        </div>
    </div>

      express-handlebars还支持子目录, 所以如果你有大量的局部文件,可以将它 们组织在一起。例如,你有一些社交媒体局部文件,可以将它们放在views/ partials/social 目录下面, 然后使用{{> social/facebook}}、{{> social/twitter}} 等来引入它们。

      

  • 相关阅读:
    December 23rd 2016 Week 52nd Friday
    December 22nd 2016 Week 52nd Thursday
    December 21st 2016 Week 52nd Wednesday
    December 20th 2016 Week 52nd Tuesday
    December 19th 2016 Week 52nd Sunday
    December 18th 2016 Week 52nd Sunday
    uva294(唯一分解定理)
    uva11624Fire!(bfs)
    fzu2150Fire Game(双起点bfs)
    poj3276Face The Right Way
  • 原文地址:https://www.cnblogs.com/SamWeb/p/6711351.html
Copyright © 2011-2022 走看看