利用express搭建网站

作者: Obeing | 来源:发表于2016-10-10 22:39 被阅读864次

    先建个简单的服务器

    • 当然你先得安装express npm install express
    //使用express,如果这里的代码复制后运行不了请移步我的github下载源码,顺手star给我个小星星鼓励哈
    //http://github.com/sally2015/express-project
    // npm install  运行node main 后访问loaclhost:3000
    var express = require('express');
    var app = express();
    app.set('port', process.env.PORT || 3000);
    
    app.get('/',function(req, res){
        res.send('home');
        
    });
    app.use('/about',function(req, res){
        res.send('about');
        
    });
    app.use(function(req, res){
    
    
        res.send('404');
        
    });
    
    app.use(function(req, res, next){
    
        res.send('500');
    });
    
    app.listen(app.get('port'), function () {
        console.log('Express started on http:localhost'+app.get('port'));
    });
    
    
    
    • app.use(function(req,res,next){})默认匹配的路由是‘/’,多个use要使用next()方法,但是使用了,res.end()或者res.send()就不能使用next到达下一个use了
    • app.get()是添加路由的方法,忽略大小写,反斜杠,进行匹配时不考虑查询字符串
    //不使用express你可能要这么写
    /*
    * var http = require('http');
    * var server =  http.createServer(function(req, res){
    *   if(req.url === '/'){
            res.setHeader('Content-type','text-plain');
            res.write('……');&&res.end();
    *   }
    *}).listen(3000,'localhost');
    */
    
    
    • 对定制的404页面和500页面的处理与对普通页面的处理有所区别,用的不是app.get,而是app.use。app.use是express添加中间件的一种方法
    • express中路由和中间件的添加顺序至关重要,如果把404处理器放在所有的路由上面,普通页面的路由就不能用了
    • express能根据回调函数中的参数区分404和500处理器

    使用handlebars

    • (defaultLayout:'main')意味着除非你特别指明否则所有的视图都是这个布局
    var handlebars = require('express3-handlebars') //现在已经重命名为express-handlebar了,由于牵一发可能要动全身,我这里就不改了
    .create({ 
        defaultLayout: 'main', // 设置默认布局为
    });
    
    app.engine('handlebars', handlebars.engine); // 将express模板引擎配置成handlebars 
    app.set('view engine', 'handlebars');
    
    
    • 创建一个views/layouts/main.handlebars文件

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Document</title>
    </head>
    <body>
    {{{body}}}
    </body>
    <html>

    ```
    
    • {{{body}}}注意这里是三个大括号,这个表达式会被每个视图自己的html取代
    • 分别创建首页、关于、404、500页面,后缀名都是handlebars

    views/home.handlebars
    => <h1>welcome to home</h1>
    views/about.handlebars
    =><h1>welcome to about</h1>
    views/404.handlebars
    =><h1>not found - 404</h1>
    views/500.handlebars
    =><h1>500 server error</h1>

    ```
    

    视图和静态文件

    • express靠中间件处理静态文件和视图,中间件是一种模块化手段,使请求处理更加容易
    • static中间件可以将一个或多个目录指派为包含静态资源的目录,其中的资源不经过特殊处理直接发送到客户端
    app.use(express.static(__dirname+'/public'));
    //现在所有文件都可以相对public直接进行访问,例如public下面有一个img文
    //件夹,那么在handlebars中(不需要理会在handlebars的目录结构)直接访问
    //路径/img/logo.png
    
    • 视图和静态资源的区别是它不一定是静态的,html可以动态构建
    • 在项目下建一个public的子目录,应该把static中间件加载所有路由之前

    向handlebars里传递参数

    var args = 'its a arguments';//虚拟一个参数
    //修改此时的about路由
    app.get('/about', function(req,res){
        
        res.render('about', {args:args});
    });
    
    • 修改about文件
    Paste_Image.png
    • 此时访问就会得到下面的结果
    Paste_Image.png

    以上代码在https://github.com/sally2015/express-project ch1
    ---------------------------------------------分割线----------------------------------------------

    ch2讲讲怎么快速、可维护的开发

    使用自定义模块

    • ch1为了传递参数在main.js里面定义了一个虚拟数据,为了将数据分离出来,在根目录下定义一个lib目录,放置一个数据模块m_data.js
    var args = 'its a arguments';//虚拟一个参数
    
    exports.getData = function(){
        return args;
    }
    
    • main.js
    var m_data = require('./lib/m_data.js');
    app.get('/about', function(req,res){
        
        res.render('about', {args:m_data.getData()});
    });
    

    使用nodemon自动重启服务器

    • 每次修改main文件都要ctrl+c停止再运行很累,使用nodeman每次修改都会帮我们重启服务器
    • 使用也非常简单,npm install nodemon -g,运行nodemon main

    页面测试

    • 需要一个测试框架Mocha ---- npm install mocha --save-dev 这里dev的意思是只在开发时依赖

    • mocha是要运行在客户端的所以把mocha资源放在public目录下

      • public/vendor
      • => node_modules/mocha/mocha.js
      • => node_modules/mocha/mocha.css
    • 测试通常需要一个assert函数

      • npm install chai --save-dev
      • node_modules/chai/chai.js => public/vendor
    • 不让测试一直运行

      • 因为拖慢网站的速度,用户也不需要看到测试结果
      • 期望的情况是在url后面加上?test=1才加载测试页面
    • 定义中间件来检测查询字符串中的test=1,放在所有路由之前

    • 如果test=1出现在任何页面的字符串查询中,属性res.locals.showTests就会被设为true

    • res.locals对象是要传给视图上下文的一部分

    app.use(function(req, res, next){
        res.locals.showTests = app.get('env') !== 'production' 
              && req.query.test === '1';
        next();
    });
    

    引入测试框架

    • 修改main.handlebars(以后简写main),修改head
    <head>
        <title>Meadowlark Travel</title>
        {{#if showTests}}
            <link rel="stylesheet" href="/vendor/mocha.css">
        {{/if}}
        <script src='//code.jquery.com/jquery-2.0.2.min.js'></script>
    </head>
    
    
    • 这里在head引入jquery是为了方便测试
    • 在</body>之前引入mocha和chai,还需引入一个qa/global-test.js脚本
    {{#if showTests}}
            <div id="mocha"></div>
            <script src='/vendor/mocha.js'></script>
            <script src='/vendor/chai.js'></script>
            <script>
                mocha.ui('tdd');
                var assert = chai.assert;
            </script>
            <script src='/qa/tests-global.js'></script>
            {{#if pageTestScript}}
                <script src='{{pageTestScript}}'></script>
            {{/if}}
              <script>mocha.run()</script>
        {{/if}}
    
    • 创建public/qa/tests-global.js全局测试脚本
    suite('Global Tests', function(){
        test('page has a valid title', function(){
            assert(document.title && document.title.match(/\S/) && 
              document.title.toUpperCase() !== 'TODO');
        });
    });
    
    
    • 访问localhost:3000没有任何变化,但是访问localhost:3000?test=1,你会发现加载了测试的文件帮你做的这些东西
    Paste_Image.png
    • 针对about页面进行测试
      • 这里假设测试确保有总有一个指向联系我们页面的链接,创建一个public/qa/tests-about.js

      suite('"About" Page Tests', function(){
      test('page should contain link to contact page', function(){
      assert($('a[href="/contact"]').length);
      });
      });
      - 在main.js上改变路由/about的参数
      ```js
    app.get('/about', function(req,res){
        
        res.render('about', {
            args:m_data.getData(),
            pageTestScript:'/qa/tests-about.js'
        });
    });
    
    • 现在刷新页面about会有一个错误的断言
    -
    • 只要about模板中有一个链接,这个错误测试断言就会消失
    • 例如<a href="/contact">contact us</a>
    测试通过

    使用局部组件

    • 场景定义一个天气组件,在任何页面都可以调用,这样的需要重复调用的可以用局部文件实现
      • 新建一个views/partials/weather.handlebard
    <div class="weatherWeight">
        {{#each weather.locations}}
            <div class="location">
                <h3>{{name}}</h3>
                <a href="{{forecastUrl}}">
                    {{weather}},{{temp}}
                </a>
            </div>
        {{/each}}
    </div>
    
    
    • 在weatherData.js中放入虚拟数据
    function getWeatherData(){
    
        return {
            locations:[
                {
                    name:'广州',
                    forecastUrl:'https://github.com/sally2015',
                    weather:'广州的温度情况',
                    temp:'温度'
                },
                {
                    name:'深圳',
                    forecastUrl:'https://github.com/sally2015',
                    weather:'深圳的温度情况',
                    temp:'温度'
                },
                {
                    name:'珠海',
                    forecastUrl:'https://github.com/sally2015',
                    weather:'珠海的温度情况',
                    temp:'温度'
                }
            ]
        }
    }
    exports.getWeatherData = getWeatherData
    
    
    • 创建一个中间件给res.locals.weather添加数据
    
    //给res.locals.weather添加数据
    app.use(function(req, res, next){
        if(!res.locals.weather){
            res.locals.weather = {};
    
        }
        res.locals.weather = m_weatherData.getWeatherData();
        next();
    });
    
    
    
    • 将组件放在主页home上
    <h1>welcome to home</h1>
    {{>weather}}
    
    
    • 语法{> partialname}可以让你在视图中包含一个局部文件,express-handlebars会在views/partials寻找
    • 你可以将这个语法放在任何你需要的页面上
    现在的home效果

    客户端使用模板和动态获取数据

    • 客户端使用handlebars需要加载handlebars文件,你可以从node_moudles里面找,像正常文件一样引入即可
    • 定义一个view/partials/ajaxtest.handlebars文件
    <script id='ajaxtest' type='text/x-handlebars-template'>
        Marry hadd a little <b>\{{animal}}</b>
        its<b>\{{bodyPart}}</b>
        was <b>\{{adjective}}</b>as <b>\{{noun}}</b>
    </script>
    <button id='btn'>动态获取数据<tton>
    <div id="content">
        
    </div>
    <script>
        $(document).ready(function(){
            var template = Handlebars.compile($('#ajaxtest').html());
            
            $('#btn').click(function(){
                $.ajax('/data/ajaxtest',{
                    success:function(data){
                        $('#content').html(template(data));
                    }
                });
            });
        });
        
    </script>
    
    
    • 在main.js中设定接口
    app.get('/data/ajaxtest', function(req, res) {
        res.json({
                animal:'dog',
                bodyPart:'tail',
                adjective : 'sharp',
                noun : 'run'
            });
    });
    
    
    • 在你想要的视图里面加入{{>ajaxtest}}
    • 这时候当你点击按钮就会请求到数据,注意接口使用的方法是json
    返回数据
    --------------------分割线------------------------------------ch2 https://github.com/sally2015/express-project

    form表单处理

    • 如果使用post需要引入中间件来解析URL编码体
    • app.use(require('body-parser')());
    • 新建一个简单的表单页 /views/newsletter.handlebars
      <h2>sign up</h2>
    <form action="/process?form=newsletter" method="POST" class="form-horzontal">
        <input type="hidden" name="_csrf" value="{{csrf}}">
        <div class="form-group">
            <label for="fieldName" class="col-sm-2 control-label">name</label>
            <div class="col-sm-4">
                <input type="text" class="form-control" id="fieldName" name="name">
            </div>
        </div>
        <div class="form-group">
            <label for="fieldEmail" class="col-sm-2 control-label">email</label>
            <div class="col-sm-4">
                <input type="text" class="form-control" id="fieldEmail" name="email" require>
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm=offset-2 col-sm-4">
                <button type="submit" class="btn btn-default">register<button>
            </div>
        </div>
    </form>
    
    
    • post的地址是/process所以在main.js里应该这么定义
    • 这里使用303重定向,http1.1添加的响应码,相对于302而言303是一种更合适的代码,因为http规则明确表明浏览器303重定向后,无论之前是什么方法,都应该用get请求,这是用于响应表单提交请求的推荐方法
    //表单页
    app.get('/newsletter', function(req, res) {
        res.render('newsletter', {csrf:'csrf data'});
    });
    //表单数据请求
    app.post('/process',function(req, res){
        console.log('Form (from querystring):'+req.query.form);
        console.log('name csrf'+req.body._csrf);
        console.log('name value' + req.body.name);
        console.log('email value' + req.body.email);
        res.redirect(303,'/thankyou');
    });
    //表单跳转页
    app.get('/thankyou', function(req, res) {
        res.send('thank you');
    });
    
    
    表单页 输出参数

    ajax表单处理

    • 在newsletter中加上这一段代码
    <div id="content">
        <button id='btn'>动态获取数据<tton>s
    </div>
    <script>
        $(document).ready(function(){
            var template = Handlebars.compile($('#ajaxtest').html());
            
            $('#btn').click(function(){
                $.ajax('/data/ajaxtest',{
                    success:function(data){
                        $('#content').html(template(data));
                    }
                });
            });
        });
        
    </script>
    
    
    • express提供了两个属性req.xhr和req.accepts
    • req.accepts('json,html')询问最佳返回格式是json还是html
    //ajax 表单请求
    app.post('/process',function(req, res){
        if(req.xhr || req.accepts('json,html') === 'json'){
            res.send({success:true});
        }else{
            res.redirect(303,'/thankyou');
        }
    });
    
    

    文件上传

    • 文件上传可以使用Connect的内置中间件multipart来处理,但是这个中间件已经从COnnect中移除了,一旦Express更新了对Connect的依赖项,它也将从Express中消失,所以不要使用这个中间件
    • 对于符合表单处理可以使用Formidable
    • 创建一个文件上传表单views/photoUpload.handlebars
    • 必须指定enctype="multipart/form-data"来启用文件上传,也可以通过属性限制上传文件类型
    <h2>sign up</h2>
    <form id="newsletter" action="/photoUpload/{year}/{month}" enctype="multipart/form-data" method="POST" class="form-horzontal">
        <input type="hidden" name="_csrf" value="{{csrf}}">
        <div class="form-group">
            <label for="fieldName" class="col-sm-2 control-label">name</label>
            <div class="col-sm-4">
                <input type="text" class="form-control" id="fieldName" name="name">
            </div>
        </div>
        <div class="form-group">
            <label for="fieldEmail" class="col-sm-2 control-label">email</label>
            <div class="col-sm-4">
                <input type="text" class="form-control" id="fieldEmail" name="email" require>
            </div>
        </div>
        <div class="form-group">
            <label for="fieldPhoto" class="col-sm-2 control-label">email</label>
            <div class="col-sm-4">
                <input type="file" class="form-control" id="fieldPhoto" accept="image/*" name="photo" require>
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm=offset-2 col-sm-4">
                <button type="submit" class="btn btn-default">register<button>
            </div>
        </div>
    </form>
    
    
    
    • 安装Formidable并处理路由
    var formidable = require('formidable');
    //文件上传页面
    app.get('/photoUpload', function(req, res) {
        var now = new Date();
        res.render('photoUpload',{
            year: now.getFullYear(),
            month: now.getMonth()
        });
    });
    //文件上传请求
    app.post('/photoUpload/:year/:month',function(req, res){
        var form = new formidable.IncomingForm();
        form.parse(req,function(err,fileds,files){
            if(err) return res.redirect(303,'/error');
            console.log(fileds);
            console.log(files);
            res.redirect(303,'/thankyou');
        })
    });
    
    
    • year 和month 是路由参数,运行后的文件对象结果如下,可以利用数据库存储
    file对象

    关于cookie

    • 引入中间件cookie-parser

    • app.use(require('cookie-parser')());

    • 使用:res.cookie('test','iddddddi');在需要相应的地方

    • 加密cookie

    • 加密cookie只能通过res.signedCookies而不是res.cookies访问

    • 定义一个文件creddential.js

    app.use(require('cookie-parser')(credential.cookieSercret));
    //在需要相应的地方
    res.cookie('test','iddi',{signed :true});
    

    加密(signed)cookie -- 服务端对cookie加密
    Cookie:test=s%3Aiddi.zP%2FeYHodVessW4MGVqlrkPNslTobphomZproupf%2BGVI
    不加密
    Cookie:test=iddi

    相关文章

      网友评论

        本文标题:利用express搭建网站

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