express

Express

Express 应用程序生成器

通过应用生成器工具 express-generator 可以快速创建一个应用的骨架。

express-generator 包含了 express 命令行工具。通过如下命令即可安装:

npm install express-generator -g

$ express -h

  Usage: express [options] [dir]

  Options:

    -h, --help          输出使用方法
        --version       输出版本号
    -e, --ejs           添加对 ejs 模板引擎的支持
        --hbs           添加对 handlebars 模板引擎的支持
        --pug           添加对 pug 模板引擎的支持
    -H, --hogan         添加对 hogan.js 模板引擎的支持
        --no-view       创建不带视图引擎的项目
    -v, --view <engine> 添加对视图引擎(view) <engine> 的支持 (ejs|hbs|hjs|jade|pug|twig|vash) (默认是 jade 模板引擎)
    -c, --css <engine>  添加样式表引擎 <engine> 的支持 (less|stylus|compass|sass) (默认是普通的 css 文件)
        --git           添加 .gitignore
    -f, --force         强制在非空目录下创建

例如,如下命令创建了一个名称为 myapp 的 Express 应用。此应用将在当前目录下的 myapp 目录中创建,并且设置为使用 Pug 模板引擎(view engine):

express --view=pug myapp

npm install 安装所有依赖

生成的目录结构

.
├── app.js
├── bin
│   └── www //修改端口号
├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
│       └── style.css
├── routes
│   ├── index.js // 注册路由
│   └── users.js
└── views
    ├── error.pug
    ├── index.pug
    └── layout.pug

Live Reload

问题: 每次修改后台应用代码, 需要重新运行命令修改才生效

解决: 使用 nodemon 包

下载: npm install --save-dev nodemon

测试: 修改后台任何代码, 会自动重新运行最新的代码 (按下 ctrl + S)

使用路由

API

Routing refers to determining how an application responds to a client request to a particular endpoint, which is a URI (or path) and a specific HTTP request method (GET, POST, and so on).

Each route can have one or more handler functions, which are executed when the route is matched.

路由的定义方法如下

app.METHOD(PATH, HANDLER)

The following examples illustrate defining simple routes.Respond with Hello World! on the homepage:

app.get('/', function (req, res) {
  res.send('Hello World!')
})

Respond to POST request on the root route (/), the application’s home page:

app.post('/', function (req, res) {
  res.send('Got a POST request')
})

实例:用户注册的接口

关于 code 0 1 的讨论

https://segmentfault.com/q/1010000015622870/a-1020000015622950

router.post('/register', function (req, res, next) {
  //1.获取参数
  //post请求参数放在req的body属性中
  //get请求,query参数和param参数,req.query || req.param
  const {username, password} = req.body
  //2.处理数据
  console.log('register', username, password)
  if (username === 'admin') {
    //3.返回响应数据(对象)
    res.send({code: 1, msg: '此用户已存在'})
  } else {
    res.send({code: 0, data: {_id: 'abc', username, password}})
  }
})

使用 Mongoose 操作数据库

下载依赖包

npm install --save mongoose blueimp-md5

db/db_test.js

链接数据库

//commonJS语法,因为mongoose
const md5 = require('blueimp-md5')
// 1. 连接数据库
// 1.1. 引入 mongoose
const mongoose = require('mongoose')
// 1.2. 连接指定数据库(URL 只有数据库是变化的)
mongoose.connect('mongodb://localhost:27017/gzhipin_test2')
// 1.3. 获取连接对象
const conn = mongoose.connection
// 1.4. 绑定连接完成的监听(用来提示连接成功)
conn.on('connected', function () {
  console.log('数据库连接成功')
})

右键 run 进行测试

或运行命令: node db/db_test.js

得到对应特定集合的 Model

用于操作集合的数据

定义 Schema,保存 Model 构造函数

向外暴露 Model

// 2. 得到对应特定集合的 Model
// 2.1. 字义 Schema(描述/定义文档结构)
//属性名、属性值的的类型、是否是必须的、默认值
const userSchema = mongoose.Schema({
  username: {type: String, required: true}, // 用户名
  password: {type: String, required: true}, // 密码
  type: {type: String, required: true}, // 用户类型: dashen/laoban
})
// 2.2. 定义 Model(与集合对应, 可以操作集合),返回一个构造函数
const UserModel = mongoose.model('user', userSchema)
// 之后设定集合名为: users,一一对应
// 2.3. 向外暴露 Model
exports.UserModel = UserModel

通过 Model 或者其实例进行 CRUD 的操作

C:SAVE

// 3.1. 通过 Model 实例的 save()添加数据
function testSave() {
// user 数据对象
  const user = {
    username: 'xfzhang',
    password: md5('1234'),
    type: 'dashen',
  }
  const userModel = new UserModel(user)
// 调用实例的save方法保存到数据库
  userModel.save(function (err, userDoc) {
    console.log('save', err, userDoc)
  })
}
//testSave()

R:Retrieve

// 3.2. 通过 Model 的 find()/findOne()查询多个或一个数据
function testFind() {
// 查找多个
  UserModel.find(function (err, users) { // 如果有匹配返回的是一个[user, user..], 如果
    //没有一个匹配的返回undefined
    console.log('find() ', err, users)
  })
// 查找一个
  UserModel.findOne({_id: '5ae1d0ab28bd750668b3402c'}, function (err, user) { // 如果
    //有匹配返回的是一个 user, 如果没有一个匹配的返回 null
    console.log('findOne() ', err, user)
  })
}
// testFind()

U:update

// 3.3. 通过 Model 的 findByIdAndUpdate()更新某个数据
function testUpdate() {
  UserModel.findByIdAndUpdate({_id: '5ae1241cf2dd541a8c59a981'}, {username: 'yyy'},
    function (err, user) {
      console.log('findByIdAndUpdate()', err, user)
    })
}
// testUpdate()

D:delete

// 3.4. 通过 Model 的 remove()删除匹配的数据
function testDelete() {
  UserModel.remove({_id: '5ae1241cf2dd541a8c59a981'}, function (err, result) {
    console.log('remove()', err, result)
  })
}
// testDelete()

Demo 用户注册/登录

数据库数据操作模块:db/models.js

包含 n 个能操作 mongodb 数据库集合的 model 的模块

连接数据库

  1. 引入 mongoose
  2. 连接指定数据库 (URL 只有数据库是变化的)
  3. 获取连接对象
  4. 绑定连接完成的监听 (用来提示连接成功)

定义出对应特定集合的 Model 并向外暴露

  1. 定义 Schema(描述文档结构)
  2. 定义 Model(与集合对应, 可以操作集合)
  3. 向外暴露 Model
/*1. 连接数据库*/
// 1.1. 引入 mongoose
const mongoose = require('mongoose')
// 1.2. 连接指定数据库(URL 只有数据库是变化的)
mongoose.connect('mongodb://localhost:27017/bossz')
// 1.3. 获取连接对象
const conn = mongoose.connection
// 1.4. 绑定连接完成的监听(用来提示连接成功)
conn.on('connected', function () {
  console.log('数据库连接成功!')
})
/*2. 定义出对应特定集合的 Model 并向外暴露*/
// 2.1. 字义 Schema(描述文档结构)
const userSchema = mongoose.Schema({
  username: {type: String, required: true}, // 用户名
  password: {type: String, required: true}, // 密码
  type: {type: String, required: true}, // 用户类型: dashen/laoban
  header: {type: String}, // 头像名称
  post: {type: String}, // 职位
  info: {type: String}, // 个人或职位简介
  company: {type: String}, // 公司名称
  salary: {type: String} // 工资
})
// 2.2. 定义 Model(与集合对应, 可以操作集合),集合自动命名为users
const UserModel = mongoose.model('user', userSchema)
// 2.3. 向外暴露 Model
exports.UserModel = UserModel

路由器模块: routes/index.js

// 引入 md5 加密函数库
const md5 = require('blueimp-md5')
// 引入 UserModel
const UserModel = require('../db/models').UserModel
const filter = {password: 0} // 查询时过滤出指定的属性

注册路由

// 注册路由
//post方式还是get方式,接受两个参数路径;回调函数
router.post('register',(req,res)=>{
// 回调函数内部:获取请求参数、处理、返回响应数据
  // 参数的名字依照文档
  const {username,password,type} = req.body
  //判断用户是否已经存在,根据(username)查找
  UserModel.findOne({username},(err,user)=>{
    //如果user有值,说明用户名已存在
    if(user){
      //返回错误信息:都是JSON对象
      res.send({code:1,msg:"用户名已存在"})
    }else{
      //保存用户信息
      // 先对密码进行加密
      new UserModel({username,type,password:md5(password)}).save((err,user)=>{
        //返回包含user的JSON数据,但是不能返回密码
        const data = {username,type,_id:user._id}
        //用户注册成功,帮他登陆,并且标识用户已经登陆
        //方法一:cookie
        //生成一个 cookie(userid: user._id), 并交给浏览器保存
        res.cookie('userid', user._id, {maxAge: 1000*60*60*24*7})
        //前者是用户的user._id,后两者指定持久化 cookie, 浏览器会保存在本地文件
        //方法二:session,参考vue项目
        res.send({code:1,data:data})
      })
    }
  })
})

登陆路由

//登陆路由
router.post('/login',(req,res)=>{
  const{username,password}= req.body
  //根据username和password查询数据库users
  UserModel.findOne({username,password:md5(password)},{password:0},function (err, user) {
    if(user){
      //user存在,登陆成功
      res.cookie('userid', user._id, {maxAge: 1000*60*60*24*7})
      res.send({code:0,data:user})
    } else {
      //user不存在,登陆失败
      res.send({code:1,msg:"用户名或密码不正确"})
    }
  })
})

注意

HTTP 响应控制

配置跨域

控制缓存

Cookie 和 Session

NodeJS 中

Express

Express 中的 Session

在内存中存储 Session

在 Redis 中存储 Session