首先下载node,下载好后查看版本node -v
【项目创建】对于express,其实有一种更普遍的方法,那就是直接用它来创建一个项目:
比如,我要创建项目express_test 就输入:express -e express_test
项目创建成功之后,生成4个文件夹,主文件app.js与配置信息文件packetage.json
①bin是项目的启动文件,配置以什么方式启动项目,默认 npm start
②public是项目的静态文件,放置js css img等文件
③routes是项目的路由信息文件,控制地址路由
④views是视图文件,放置模板文件ejs或jade等(其实就相当于html)
⑤express这样的MVC框架模式,是一个Web项目的基本构成
【模块安装】:
一般我们创建新项目的时候也要先给他安装相应的模块
我们直接使用npm install 就行了,它会自动检测package.json文件下载安装相应的模块
之后可以先启动下项目:默认 npm start,启动成功后可以到浏览器查看下,或者自己修改----http://localhost:3000/
【最后】随手谈一下上面提到的require相关的知识
编写稍大一点的程序时一般都会将代码模块化,在NodeJS中,一般将代码合理拆分到不同的JS文件中,每一个文件就是一个模块,而文件路径就是模块名。
在编写每个模块时,都有require、exports、module三个预先定义好的变量可供使用。
(一)require函数用于在当前模块中加载和使用别的模块,传入一个模块名,返回一个模块导出对象。
模块名可使用相对路径(以./开头),或者是绝对路径(以/或C:之类的盘符开头),模块名中的.js扩展名可以省略
举个例子:var func1 = require("./func1");// 是与当前main.js同路径下的func1.js模块
(二)exports对象是当前模块的导出对象,用于导出模块公有方法和属性
别的模块通过require函数使用当前模块时得到的就是当前模块的exports对象
比如上面提到的,我们可以这样写代码然后导出来:
exports.func1 = function(){
console.log("This is func1");
};
(三)module对象可以访问到当前模块的一些相关信息,但最多的用途是替换当前模块的导出对象(将导出对象改为函数)
例如模块导出对象默认是一个普通对象,如果想改成一个函数的话,可以使用以下方式。
func1.js:
module.exports = function () {
console.log('Hello World!');
};
这样一来模块默认导出对象被替换为一个函数
//在func1.js里边这样
module.exports = function(name, age) {
this.name = name;
this.age = age;
this.about = function() {
console.log(this.name +' is '+ this.age +' years old')}};
//然后在main.js里边这样
var Func1 = require('./func1.js');
var r = new Func1('xiaoming', 12);
r.about(); // xiaoming is 12 years old
【解析小段node代码】:
var http = require('http');上面说到了,http是nodeJS里边内置的对象模块,我们使用require这种方式把它引进来
然后http现在这个对象模块有一个方法是http.createServer(),这个方法创建一个服务之后再监听一个地址:http.createServer().listen(port,ip)
从而搭建了一个服务器
createServer()里边有一个匿名函数,主要就是用来处理相关信息了。req是请求request,res是回复response。
req一般用于请求阶段的解析处理等,比如常见的get中的地址栏url字符的处理。res一般用于收到请求后相应的操作,比如写响应头响应体渲染页面等等。
在这里,res.writeHead(200, {'Content-Type': 'text/plain'});就相当于写了个响应头, res.end('Hello World\n');相当于写了个响应体。
nodeJS提供了很多内置对象方法,比如http,fs,EventEmitter,url等等
【构建项目】
(一)修改模板引擎文件:
现在开始解析如何构建这个小项目:直接使用了后缀名 .html ,所以我们要先修改一下ejs模板,再把原来views目录下模板文件后缀改成 .html
var app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); app.engine("html",require("ejs").__express); //app.set("view engine","ejs"); app.set('view engine', 'html');
(二)创建模板:error.html,home.html,index.html,login.html,register.html
其中 <%= title %>使用到了模板 连接<a> 直接使用了路由路径的方法,这里我的bootstrap引用的网上的,如果本地有的话可以调用本地
<h1><%= message %></h1> <h2><%= error.status %></h2> <pre><%= error.stack %></pre>
user.name 就是使用ejs模板通过session.user来获取user对象,这里user有name和password的属性
<!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> <style type="text/css"> a{margin-left: 20px; text-decoration: none;} a:hover{text-decoration: underline;} </style> </head> <body> <h1>Your name: <%- user.name %></h1> <p>Welcome to your home ~</p> <p><a href="/logout">我要注销 </a> </p> </body> </html>
<!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> <style type="text/css"> a{margin-left: 20px; text-decoration: none;} a:hover{text-decoration: underline;} </style> </head> <body> <h1><%= title %></h1> <p>Welcome to <%= title %></p> <p><a href="/login">登录 </a> <a href="/register"> 注册</a> </p> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title><%= title %></title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> <style type="text/css"> .m15{ margin: 15px;} .tc{ text-align: center;font-size: 18px;font-weight: 600;} </style> </head> <body screen_capture_injected="true"> <div class="container"> <%- message %> <form class="col-sm-offset-4 col-sm-4 form-horizontal" role="form" method="post" onsubmit="return false"> <fieldset> <legend></legend> <div class="panel panel-default"> <div class="panel-heading"> <p class="tc">请先登录</p> </div> <div class="panel-body m15"> <div class="form-group"> <div class="input-group"> <span class="input-group-addon"> <span class="glyphicon glyphicon-user"></span> </span> <input type="text" class="form-control" id="username" name="username" placeholder="请输入用户名" required> </div> </div> <div class="form-group"> <div class="input-group"> <span class="input-group-addon"> <span class="glyphicon glyphicon-lock"></span> </span> <input type="text" class="form-control" id="password" name="password" placeholder="请输入密码" required> </div> </div> <div class="form-group"> <button type="submit" class="btn btn-primary btn-block" id="login0">登录</button> </div> <div class="form-group"> <button type="button" class="btn btn-info col-sm-2 col-sm-offset-10" id="register0">注册</button> </div> </div> </div> </fieldset> </form> </div> <script src="https://cdn.bootcss.com/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> <script type="text/javascript"> $(function(){ $("#register0").click(function(){ location.href = 'register'; }); $("#login0").click(function(){ var username = $("#username").val(); var password = $("#password").val(); var data = {"uname":username,"upwd":password}; $.ajax({ url:'/login', type:'post', data: data, success: function(data,status){ if(status == 'success'){ location.href = 'home'; } }, error: function(data,status){ if(status == 'error'){ location.href = 'login'; } } }); }); }); </script> </body> </head> </html>
注册方式主要是把原始 form表单 onsubmit="return false" 防止默认提交,然后在输入信息正确的情况下,通过ajax,把表单信息post到路径/register
然后我们就通过路由功能根据此路径来处理信息
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title><%= title %></title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> <style type="text/css"> .m15{ margin: 15px;} .tc{ text-align: center;font-size: 18px;font-weight: 600;} </style> </head> <body screen_capture_injected="true"> <div class="container"> <%- message %> <form class="col-sm-offset-4 col-sm-4 form-horizontal" role="form" method="post" onsubmit="return false"> <fieldset> <legend></legend> <div class="panel panel-default"> <div class="panel-heading"> <p class="tc">注册信息</p> </div> <div class="panel-body m15"> <div class="form-group"> <div class="input-group"> <span class="input-group-addon"> <span class="glyphicon glyphicon-user"></span> </span> <input type="text" class="form-control" id="username" name="username" placeholder="请输入用户名" required> </div> </div> <div class="form-group"> <div class="input-group"> <span class="input-group-addon"> <span class="glyphicon glyphicon-lock"></span> </span> <input type="text" class="form-control" id="password" name="password" placeholder="请输入密码" required> </div> </div> <div class="form-group"> <div class="input-group"> <span class="input-group-addon"> <span class="glyphicon glyphicon-lock"></span> </span> <input type="text" class="form-control" id="password1" name="password1" placeholder="请再次输入密码" required> </div> </div> <div class="form-group"> <button type="submit" class="btn btn-primary btn-block" id="register1">注册</button> </div> <div class="form-group"> <button type="button" class="btn btn-info col-sm-2 col-sm-offset-10" id="login1">登录</button> </div> </div> </div> </fieldset> </form> </div> <script src="https://cdn.bootcss.com/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> <script type="text/javascript"> $(function(){ $("#login1").click(function(){ location.href = 'login'; }); $("#register1").click(function(){ var username = $("#username").val(); var password = $("#password").val(); var password1 = $("#password1").val(); if(password !== password1){ $("#password").css("border","1px solid red"); $("#password1").css("border","1px solid red"); }else if(password === password1){ var data = {"uname":username,"upwd":password}; $.ajax({ url: '/register', type: 'post', data: data, success: function(data,status){ if(status == 'success'){ location.href = 'login'; } }, error: function(data,err){ location.href = 'register'; } }); } }); }); </script> </body> </head> </html>
(三)配置模板路由,让浏览器访问到路径后得以被解析
在app.js里添加路由配置
var express = require('express'); var routes = express.Router(); app.use('/', routes); // 为路径 / 设置路由 app.use('/users', users); // 为路径 /users 设置路由 app.use('/login',routes); // 为路径 /login 设置路由 app.use('/register',routes); // 为路径 /register 设置路由 app.use('/home',routes); // 为路径 /home 设置路由 app.use("/logout",routes); // 为路径 /logout 设置路由 //app.use是一个中间件的用法,这里的routes看初始项目的下两句代码,就是引用了routes文件夹下的index.js模块 //var routes = require('./routes/index'); //var users = require('./routes/users');
(四)创建项目数据库和超级管理员:
【操作数据库】数据库集合collection,文档设置insert
cmd命令行里:
mongo//进入数据库
use nodedb//创建项目数据库
db.addUser("shuaige", "123456")//给这个数据库创建了一个叫帅哥的账号,密码123456 (但是我觉得可能我理解的不到位,你也可以不做这个操作)
若以授权方式启动数据库,那么我们可以进行管理员验证,之后可以继续操作
然后,我们就为这个nodedb数据库创建collection(collection就相当于oracle和mysql里的table)
db.createCollection("users")//创建一个集合,也就是表 db.users.insert({user: "admin", password: "123456"})//给users里添加一个文档,也就是一条记录账号admin,密码123456
ok,现在检查一下:
db.users.find() //如果看到你刚刚添加的文档记录,就ok咯
现在我的nodedb数据库里已经有了用户admin,之后操作就在nodedb数据库里进行
(五)连接数据库:
mongodb主要有两种使用方法,这里使用了其中的一种:使用 mongoose
Mongoose是MongoDB的一个对象模型工具,是基于node-mongodb-native开发的MongoDB nodejs驱动,可以在异步的环境下执行。
同时它也是针对MongoDB操作的一个对象模型库,封装了MongoDB对文档的的一些增删改查等常用方法,让NodeJS操作Mongodb数据库变得更加灵活简单
安装模块:npm i mongoose
在项目根目录下建立一个database文件夹,建立文件models.js然后建立model处理文件 dbHandel.js
写入文件 models.js,一个user集合,有name和password属性
module.exports = { user:{ name:{type:String,required:true}, password:{type:String,required:true} } };
写入文件 dbHandel.js 里边主要是获取 Schema 然后处理获取 model ,最后就是返回一个model了(提供其他文件对model的操作)
var mongoose = require('mongoose'); var Schema = mongoose.Schema; var models = require("./models"); for(var m in models){ mongoose.model(m,new Schema(models[m])); } module.exports = { getModel: function(type){ return _getModel(type); } }; var _getModel = function(type){ return mongoose.model(type); };
app.js加上连接数据库:
var mongoose = require('mongoose'); global.dbHandel = require('./database/dbHandel'); global.db = mongoose.connect("mongodb://localhost/nodedb"); app.use(bodyParser.urlencoded({ extended: true })); app.use(cookieParser());app.js加上session模块,因为使用到了session(比如进入home的时候判断session值是否为空),所以需要express-session 模块 命令:npm i express-session,在app.js中引用它并作初始设置
var session = require('express-session'); var app = express(); app.use(session({ secret: 'secret' cookie:{ maxAge: 1000*60*30 } })); app.use(function(req,res,next){ res.locals.user = req.session.user; // 从session 获取 user对象 var err = req.session.error; //获取错误信息 delete req.session.error; res.locals.message = ""; // 展示的信息 message if(err){ res.locals.message = '<div class="alert alert-danger" style="margin-bottom:20px;color:red;">'+err+'</div>'; } next(); //中间件传递 });
【好现在想想我们还剩下什么:】
数据库已经提供出model接口给我们使用(给它填数据)
已经初始化了路径处理
初始化了session信息 数据库配置等
页面模板也已经做完
所以剩下的就是路径处理的部分:去routes目录下修改index.js
/ 路径:
/* GET index page. */ router.get('/', function(req, res,next) { res.render('index', { title: 'Express' }); // 到达此路径则渲染index文件,并传出title值供 index.html使用 });
/login 路径:
/* GET login page. */ router.route("/login").get(function(req,res){ // 到达此路径则渲染login文件,并传出title值供 login.html使用 res.render("login",{title:'User Login'}); }).post(function(req,res){ // 从此路径检测到post方式则进行post数据的处理操作 //get User info //这里的User就是从model中获取user对象,通过global.dbHandel全局方法(这个方法在app.js中已经实现) var User = global.dbHandel.getModel('user'); var uname = req.body.uname; //获取post上来的 data数据中 uname的值 User.findOne({name:uname},function(err,doc){ //通过此model以用户名的条件 查询数据库中的匹配信息 if(err){ //错误就返回给原post处(login.html) 状态码为500的错误 res.send(500); console.log(err); }else if(!doc){ //查询不到用户名匹配信息,则用户名不存在 req.session.error = '用户名不存在'; res.send(404); // 状态码返回404 // res.redirect("/login"); }else{ if(req.body.upwd != doc.password){ //查询到匹配用户名的信息,但相应的password属性不匹配 req.session.error = "密码错误"; res.send(404); // res.redirect("/login"); }else{ //信息匹配成功,则将此对象(匹配到的user) 赋给session.user 并返回成功 req.session.user = doc; res.send(200); // res.redirect("/home"); } } }); });
/register 路径
/* GET register page. */ router.route("/register").get(function(req,res){ // 到达此路径则渲染register文件,并传出title值供 register.html使用 res.render("register",{title:'User register'}); }).post(function(req,res){ //这里的User就是从model中获取user对象,通过global.dbHandel全局方法(这个方法在app.js中已经实现) var User = global.dbHandel.getModel('user'); var uname = req.body.uname; var upwd = req.body.upwd; User.findOne({name: uname},function(err,doc){ // 同理 /login 路径的处理方式 if(err){ res.send(500); req.session.error = '网络异常错误!'; console.log(err); }else if(doc){ req.session.error = '用户名已存在!'; res.send(500); }else{ User.create({ // 创建一组user对象置入model name: uname, password: upwd },function(err,doc){ if (err) { res.send(500); console.log(err); } else { req.session.error = '用户名创建成功!'; res.send(200); } }); } }); });
/home 路径:
/* GET home page. */ router.get("/home",function(req,res){ if(!req.session.user){ //到达/home路径首先判断是否已经登录 req.session.error = "请先登录" res.redirect("/login"); //未登录则重定向到 /login 路径 } res.render("home",{title:'Home'}); //已登录则渲染home页面 });
/logout路径:
/* GET logout page. */ router.get("/logout",function(req,res){ // 到达 /logout 路径则登出, session中user,error对象置空,并重定向到根路径 req.session.user = null; req.session.error = null; res.redirect("/"); });