zoukankan      html  css  js  c++  java
  • nodejs+mongodb运用

    引言

    教程源于:


    该文章用于笔记整理

    gfX7rt.png


    准备

    node版本管理:nvm——https://github.com/creationix/nvm)。可以轻松地切换 Node.js 版本,也可以安装新版本用以尝试并且当出现问题时轻松地回滚

    一 线程模型

    1. 接收请求(request)
    2. 开出线程(thread)处理,用户等待
    3. 请求处理完成,线程(thread)释放

    线程处理模式重复以上三个步骤,来处理来自客户端的各种请求。当有大量客户端请求来袭时,服务器消耗的资源也会随之增加。

    二 node中的事件循环

    1. 开一个事件等待循环(event-loop)
    2. 接收请求
    3. 放入事件处理队列中,然后继续接收新的请求
    4. 请求完成后,调用I/O,结束请求(非阻塞调用)

    事件循环处理模式中,线程不用等待req处理完,而是将所有请求放入队列中,然后采用非同步的方式,等待请求处理完成后再调用I/O资源,然后结束请求。

    gW7UdH.jpg

    三 node中的非阻塞处理

    • 阻塞处理(Java,Ruby,PHP,Asp,Net)
    • 非阻塞处理(Node.js)

    阻塞处理:后边的语句无法执行,除非前面的执行完毕

    function updb1(){
        let start =new Date().getTime()
        while (new Date().getTime()<start+3000);
    }
    updb1()
    console.log('数据库更新成功!')
    console.log('其他语句')
    

    非阻塞处理:继续执行后面的。(必须提供回调函数)

    function updb2(done){
        setTimeOut(()=>{
            done()
        },3000)
    }
    updb2(function(){
        console.log('数据库更新成功!')
    })
    console.log('其他语句')
    

    四 搭建http服务器


    const http=require('http');
    const hostname='127.0.0.1';
    const port=3000;
    
    const server=http.createServer((req,res)=>{
        res.statusCode=200;//设置状态码
        res.setHeader('Content-Type','text/plain')//设置响应头
        res.end('Hello World')
    })
    
    server.listen(port,hostname,()=>{
        console.log(`Server running at http://${hostname}:${port}/`);
    })
    

    一般情况下,每次更代码都需重新启动服务器,否则页面将没有变化。supervisor工具可以在不用重复启动服务器的情况下就能看到页面变化:

    npm install -g supervisor	//全局安装
    supervisor app.js	//使用supervisor替代node命令来运行程序
    

    五 引入自定义模块

    默认情况下js文件是私有的。要让外部访问,需通过exportsmodule.exports暴露:

    • require:引用外部文件
    • exports、module.exports:公开文件

    例子:hostname和port属于公共部分,可以抽出来作为模块

    • config.js

      const config={
          hostname:'127.0.0.1',
          port:3000
      }
      exports.config=config
      
    • server.js

      const http=require('http');
      const config=require('./config').config //引用模块
      
      const server=http.createServer((req,res)=>{
          res.statusCode=200;
          res.setHeader('Content-Type','text/plain')
          res.end('Hello World')
      })
      
      server.listen(config.port,config.hostname,()=>{
          console.log(`Server running at http://${config.hostname}:${config.port}/`);
      })
      

    另一种写法:

    • config.js

      const config={
          hostname:'127.0.0.1',
          port:3000
      }
      module.exports=config
      
    • server.js

      const config=require('./config')
      

    六 路由

    const http=require('http');
    const hostname='127.0.0.1';
    const port=3000;
    
    const server=http.createServer((req,res)=>{
        res.statusCode=200;
        res.setHeader('Content-Type','text/plain')
        
        switch(req.url){
            case '/':
                res.end('Hello World');
                break;
            case '/about':
                res.end('This is about page')
                break;
            case '/home':
                res.end('Welcome to myhomepage')
                break;
            default:
                res.end('404 NOT FOUND!')
        }
    })
    
    server.listen(port,hostname,()=>{
        console.log(`Server running at http://${hostname}:${port}/`);
    })
    

    七 搭建静态服务器


    7.1 读取html

    ./index.html

    <html>
        <body>
            <h1>Hello world!</h1>
        </body>
    </html>
    

    ./server.js

    const http=require('http');
    const fs=require('fs')
    
    //__dirname:当前文件所在目录
    const server=http.createServer((req,res)=>{
        fs.readFile(__dirname+'/index.html','utf-8',(err,data)=>{
            if(err){
                res.setHeader('Content-Type','text/plain');//纯文本
                res.statusCode=404;
                res.end('404 Not Founded!')
            }else{
                res.setHeader('Content-Type','text/html');//html
                res.statusCode=200;
                res.end(data)
            }
        })
    })
    
    const hostname='127.0.0.1';
    const port=3000;
    server.listen(port,hostname,()=>{
        console.log(`Server running at http://${hostname}:${port}/`);
    })
    

    7.2 读取其他

    由于不同文件的Content-Type不一样,所以这里封装一个根据后缀名获取文件Content-Type的方法:

    ./modules.js

    const fs=require('fs')
    exports.getMimeType=function (extname){
        let data=fs.readFileSync('./mime.json') 
        let mimeObj=JSON.parse(data.toString())//转为对象
        return mimeObj[extname]
    };
    //mime.json可在网上找:格式{".html": "text/html"}
    

    ./server.js

    const http=require('http');
    const fs=require('fs')
    const path=require('path');
    const common=require('./modules');
    
    const server=http.createServer((req,res)=>{
        let pathname=req.url
        pathname=pathname=='/'?'/index.html':pathname
    	let extname=path.extname(pathname)//获取文件后缀名
        fs.readFile('./static'+pathname,(err,data)=>{
            if(err){
            	res.setHeader('Content-Type','text/plain');
                res.statusCode=404;
                res.end('404 Not Founded!')
            }else{
                let mime=common.getMimeType(extname)
                res.setHeader('Content-Type', mime+";charset='utf-8'");
                res.statusCode=200;
                res.end(data)
            }
        })
    })
    
    const hostname='127.0.0.1';
    const port=3000;
    server.listen(port,hostname,()=>{
        console.log(`Server running at http://${hostname}:${port}/`);
    })
    

    7.3 封装静态服务器

    ./module/route.js

    const fs=require('fs')
    const path=require('path');
    const url=require('url');
    
    function getMimeType(extname){
        let data=fs.readFileSync('./mime.json')
        let mimeObj=JSON.parse(data.toString())//转为对象
        return mimeObj[extname]
    };
    
    exports.static=function(req,res,staticPath){
        let pathname=url.parse(req.url,true).pathname
        pathname=pathname=='/'?'/index.html':pathname
        let extname=path.extname(pathname)
        fs.readFile('./'+staticPath+pathname,(err,data)=>{
            if(err){
                res.setHeader('Content-Type','text/plain');
                res.statusCode=404;
                res.end('404 Not Founded!')
            }else{
                let mime=getMimeType(extname)
                res.setHeader('Content-Type', mime+";charset='utf-8'");
                res.statusCode=200;
                res.end(data)
            }
        })
    }
    

    ./server.js

    const http = require('http');
    const route= require('./module/route');
    
    http.createServer(function (req, res) {
        route.static(req, res,'static')
    }).listen(3000);
    

    八 使用第三方包ejs

    • ejs(effective JavaScript template)(第三方模块)——指路-官方文档
    • querystring(node内置模块)——查询字符串,指路-官方文档
    • npm:Nodejs附带的第三方软件包管理器。是世界上最大的开放源代码的生态系统,可通过它下载各种各样的包,为Nodejs提供更多的功能支持——指路-npm与依赖包
    • fs

    8.1 基本使用

    安装

    npm install ejs
    

    ./helo.ejs

    <html>
        <title><%= title %></title>
        <body>
            <%- content %>
        </body>
    </html>
    

    ./server.js

    const http=require('http');
    const fs=require('fs')
    const ejs=require('ejs')
    var template=fs.readFileSync(__dirname+'/helo.ejs','utf-8'); 
    
    const server=http.createServer((req,res)=>{
        //模板渲染-传值
        var data=ejs.render(template,{
            title:'helo ejs',
            content:'<strong>big helo ejs.</strong>'
        })
        res.setHeader('Content-Type','text/html');
        res.statusCode=200;
        res.end(data)
    })
    
    const hostname='127.0.0.1';
    const port=3000;
    server.listen(port,hostname,()=>{
        console.log(`Server running at http://${hostname}:${port}/`);
    })
    

    8.2 get/post

    ./list.ejs

    <!DOCTYPE html>
    <html lang="zh-cmn-Hans">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>列表</title>
    </head>
    <body>
        <form action="" method="post">
            <input type="text" name="content" id="content">
            <input type="submit" value="提交">
            <ul>
                <% for(let i=0 ; i < posts.length;i++){  %>
                    <li><%= posts[i] %></li>
                <% } %>
            </ul>
        </form>
    </body>
    </html>
    

    ./server.js

    const http=require('http');
    const fs=require('fs')
    const ejs=require('ejs')
    const qs=require('querystring') //+ 处理用户提交的表单数据
    
    var template=fs.readFileSync(__dirname+'/list.ejs','utf-8'); 
    var posts=[] //+ 用户输入的内容
    
    const server=http.createServer((req,res)=>{
        if(req.method==='POST'){
            req.data="";
            req.on("data",function(postDataChunk){
                req.data+=postDataChunk
            })        
            req.on("end",function(){
                //表单处理
                let query=qs.parse(req.data)//将数据进行内部解析,生成查询变量
                posts.push(query.content)//content就是取到表单name为content的内容
                showForm(posts,res)
            })
        }else{
            showForm(posts,res)
        }
    })
    
    //渲染页面
    function showForm(posts,res){
        let data=ejs.render(template,{
            title:'列表',
            posts
        })
        res.setHeader('Content-Type','text/html');
        res.statusCode=200;
        res.end(data)
    }
    
    const hostname='127.0.0.1';
    const port=3000;
    server.listen(port,hostname,()=>{
        console.log(`Server running at http://${hostname}:${port}/`);
    })
    

    九 使用MongoDB


    9.1 连接MongoDB

    驱动安装

    npm install mongodb --save
    

    ./connect.js

    //mongodb客户端
    const MongoClient=require('mongodb').MongoClient
    
    // 判断某两值是否相等的库
    const test=require('assert')
    
    // mongodb的连接地址
    const url='mongodb://127.0.0.1:27017'
    
    // 数据库名称-指定数据库名称
    const dbName='admin'
    
    // 连接使用客户端
    MongoClient.connect(url, function(err, client) {
        test.equal(null, err);//判断错误对象是否为空,为空说明成功
        const adminDb = client.db(dbName)// 打开数据库进行操作
        console.log('链接数据库成功')
        client.close();
    });
    

    9.2 插入数据

    ./insert.js

    const MongoClient=require('mongodb').MongoClient
    const test=require('assert')
    const url='mongodb://127.0.0.1:27017'
    const dbName='mydata'//这里的mydata数据库是提前创建好的,其下创建了posts集合。
    
    MongoClient.connect(url, function(err, client) {
        test.equal(null, err);
        const db = client.db(dbName)
        console.log('链接数据库成功')
        db.collection("posts").insertMany(
            [
                {title:"今天不是很热",tag:"life"},
                {title:"外卖还没到,非常饿!",tag:"life"},
            ],
            (err,result)=>{
                test.equal(null,err)
                client.close();
            }
        )
    });
    

    9.3 查询数据

    ./find.js

    const MongoClient=require('mongodb').MongoClient
    const test=require('assert')
    const url='mongodb://127.0.0.1:27017'
    const dbName='mydata'
    
    MongoClient.connect(url, function(err, client) {
        test.equal(null, err);
        const adminDb = client.db(dbName)
        console.log('链接数据库成功')
        adminDb.collection("posts").find({tag:"study"}).toArray((err,docs)=>{
            test.equal(null, err);
            console.log(docs)
            client.close()
        })
    });
    

    十 处理异步

    引言-异步/非阻塞的处理方式

    10.1 回调地狱

    Node.js是非阻塞编程,那么在编码过程中会遇到很多的回调函数(Callback),如果多个处理的回调函数嵌套在一起的话,就会形成回调地狱,虽然对于程序的结果没有任何影响,但对于程序代码的可读性来说就是个地狱。

    function dbupd(sql, done) {
        setTimeout(() => done(sql + " upd ok."), 800);
    }
    dbupd("1.sql1", result => {
        console.log(result);
        dbupd("2.sql2", result => {
            console.log(result);
            dbupd("3.sql3", result => {
                console.log(result);
            });
        });
    });
    

    10.2 Promise解决

    // Promise函数嵌套解决方法
    function dbupAsync(sql) {
        const p = new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log(sql + " upd ok.");
                resolve(sql + ".ok");
            }, 800)
        });
        return p;
    }
    

    方法一:

    dbupAsync("2.sql1")
        .then(() => dbupAsync("2.sql2"))
        .then(() => dbupAsync("3.sql3"));
    

    方法二:async/await 更简洁

    async function upAllDB() {
        const result1 = await dbupAsync("3.sql1");
        const result2 = await dbupAsync("3.sql2");
        const result3 = await dbupAsync("3.sql3");
        console.log(result1, result2, result3);
    }
    upAllDB();
    

    十一 封装express路由

    ./moudle/route

    
    const fs=require('fs')
    const path=require('path');
    const url=require('url');
    
    //获取文件类型
    function getMimeType(extname){
        let data=fs.readFileSync('./mime.json')
        let mimeObj=JSON.parse(data.toString())//转为对象
        return mimeObj[extname]
    };
    
    //扩展res
    function changeRes(res){
        res.send=(data)=>{
            res.writeHead(200, { 'Content-Type': 'text/html;charset="utf-8"' });
            res.end(data);
        }
    }
    
    //静态web服务的方法
    function initStatic(req, res, staticPath) {
        //1、获取地址
        let pathname = url.parse(req.url).pathname;
        pathname = pathname == '/' ? '/index.html' : pathname;
        let extname = path.extname(pathname);
        //2、通过fs模块读取文件
        try {
            let data = fs.readFileSync('./' + staticPath + pathname);
            if (data) {
                let mime=getMimeType(extname)
                res.setHeader('Content-Type', mime+";charset='utf-8'");
                res.statusCode=200;
                res.end(data)
            }
        } catch (error) {
    
        }
    
    }
    
    
    let server = () => {
        let G = {
            _get:{},
            _post:{},
            staticPath:'static' //默认静态web目录
        };
    
        let app = function (req, res) {
            //扩展res的方法
            changeRes(res);
            //配置静态web服务
            initStatic(req, res,G.staticPath);
    
            let pathname = url.parse(req.url).pathname;
            //获取请求类型
            let method=req.method.toLowerCase();
            
            if (G['_'+method][pathname]) {
    
                if(method=="get"){ 
                    G['_'+method][pathname](req, res);  //执行方法
                }else{
                    //post  获取post的数据 把它绑定到req.body
                    let postData = '';
                    req.on('data',(chunk)=>{
                        postData+=chunk;
                    })
                    req.on('end',()=>{                  
                       req.body=postData;
                       G['_'+method][pathname](req, res);  //执行方法
                    })
                   
                }
    
            } else {
                res.writeHead(404, { 'Content-Type': 'text/html;charset="utf-8"' });
                res.end('页面不存在');
            }
        }
        //get请求
        app.get = function (str, cb) {
            //注册方法
            G._get[str] = cb;     
        }
        //post请求
        app.post = function (str, cb) {
            //注册方法
            G._post[str] = cb;      
        }
        //配置静态web服务目录
        app.static=function(staticPath){
            G.staticPath=staticPath;
        } 
    
        return app;
    }
    module.exports = server();
    

    十二 注册用户并存入数据库

    • ejs
    • mongodb
    • fs
    • querystring

    .server.js

    const http=require("http")
    const app=require('./module/route')//上面封装的
    const ejs=require('ejs')
    const qs=require('querystring')
    
    const MongoClient=require('mongodb').MongoClient
    const test=require('assert')
    const url='mongodb://127.0.0.1:27017'
    const dbName='mydata'
    
    http.createServer(app).listen(3000)
    
    //首页
    app.get('/',(req,res)=>{
        ejs.renderFile("./views/index.html",{},(err,data)=>{
            res.send(data)
        })
    })
    
    app.get('/register',(req,res)=>{
        ejs.renderFile("./views/register.ejs",{},(err,data)=>{
            res.send(data)
        })
    })
    
    //获取注册用户列表
    app.get('/userlist',(req,res)=>{
        MongoClient.connect(url, function(err, client) {
            test.equal(null, err);
            const db = client.db(dbName)
            console.log('链接数据库成功')
            db.collection("users").find().toArray((err,result)=>{
                test.equal(null, err);
                ejs.renderFile("./views/userlist.ejs",{
                    users:result
                },(err,data)=>{
                    res.send(data)
                    console.log('获取成功')
                    client.close()
                })  
            })
        });
        
    })
    
    //提交注册
    app.post('/submit',(req,res)=>{
        let body=qs.parse(req.body)
        MongoClient.connect(url, function(err, client) {
            test.equal(null, err);
            const db = client.db(dbName)
            console.log('链接数据库成功')
            db.collection("users").insertOne(
                body,
                (err,result)=>{
                    test.equal(null,err)
                    console.log('注册成功!')
                    res.send('注册成功!')
                    client.close();
                }
            )
        });
    })
    
  • 相关阅读:
    Windows性能调优: Perfomn.exe 和Perfmon /res
    WPF:逻辑树和视觉树
    AD FS 概述
    SQL Server : TRUSTWORTHY 数据库属性
    WCF:在开发期间应该注意的问题
    ASP.NET MVC 2中的数据验证
    SQL Server:如何在Service Broker发送消息验证失败后获取源消息
    GDI+:自定义控件时如何使用Region来输出特定区域
    LINQ to XML:如何替换XCData的内容
    javascript是否真的就不能实现跨站请求呢?
  • 原文地址:https://www.cnblogs.com/sanhuamao/p/14781766.html
Copyright © 2011-2022 走看看