node.js的一个小例子

2018-06-24 00:28:11来源:未知 阅读 ()

新老客户大回馈,云服务器低至5折

安装配置的环节这里就不说了。

这是我学习nodejs以来做的第一个小例子,很简单,就是在第一个页面里输入自己的名字,在第二个页面(有图片)中显示。思路和很多地方都参考了http://www.cnblogs.com/giggle/p/6287931.html 这篇文章,感谢作者。

这篇文章中说得不对的地方希望大家指正。

客户端的步骤是进入127.0.0.1:8000/login进入login.html,填写输入框后进入mian.html。

服务器端的步骤是:

1.通过浏览器传来的url获取路由,判断进入login.html,

2.用户提交表单,在表单action方法中进入main.js,此时使用post方法,携带参数,并根据html中的{name}占位,利用node动态替换。

3.在遇到图片src时,处理图片请求。

 

先贴上两个html文件:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="author" content="Hx2">
        <meta name="format-detection" content="telephone=no">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="black">
        <meta name="Description" content="网页描述"/>
        <meta name="Keywords" content="关键字"/>
        <title></title>
        <style>
            form {
                text-align:center;
            }
        </style>
    </head>
    <body>
        <form action="./deliver" method="post">
            账户:<input type="text" name="name"/><br/>
            密码:<input type="password" name="password"/><br/>
            <input type="submit" value="提交"/>
        </form> 
    </body>
</html>
login.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="author" content="Hx2">
        <meta name="format-detection" content="telephone=no">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="black">
        <meta name="Description" content="网页描述"/>
        <meta name="Keywords" content="关键字"/>
        <title></title>
        <style>
            body {
                text-align:center;
            }
            span {
                color: brown;
            }
            img{
                width: 100%;
            }
        </style>
    </head>
    <body>
        <div>
            hello!<span>{name}</span>
        </div>
        <img src="./showImg" />
    </body>
</html>
main.js

 

首先是主文件,创建一个服务:

1 var http = require('http');
2 http.createServer(function(request,response)        {        
3         response.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});        
4         if(request.url!=="/favicon.ico"){
5             
6               response.end('');    
7         }
8 }).listen(8000);        
9 console.log('Server running at http://127.0.0.1:8000/');
View Code

这段中引入node的自带对象http。

response.writeHead是协议头。

然后用if清除对favicon.ico的访问,否则刷新一次就会有两次访问,就避免了未知的错误。

response.end结束请求。

listen是监听某个端口。

 

当一个请求来到服务器的时候,他会获取到url的路径来判断接下来的操作。所以需要添加url模块,利用这个模块来获得url中的相关路径并处理。就是这句:var url = require('url');

连接到router.js文件后,此时主文件中获取路由,通过正则处理掉多余的 “ / ” 以正确匹配router.js里面的方法。同时,用try catch来处理这里可能发生的异常。

完整的main.js文件如下:

var http = require('http');
var url = require('url');
var router = require('./router.js');

http.createServer(function(request,response)        {        
        if(request.url!=="/favicon.ico"){
            pathname = url.parse(request.url).pathname;
            pathname = pathname.replace(/\//,'');
            try{//如果这一部分有错误,执行下面catch
                router[pathname](request,response)
            }catch(err){
                console.info('router错误' + err);
                response.writeHead(200,{'Content-Type':'text/html;charset=utf-8'}); //协议头
                response.write(err.toString());
                response.end(''); 
            }
        }
}).listen(8000);        
console.log('Server running at http://127.0.0.1:8000/');

 

根据需求,我们需要处理的是进入login页面的login、传递名字的deliver、显示图片的showImg。所以在router.js文件中有了个基本的骨架:

module.exports = {
    login : function(req,res){
        
    },
    deliver : function(req,res){
        
    },
    showImg : function(req,res){
        
    }
}
View Code

 

在router.js里面有三个操作,他们有个共同点,就是都会读取服务器的本地文件将它写在客户端。这样,把他的操作都提取出来放在一个文件中,有利于管理和调用,这里建个optfile.js存放操作。暂定optfile.js里面有三个方法,分别是同步读取:readfileSync、异步读取:readfile、读取图片:readImg。具体操作等会说。

在router.js里的login中,如果是同步的话可以这样写,他会一步一步向下执行:

login : function(req,res){
        res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});
        optfile.readfileSync('./view/login.html',res);
        res.write(data);
        res.end('');
},

但是node.js的优势是异步,我们需要做的是异步的。

异步读取文件的方法(在下面会说)需要一个闭包来存储此时的request和response,来保证readFile在异步执行的时候,这里的request和response不会在垃圾回收机制下被自动清除。并且在闭包中加入end()就不存在当异步还没有执行完,主程序先执行完了报错这种情况。

在后面的readFile方法里只需要调用这个闭包就可以了。

这个闭包就是下面的recall(),我们在闭包中对读取的文件进行相关的操作。

login : function(req,res){    
        response.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});
        function recall(data){
                res.write(data);
                res.end('');
        };
        optfile.readfile('./view/login.html',recall)
}      

在操作比较多的项目中,每一个相同操作(这里是显示到客户端)都要写一个recall函数非常麻烦,所以封装成getRecall方法:

function getRecall(req,res){
    res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});
    function recall(data){
        res.write(data);
        res.end('');
    }
    return recall;
}

这样,login就变得很简洁:

login : function(req,res){
    recall = getRecall(req,res);//这个recall并不是getRecall,而是getRecall返回的闭包recall
    optfile.readfile('./view/login.html',recall)
},

在router.js里的deliver中,需要使用post或get方法传参,这里使用post方法:

var post = '';
req.on('data',function(chunk){
    post += chunk;
})
req.on('end',function(){
    post = querystring.parse(post);
    console.log('收到参数:' + post['name值'] + '\n');
}

这时的思路是把收到的参数显示在main.html中,在main.js中接收参数(如果是多个就使用数组),而deliver方法就是带着参数跳转到main.js中。所以想到接下来的步骤应该是:

recall = getRecall(req,res);

optfile.readfile('./view/main.html',recall);

但是上面的post方法相对于上面的两句是异步的,异步传参,所以为了确保这两句在post方法后执行,可以把他放在end事件中。

在recall函数里,他要做的就不仅仅是显示在页面那么简单,而是要寻找到带{}标记的元素,把他替换为传来的参数,重写recall:

function recall(data){
    dataStr = data.toString();//字符串
    re = new RegExp('{name}','g');//new RegExp和/.../的区别是他可以在里面直接用字符串拼接的形式表示
    dataStr = dataStr.replace(re,post['name值']);
    res.write(dataStr);
    res.end();
}
optfile.readfile('./view/main.html',recall);

如果需要传输多个参数,使用数组

arr = ['email','pwd'];
function recall(data){
    dataStr = data.toString();
    for(var i = 0; i < arr.length; i++){
        re = new RegExp('{' + arr[i] + '}','g');
        dataStr = dataStr.replace(re,post[arr[i]]);
    }
    res.write(dataStr);
    res.end();
}

第三个showImg,协议头改成请求图片的协议头,然后读取图片即可:

showImg : function(req,res){
    res.writeHead(200,{'Content-Type':'image/jpeg'});
    optfile.readImg('./src/xin2.jpg',res);
}

完整的router.js:

var optfile = require('./models/optfile.js');
var url = require('url');
var querystring = require('querystring');//post需导入

function getRecall(req,res){
    res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});
    function recall(data){
        res.write(data);
        res.end('');
    }
    return recall;
}

module.exports = {
    login : function(req,res){
        recall = getRecall(req,res);//这个recall并不是getRecall,而是getRecall返回的闭包recall
        optfile.readfile('./view/login.html',recall)
    },
    deliver : function(req,res){
        var post = '';
        req.on('data',function(chunk){
            post += chunk;
        })
        req.on('end',function(){
            post = querystring.parse(post);
            console.log('收到参数:' + post['name'] + '\n');
            console.log('收到参数:' + post['password'] + '\n');
            
            function recall(data){
                dataStr = data.toString();
                re = new RegExp('{name}','g');
                dataStr = dataStr.replace(re,post['name']);
                res.write(dataStr);
                res.end();
            }
            optfile.readfile('./view/main.html',recall);
        })
    },
    showImg : function(req,res){
        res.writeHead(200,{'Content-Type':'image/jpeg'});
        optfile.readImg('./src/xin2.jpg',res);
    }
}

 

来说optfile.js了!optfile.js暂时有三个操作,同、异步读文件和读取图片。

var fs = require('fs');
module.exports = {
    readfileSync : function(path){
        var data = fs.readFileSync(path,'utf-8');
    },
    readfile : function(path,res){
        fs.readFile(path,function(err,data){
            if(err){
                console.log('读文件异常!');
            }else{
                console.log('读好了');
            }
        })
    },
    readImg : function(path,res){
        ...
    }
}

大概这么写,但是上面有说,如果想在readFile中添加一个操作,比如说需求中将data显示在客户端,此时可能的思路是把console.log('读好了');替换为res.write(data) 之类。但是不对。因为readFile方法是异步的,在异步里,主线程执行过程中会有很多分线程来执行各种不同的操作,但分线程并不影响主线程的执行。在读取文件的这个分线程中,如果直接执行res.write(data)会报错,因为主线程此时可能已经执行完了。

所以这里要调用到之前写的闭包。虽然recall()在这个线程中执行,但原来的response并没有被清除掉,还在等待着recall();当recall返回一个data时,原来的response接收到了,进行操作。这个闭包储存了原来的req和res用来操作,并解决了分线程end的问题。

readfile : function(path,recall){
    fs.readFile(path,function(err,data){
        if(err){
            console.log('读取文件分线程错误:'+err);
            recall('文件不存在');
        }else{
            console.log('异步读取文件data为:' + data.toString());
            recall(data);
        }
    })
    console.info('异步读取文件方法执行了');
},

如果readFile这里异步线程出错,主线程可以执行,异步可以读取出来,但如果这里有错误并且没有回调recall('文件不存在');的话,记载图标会一直转,所以这里处理异常要使用recall('文件不存在'),其中包括错误提示和结束线程。

readImg和文件读取差不多,就是图片需要用二进制流'binary'的方式读取。

完整的optfile.js:

var fs = require('fs');
module.exports = {
    readfileSync : function(path){
        var data = fs.readFileSync(path,'utf-8');
        console.info('同步读取文件执行完毕,data为:' + data);
    },
    readfile : function(path,recall){
        fs.readFile(path,function(err,data){
            if(err){
                console.log('读取文件分线程错误:'+err);
                recall('文件不存在');
            }else{
                recall(data);
            }
        })
        console.info('异步读取文件方法执行了');
    },
    readImg : function(path,res){
        fs.readFile(path,'binary',function(err,imgdata){
            if(err){
                console.log('读图片分线程错误:'+ err);
                return;
            }else{
                res.write(imgdata,'binary');
                res.end('');
            }
        })
    }
}

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:关于JavaScript的innerWidth与innerHeight

下一篇:bootstrap可编辑下拉框jquery.editable-select