如何使用Node.js核心快速搭建服务器 【完整教程源码】

790 阅读6分钟
'假设你已经了解什么是express框架 mongodb数据库(非关系型数据库) 那么让我们开始吧!'


Node.js的核心,通过npm下载或者自己有的包,require引入时不需要加/,
但是自己写的模块则需要加,否则找不到,Node.js使用的是commonJs的模块
化规范哦 
 
'让我们开始吧!' 

github源码地址

项目需求

  • 登陆,注册,主页,各一个界面(路由)
  • 不需要静态资源,需要前后端能交互,在对应的位置提醒用户更新不同的内容
  • 用户如果不登陆,是不能访问主页。如果用户在规定有效期内登陆成功过,也可以访问主页
  • 用户如果登陆/注册失败,要提醒对应的全部失败原因,并且除了密码外都要保存在页面中(不让客户重复输入)
  • 用户的密码在保存数据库中时,需要使用sha1的包进行加密,在查询时候也需要加密
  • 用户的session_id也需要加密然后保存在数据库中
  • 对用户的session_id所对应的cookie也要设定一个有效期
  • ...后续可能加一些防攻击的需求,在应用型的中间件中。

所需要的下载的包
  "dependencies": {
    "cookie-parser": "^1.4.4",    //解析cookie 
    "ejs": "^2.6.1",   //ejs的模板渲染
    "express": "^4.16.4",    //express框架 
    "express-session": "^1.15.6",   //session的处理  
    "mongoose": "^5.4.19"   //mongoose包,处理mongodb数据库的
  },
  "devDependencies": { 
    "nodemon": "^1.18.10",  //每次修改JS文件后服务器自动更新
    "sha1": "^1.1.1"   //对数据加密的模块 
  }
  
  
'直接复制我上面的json文件内容,把后面的注释删除掉即可 npm install  下载'


 我们使用ejs的渲染引擎进行渲染
 
 首先看主文件的入口  
 
 -------
 ejs.js  
 '这里首先引入了express框架后,还引入了session的处理模块,以及自定义的uirouter、
 inforrouter两个路由器路由模块 ,设置了ejs的渲染目录views,还指定了渲染
 引擎为ejs(ejs的模块不需要引入,设置就好),'
const express = require('express');
const app = express();
const mongoose = require('./database');
const uirouter = require('./uirouter');
const inforouter = require('./inforouter');
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);
app.set('views', 'views');
app.set('view engine', 'ejs');
app.use(session({     //设置session
    secret: 'hello',     //对session_id的加密 
    saveUninitialized: false,   
    resave: false,  
    store: new MongoStore({
        url: 'mongodb://localhost:27017/user',
        touchAfter: 24 * 3600   
    }),
    cookie:{maxAge:1000*3600*24*7},   
    //session是基于cookie传送,cookie默认是会话存储,需要指定有效期
})) 
mongoose.then(async () => {      //设置路由器路由,从下往下解析 
    app.use(uirouter);    
    app.use(inforouter);
})
app.use((err, req, res, next) => {  //错误机制处理路由 
    if (err) {
        console.log(err);
    }
})
app.listen(5555, err => {   //监听端口号 
    if (!err) {
        console.log('监听成功');
    } else {
        console.log(`监听失败${err}`);
    }
});
 
 ----------------
 
 'database.js 这个是数据库的连接模块 '
 
 ---------------------
 
 '这里暴露的是一个promise对象,必须先连接成功数据库再走下一步,所以主模块使用了.then()'
const mongoose = require('mongoose');
module.exports = new Promise((resolve, reject) => {
    mongoose.connect('mongodb://localhost:27017/user', { useNewUrlParser: true, useCreateIndex: true });
    //指定连接的域名和端口号,以及数据库的名称
    
    mongoose.connection.once('open', err => {
        if (!err) {
            console.log('数据库连接成功');
            resolve();
        } else {
            console.log('数据库连接失败:' + err);
            reject(err);
        }
    })
})


---------------------
 
 
 'model.js 这个是模型对象模块, 暴露的是模型对象,可以使用它身上对数据库的CRUD操作'
 
const { Schema, model } = require('mongoose');
const schema = new Schema({
    username: {
        type: String,
        unique: true,
        required: true
    },
    password: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true
    }
});
const Model = model('userinfos', schema);  
//指定数据库的模型对象,以及保存的集合名称为 'userinfos'
module.exports = Model;

-----


'inforrouter 这个是注册模块的文件 '

const { Router, urlencoded } = require('express');
const sha1 = require('sha1');  
const router = new Router();
const model = require('./model');
router.use(urlencoded({ extended: true }));
router.post('/login', async (req, res) => {
    let { username, password } = req.body;
    const result = await model.findOne({ username, password: sha1(password) });
    //'如果能通过用户名和加密后的密码在数据库中查找到,那么给它添加session_id然后跳转到主页'
    if (result) {
        req.session.userid = result.id
        res.redirect('/index')
    } else {
        res.render('login', { err: { username: username, usernameerr: '用户名或密码错' } });
        //'如果找不到,渲染login.ejs文件,并且渲染对应的err数据到页面上'
    }
})
router.post('/re', async (req, res) => {
    let { username, password, repassword, email } = req.body;
    //ES6的解构赋值,提取post请求体中的四个对应的数值
    const usernameReg = /^[A-Za-z0-9_]{5,10}$/;  
    const passwordReg = /^[A-Za-z0-9_]{5,12}$/;
    const emailReg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
    //三个正则匹配
    let err = {};
    const result = await model.findOne({ username });
    if (!usernameReg.test(username)) {
        err.usernameerr = '用户名格式不正确';
    }
    if (!passwordReg.test(password)) {
        err.password = '密码格式不正确';
    }
    if (password != repassword) {
        err.repassword = '跟上次输入密码不一致';
    }
    if (!emailReg.test(email)) {
        err.email = '邮箱格式不正确';
    }
    if (result) {
        err.usernameerr = '用户名已存在'
    }
    
    if (err.usernameerr || err.password || err.repassword || err.emailerr) {
        res.render('re', { err })
        return;
        //'只要有一个错误存在,那么就渲染re.ejs文件,并且渲染err这个数据'
    }
    model.create({
        username,
        password: sha1(password),
        email
        
    })
    res.redirect('/index')
    //'如果上面的错误都没有,那么就保存这个文档对象在数据库中,
    并且跳转到index的路由上,index的路由也有处理机制'
})
module.exports = router;

------------


'uirouter.js 是处理get请求的路由文件'


----------------


const { Router } = require('express');
const model = require('./model');
const router = new Router();
router.get('/login', async (req, res) => {
    res.render('login.ejs', { err: '' })
    //'渲染对应的ejs文件,这里如果不写空的err渲染数据,
    后期的渲染会报错,会提示err not defined'
})
router.get('/re', (req, res) => {
    res.render('re.ejs', { err: "" });
})
router.get('/index', async (req, res) => {
    const { userid } = req.session;
    if (!userid) {
        res.redirect('/login')
        return;
    }
    const result = await model.findById({ _id: userid });
    if (result) {
        res.render('index');
    } else {
        res.redirect('/login');
    }
    //'如果用户成功登陆过,那么会有一个session_id文件,
    如果有并且能在数据库中找到,那么就让用户去index.ejs
    的渲染文件'
})
module.exports = router;



-----------------


'views文件夹,下面的三个ejs文件,是使用ejs渲染引擎的渲染文件'


'index.ejs '


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <h1>欢迎来到首页</h1>
    <a href="/login">登陆界面</a>
    <a href="/re">注册界面</a>
</body>

</html>


-------------------------

'login.ejs 是登陆文件'



<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .err{
            color:red;
        }
    </style>
</head>

<body>
    <form action="/login" method="post">
        <label for="username">用户名</label>
        <input type="text" name="username" value=<%= err.username %> ><span class="err"> <%= err.usernameerr %> </span> <br />
        <label for="password">密码</label>
        <input type="password" name="password"><span  class="err"> </span> <br />
        <input type="submit" value='登陆'>
    </form>
</body>

</html>


-------------------



're.js 是注册页面文件  '


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .err{
            color:red;
        }
    </style>
</head>
<body>
    <form action="/re" method="post">
        <label for="username">用户名</label>
        <input type="text" name="username"  value=<%= err.username %>  ><span class='err'><%= err.usernameerr %></span><br />
        <label for="password">密码</label>
        <input type="password" name="password"><span class='err'><%= err.password%></span><br />
        <label for="repassword">再次确认密码</label>
        <input type="password" name="repassword"><span class='err'><%= err.repassword%></span><br />
        <label for="email">邮箱</label>
        <input type="text" name="email"   value=<%= err.email %> ><span class='err'><%= err.emailerr%></span><br />
        <input type="submit" value='注册'>
    </form>
</body>
</html>

--------------------------

'在浏览器中输入的url地址访问都是get请求,所以我们把get请求也成为uirouter,但是一旦到了提交表单,
我们使用的就是post请求的路由了,同样名字的路由,但是请求方式不一样他们并不冲突,Node.js中代码
也是从上到下解析的,这里的ejs渲染模板需要多注意的是get请求的时候一定要先给一个空的渲染数据,否
则后期post请求是渲染不上去的,会提示not defined,因为肯定是先get请求看到页面后才能提交数据,这
点要格外注意,其实express框架并不难,这个版本就能用到express中最核心的所有知识点,能掌握了就能
独立开发服务器和前后端交互以及数据处理',后面我会就web的存储技术,以及koa2框架继续深入