nodejs项目实战教程15—Express

nodejs项目实战教程15—Express

1. Express操作路由(get、post、put、delete、多级目录、动态路由、get传值)

1.1 安装Express

npm install express --save

或者

cnpm i express --save

详细文档请前往 https://www.npmjs.com/package/express

在这里插入图片描述
创建express文件夹,npm init --yse生成package.json文件,然后安装Express,创建app.js文件。

1.2 get用于显示数据

const express = require('express')
const app = express()

// get用户显示数据
app.get('/', (req, res) => {
  res.send('Hello Express')
})
app.get('/register', (req, res) => {
  res.send('注册登录页面')
})
app.get('/article', (req, res) => {
  res.send('新闻页面')
})
app.listen(3000)

在这里插入图片描述
在这里插入图片描述

1.3 post用于增加数据

app.post('/doLogin', (req, res) => {
  console.log('执行登录')
  res.send('执行登录')
})

post请求在浏览器中无法通过地址直接请求,可以通过postman请求查看
在这里插入图片描述

1.4 put主要用于修改数据

app.put('/editUser', (req, res) => {
  console.log('修改用户')
  res.send('修改用户')
})

在这里插入图片描述

1.5 delete主要用于删除数据

app.delete('/deleteUser', (req, res) => {
  console.log('执行删除')
  res.send('执行删除')
})

在这里插入图片描述

1.6 路由配置多级目录

app.get('/admin/user/add', (req, res) => {
  res.send('admin user add')
})

app.get('/admin/user/edit', (req, res) => {
  res.send('admin user edit')
})

在这里插入图片描述

1.7 动态路由

// 动态路由 
app.get('/article/:id', (req, res) => {
  let id = req.params['id']
  res.send('动态路由' + id)
})

在这里插入图片描述
注意:配置路由的时候要注意顺序,动态路由如果放在前面就会优先执行,比如/article/add想生效,就需要放在/article/:id之前

app.get('/article/add', (req, res) => {
  res.send('article add')
})
app.get('/article/:id', (req, res) => {
  let id = req.params['id']
  res.send('动态路由' + id)
})

在这里插入图片描述

app.get('/article/:id', (req, res) => {
  let id = req.params['id']
  res.send('动态路由' + id)
})

app.get('/article/add', (req, res) => {
  res.send('article add')
})

在这里插入图片描述

1.8 获取get传值

app.get('/product', (req, res) => {
  let query = req.query
  console.log(query)
  res.send('product-' + query.id)
})

在这里插入图片描述
在这里插入图片描述

2. Express 使用ejs

2.1 安装ejs

npm install ejs --save

或者

cnpm i ejs --save

ejs使用教程详情查看https://www.npmjs.com/package/ejs

创建项目express02,npm init --yse生成package.json,cnpm i express --save下载express,cnpm i ejs --save下载ejs,创建app.js文件

2.2 使用ejs

(1)配置模板引擎

app.set('view engine', 'ejs')

(2)使用(默认加载模板引擎的文件夹是views)

 res.render('index',{
 
 })

(3)实例,app.js写入:

/**
 * 1、安装 cnpm i ejs --save
 *
 * 2、app.set('view engine','ejs')
 *
 * 3、使用(默认加载模板引擎的文件夹是views)
 * res.render('index',{
 *
 * })
 */
const express = require('express')
const app = express()
// 配置模板引擎
app.set('view engine', 'ejs')

app.get('/', (req, res) => {
  res.render('index', {
    title: '123',
  })
  // res.send('Hello Express')
})

app.listen(3000)

以上代码表示端口3000的主页默认读取views/index.ejs的文件,并传递参数title。接着创建views文件夹
在这里插入图片描述
index.ejs代码如下:

<!DOCTYPE html>
<html lang="en">
  <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>Document</title>
  </head>

  <body>
    <h2>我是一个ejs模板引擎</h2>
    <p><%=title%></p>
  </body>
</html>

运行node app.js
在这里插入图片描述

2.3 在ejs中使用javascript

app.js:

/**
 * 1、安装 cnpm i ejs --save
 *
 * 2、app.set('view engine','ejs')
 *
 * 3、使用(默认加载模板引擎的文件夹是views)
 * res.render('index',{
 *
 * })
 */
const express = require('express')
const app = express()
// 配置模板引擎
app.set('view engine', 'ejs')

app.get('/', (req, res) => {
  res.render('index', {
    title: '123',
  })
  // res.send('Hello Express')
})

app.get('/news', (req, res) => {
  let userInfo = {
    username: '常山赵子龙',
    age: 30,
  }

  let article = '<h3>我是一个h3标题<h3>'

  let list = [1111, 2222, 3333]
  res.render('news', {
    userInfo: userInfo,
    article: article,
    flag: true,
    score: 60,
    list: list,
  })
})

app.listen(3000)

views/news.ejs:

<!DOCTYPE html>
<html lang="en">
  <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>Document</title>
  </head>

  <body>
    <h2>新闻模板页面</h2>
    <p><%=userInfo.username%>--<%=userInfo.age%></p>
    <p><%-article%></p>

    <%if(flag===true){%>
    <strong>flag === true</strong>
    <%}%> <%if(score>=60){%>
    <p>及格</p>
    <%}else{%>
    <p>不及格</p>
    <%}%>

    <h2>循环遍历</h2>
    <ul>
      <%for(let i = 0;i < list.length;i++){%>
      <li><%=list[i]%></li>
      <%}%>
    </ul>
  </body>
</html>

运行node app.js
在这里插入图片描述
总结下语法:
(1)直接使用变量,<%=变量%>
(2)使用javascript时,条件语句需要<%{%>和<%}%>配合使用

2.4 在ejs中引用其他ejs文件作为公共模块

views/footer.ejs:

<footer>
  <h1>公共的底部</h1>
</footer>

在views/index.ejs中引用:

<!DOCTYPE html>
<html lang="en">
  <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>Document</title>
  </head>

  <body>
    <h2>我是一个ejs模板引擎</h2>
    <p><%=title%></p>

    <%- include('footer.ejs')%>
  </body>
</html>

在这里插入图片描述
views/news.ejs中同理:
在这里插入图片描述

2.5 修改ejs为html

很多时候,处于习惯,我们希望将ejs修改为html文件,ejs支持这样的配置。
关键语句:

const ejs = require('ejs')
app.engine('html', ejs.__express)
app.set('view engine', 'html')

app.js:

const express = require('express')
const ejs = require('ejs')
const app = express()
// 配置模板引擎为html
app.engine('html', ejs.__express)
app.set('view engine', 'html')

// 配置静态资源目录
app.use(express.static('static'))

app.get('/', (req, res) => {
  res.render('index', {
    title: '123',
  })
  // res.send('Hello Express')
})

app.get('/news', (req, res) => {
  let userInfo = {
    username: '常山赵子龙',
    age: 30,
  }

  let article = '<h3>我是一个h3标题<h3>'

  let list = [1111, 2222, 3333]
  res.render('news', {
    userInfo: userInfo,
    article: article,
    flag: true,
    score: 60,
    list: list,
  })
})

app.listen(3000)

将原先的ejs文件修改为html文件,并在引用的文件处修改后缀即可
在这里插入图片描述

2.6 静态资源配置(静态文件托管)

app.js中配置静态资源文件为static:

// 配置静态资源目录
app.use(express.static('static'))

在static下创建css/base.css文件:

h1 {
  background-color: red;
}

在需要的html中引入
在这里插入图片描述
在这里插入图片描述

3. 中间件

3.1 什么是中间件

中间件就是匹配路由之前或者匹配路由完成做的一系列的操作。中间件中如果想往下 匹配的话,那么需要写 next()。

3.2 中间件的功能

(1)执行任何代码。
(2)修改请求和响应对象。
(3)终结请求-响应循环。
(4)调用堆栈中的下一个中间件。

3.3 Express中间件的种类

3.3.1 应用级中间件(通常用于权限判断)

app.use((req, res, next) => {
  console.log(new Date())
  next()
})

3.3.2 路由级中间件(用的比较少)

app.get('/news/add', (req, res, next) => {
  // res.send('执行增加新闻')
  console.log('执行增加新闻')
  next()
})
app.get('/news/:id', (req, res) => {
  res.send('新闻动态路由')
})

执行next()会继续向下匹配

3.3.3 错误处理中间件

app.use((req, res, next) => {
  res.status(404).send('404')
})

匹配路由后如果都没有的话,页面就返回404

3.3.4 内置中间件

app.use(express.static('static'))

其实就是配置静态路由,配置完成后就可以展示static文件夹下的文件

3.3.5 第三方中间件

通常用户获取post请求提交的数据,需要借助body-parser插件,详细文档可以查看https://www.npmjs.com/package/body-parser
获取post传过来的数据
1、cnpm install body-parser --save
2、const bodyParser = require(‘body-parser’)
3、配置中间件
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
4、接收post数据
在这里插入图片描述
app.js:

/**
 * 获取post传过来的数据
 * 1、cnpm install body-parser --save
 * 2、const bodyParser = require('body-parser')
 * 3、配置中间件
 *  app.use(bodyParser.urlencoded({ extended: false }))
 *  app.use(bodyParser.json())
 * 4、接收post数据
 */

const express = require('express')
const ejs = require('ejs')
const bodyParser = require('body-parser')
const app = express()

app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
// 配置模板引擎为html
app.engine('html', ejs.__express)
app.set('view engine', 'html')

// 配置静态资源目录
app.use(express.static('static'))

app.get('/', (req, res) => {
  res.send('首页')
})

app.get('/login', (req, res) => {
  // req.query 获取get
  res.render('login', {})
})

app.post('/doLogin', (req, res) => {
  let body = req.body
  console.log(body)
  res.send('执行提交' + body.username)
})

app.listen(3000)

views/login.html:

<!DOCTYPE html>
<html lang="en">
  <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>Document</title>
  </head>

  <body>
    <h2>post提交数据</h2>

    <form action="/doLogin" method="post">
      用户名:<input type="text" name="username" /><br />
      密码:<input type="password" name="password" /><br />

      <input type="submit" value="提交" />
    </form>
  </body>
</html>

在这里插入图片描述
点击提交
在这里插入图片描述

4. Cookie

4.1 Cookie简介

(1)cookie是存储在访问者电脑中的变量,可以让我们在使用同一个浏览器访问同一个域名的时候共享数据。
(2)http是无状态协议。简单来说,就是你浏览了一个页面,之后跳转到相同网站的另一个页面时,服务器无法认识到这是同一个浏览器在访问同一个网站。就是说,每次访问都是没有关系的。
(3)cookie就是一个很简单的想法:当访问一个页面时,服务器在下行http报文中,命令浏览器存储一个字符串;浏览器再访问同一个域时,将把这个字符串携带到上行的http请求中(换句话说,就是在res中存储)。因此第一次访问服务器,不可能携带cookie,必须是服务器得到这次请求,才能在下次请求中携带cookie信息(换句话说,就是在后面的req中获取)。此后每一次浏览器往这个服务器发出请求,都会携带这个cookie,直至失效。

4.2 Cookie特点

(1)保存在浏览器本地
(2)默认cookie不加密,所有用户都可以看到
(3)用户可以删除、禁用、篡改、加密
(4)cookie可用于攻击
(5)cookie存储量很小,未来会被localStorage替代 。

4.3 Cookie使用方式

4.3.1 基础使用方式

在Express中使用cookie的话,要借助cookie-parser中间件,详情可以查看https://www.npmjs.com/package/cookie-parser
(1)安装

npm install cookie-parser --save

或者

cnpm i cookie-parser --save

(2)引入和设置中间件(注意设置中间件要在app申明后使用)

const cookieParser = require('cookie-parser');

app.use(cookieParser());

(3) 设置cookie

res.cookie("username",'zhangsan',{maxAge: 1000*60*60});

(4)获取cookie

req.cookies.username

(5)删除cookie

res.cookie('rememberme', '', { expires: new Date(0)}); 
res.cookie('username','zhangsan',{domain:'.ccc.com',maxAge:0,httpOnly:true});

实例:

const express = require('express')
const cookieParser = require('cookie-parser')
const app = express()
app.use(cookieParser())

app.get('/', (req, res) => {
  res.cookie('username', 'zhangsan', {
    maxAge: 1000 * 60 * 60,
  })
  res.send('Hello World!')
})

app.get('/article', (req, res) => {
  let username = req.cookies.username
  console.log(username)
  res.send('新闻页面--' + username)
})

app.get('/user', (req, res) => {
  let username = req.cookies.username
  console.log(username)
  res.send('用户页面--' + username)
})
app.listen(3000)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.3.2 cookie属性说明

res.cookie('username', 'zhangsan', {
    maxAge: 1000 * 60 * 60,
  })

函数中的第三个选项就是cookie的属性,总共有9个可选参数,如下:

在这里插入图片描述
(1)maxAge:设置多少毫秒后失效。经常使用。
(2)signed:设置是否加密(或者说表示是否是签名cookie),设置为true时就需要使用res.signedCookies而不是res.cookies访问。被篡改的签名cookie会被服务器拒绝,并且重置为初始值。经常使用。
(3)expires:过期时间(秒),设置在某个具体的时间点后失效,如 expires=Wednesday, 09-Nov-99 23:12:40 GMT。但是一般不用这个属性。
(4)httpOnly:如果设置为true,则无法通过JavaScript读取,本意是为了防止XSS攻击。默认不做设置。
(5)path:表示cookie影响的路径,如果路径不匹配,则浏览器不发送这个cookie。
(6)domain:设置共享cookie的二级域名。经常使用。
(7)secure:当设置为true时,cookie在https请求中才有效。
encode和sameSite不重要,不用记。

在这里插入图片描述
hosts文件可以用来修改电脑访问的域名地址

4.3.3 加密cookie

在实际的项目中,我们需要对cookie进行加密,否则任何人都可以直接修改cookie。加密cookie的使用方式如下:
(1)配置中间件的时候需要传递密钥,可以随便写

const cookieParser = require('cookie-parser')
app.use(cookieParser('sheldon'))

(2)设置cookie时配置signed属性

app.get('/', (req, res) => {
  res.cookie('username', 'zhangsan', {
    maxAge: 1000 * 60 * 60,
    // path: '/article',
    // domain: '.sheldon.com',
    signed: true,
  })
  res.send('Hello World!')
})

(3)获取cookie使用req.signedCookies

app.get('/product', (req, res) => {
  // 获取加密后的cookie
  let username = req.signedCookies.username
  console.log(username)
  res.send('product--' + username)
})

完整实例:

const express = require('express')
const cookieParser = require('cookie-parser')
const app = express()
// 设置加密的密钥为sheldon,可以随意替换成别的
app.use(cookieParser('sheldon'))

app.get('/', (req, res) => {
  res.cookie('username', 'zhangsan', {
    maxAge: 1000 * 60 * 60,
    // path: '/article',
    // domain: '.sheldon.com',
    signed: true,
  })
  res.send('Hello World!')
})

// app.get('/article', (req, res) => {
//   let username = req.cookies.username
//   console.log(username)
//   res.send('新闻页面--' + username)
// })

// app.get('/user', (req, res) => {
//   let username = req.cookies.username
//   console.log(username)
//   res.send('用户页面--' + username)
// })
app.get('/product', (req, res) => {
  // 获取加密后的cookie
  let username = req.signedCookies.username
  console.log(username)
  res.send('product--' + username)
})
app.listen(3000)

在这里插入图片描述
在这里插入图片描述
如果直接修改cookie的值,刷新页面后将无法识别,取值可能为undefined或者false。
在这里插入图片描述
在这里插入图片描述

5. Session

5.1 session简介

session也是一种记录客户状态的方式,不同于cookie,session将将信息保存在服务器中,所以当访问量较多时,就会比较占用服务器的性能。单个cookie保存数据不能超过4k,很多浏览器最多只能保存大约20个cookie。但是session没有这方面的限制。

5.2 session的运作原理

session虽然不同于cookie,但是却是基于cookie运作的。当浏览器访问服务器并发送第一次请求时,服务端会创建一个session对象,生成一个类似于key——value的键值对,然后将cookie(key)返回到客户端。浏览器下次访问时,就可以携带cookie(key)找到对应的session(value)。

5.3 使用方式

更多可以查看https://www.npmjs.com/package/express-session

5.3.1 基础使用方式

(1)下载express-session

npm install express-session --save

或者

cnpm i express-session --save

(2)引入

const session = require('express-session')

(3)设置中间件

app.use(
  session({
    // 服务器端生成session的签名,随便写
    secret: 'keyboard cat',
    // 强制保存session,即使session没有变化
    resave: false,
    // 强制将未初始化的session存储
    saveUninitialized: true,
    cookie: {
      maxAge: 1000 * 60,
      secure: false,
    },
  })
)

(4)设置session

app.get('/login', (req, res) => {
  // 设置session
  req.session.username = '张三'
  res.send('登录')
})

(5)获取session

app.get('/', (req, res) => {
  // 获取session
  if (req.session.username) {
    res.send(req.session.username + '已登录')
  } else {
    res.send('未登录')
  }
})

完整实例:

const express = require('express')
const session = require('express-session')
const app = express()

// 配置session的中间件
app.use(
  session({
    // 服务器端生成session的签名,随便写
    secret: 'keyboard cat',
    // 强制保存session,即使session没有变化
    resave: false,
    // 强制将未初始化的session存储
    saveUninitialized: true,
    cookie: {
      maxAge: 1000 * 60,
      secure: false,
    },
  })
)

app.get('/', (req, res) => {
  // 获取session
  if (req.session.username) {
    res.send(req.session.username + '已登录')
  } else {
    res.send('未登录')
  }
})
app.get('/login', (req, res) => {
  // 设置session
  req.session.username = '张三'
  res.send('登录')
})

app.listen(80)

直接访问80端口
在这里插入图片描述
进入登录页面后,session设置完成
在这里插入图片描述
再次访问80,获取到session中的数据
在这里插入图片描述

5.3.2 session的参数

app.use(
  session({
    // 服务器端生成session的签名,随便写
    secret: 'keyboard cat',
    // 修改session对应的cookie名称,默认为connect.sid
    name: 'sheldon',
    // 强制保存session,即使session没有变化
    resave: false,
    // 强制将未初始化的session存储
    saveUninitialized: true,
    cookie: {
      maxAge: 1000 * 60,
      secure: false,
    },
    // 每次请求时强制重置cookie的过期时间
    rolling: true,
  })
)

(1)secret:一个 String 类型的字符串,作为服务器端生成 session 的签名。
(2)name:返回客户端的 key 的名称,默认为 connect.sid,也可以自己设置。
(3)resave:强制保存 session 即使它并没有变化,。默认为 false。 don’t save session if unmodified。
(4)saveUninitialized:强制将未初始化的 session 存储。当新建了一个 session 且未设定属性或值时,它就 处于未初始化状态。在设定一个 cookie 前,这对于登陆验证,减轻服务端存储压力,权 限控制是有帮助的。(默认:true)。建议手动添加。
(5)cookie:设置返回到前端 key 的属性。
(6)rolling:在每次请求时强行设置 cookie,这将重置 cookie 过期时间(默认:false)。

5.3.3 删除session

方法一:设置session的过期时间为0(会把所有的session都销毁)

req.session.cookie.maxAge=0

方法二:销毁指定session

req.session.username = ''

方法三:destroy方法,也会删除所有session,并且自带回调函数

req.session.destroy((err) => {})

完整示例:

const express = require('express')
const session = require('express-session')
const app = express()

// 配置session的中间件
app.use(
  session({
    // 服务器端生成session的签名,随便写
    secret: 'keyboard cat',
    // 修改session对应的cookie名称,默认为connect.sid
    name: 'sheldon',
    // 强制保存session,即使session没有变化
    resave: false,
    // 强制将未初始化的session存储
    saveUninitialized: true,
    cookie: {
      maxAge: 1000 * 60,
      secure: false,
    },
    // 每次请求时强制重置cookie的过期时间
    rolling: true,
  })
)

app.get('/', (req, res) => {
  // 获取session
  if (req.session.username || req.session.age) {
    res.send(req.session.username + '--' + req.session.age + '--已登录')
  } else {
    res.send('未登录')
  }
})
app.get('/login', (req, res) => {
  // 设置session
  req.session.username = '张三'
  req.session.age = 20
  res.send('执行登录')
})
// 销毁session
app.get('/loginOut', (req, res) => {
  // 方法一:设置session的过期时间为0(会把所有的session都销毁)
  // req.session.cookie.maxAge=0

  // 方法二:销毁指定session
  // req.session.username = ''

  // 方法三:
  req.session.destroy((err) => {})

  res.send('退出登录1')
})

app.listen(80)

6. 多服务负载均衡,session保存到数据库

6.1 为什么需要将session保存到数据库中

在这里插入图片描述
如上图所示,在实际项目中,我们大多会采用nginx,多服务负载均衡的方式减轻服务器压力,但是会有一个问题,就是有可能我们之前是在杭州的服务器设置了session,但是下次访问相同网址时如果时通过其他地方(比如深圳)的服务器就无法获取到之前在杭州服务器上设置的session。
所以在实际项目中,通常会将session存放在mongodb中,这样就可以多个服务器共同使用了。

6.2 使用方式

更多可以查看https://www.npmjs.com/package/connect-mongo
(1)下载connect-mongo

npm install connect-mongo --save

或者

cnpm i connect-mongo --save

以前的版本只需要下载这个,但是最近的版本可能因为某些原因缺包,需要手动下载依赖项mongo,否则会无法运行

cnpm i mongo --save

(2)引入模块

const MongoStore = require('connect-mongo')

(3)配置中间件

// 配置session的中间件
app.use(
  session({
    // 服务器端生成session的签名,随便写
    secret: 'keyboard cat',
    // 修改session对应的cookie名称,默认为connect.sid
    name: 'sheldon',
    // 强制保存session,即使session没有变化
    resave: false,
    // 强制将未初始化的session存储
    saveUninitialized: true,
    cookie: {
      maxAge: 1000 * 60,
      secure: false,
    },
    // 每次请求时强制重置cookie的过期时间
    rolling: true,
    store: MongoStore.create({
      // admin是账号,123456是密码,如果mongodb没有设置账号密码可以省略
      mongoUrl: 'mongodb://admin:123456@localhost/admin',
      // 无论发起都少次请求,24小时内只更新一次session,除非session发生了变化
      touchAfter: 24 * 3600,
    }),
  })
)

完整实例:

```javascript
const express = require('express')
const session = require('express-session')
const MongoStore = require('connect-mongo')
const app = express()

// 配置session的中间件
app.use(
  session({
    // 服务器端生成session的签名,随便写
    secret: 'keyboard cat',
    // 修改session对应的cookie名称,默认为connect.sid
    name: 'sheldon',
    // 强制保存session,即使session没有变化
    resave: false,
    // 强制将未初始化的session存储
    saveUninitialized: true,
    cookie: {
      maxAge: 1000 * 60,
      secure: false,
    },
    // 每次请求时强制重置cookie的过期时间
    rolling: true,
    store: MongoStore.create({
      // admin是账号,123456是密码,如果mongodb没有设置账号密码可以省略
      mongoUrl: 'mongodb://admin:123456@localhost/admin',
      // 无论发起都少次请求,24小时内只更新一次session,除非session发生了变化
      touchAfter: 24 * 3600,
    }),
  })
)

app.get('/', (req, res) => {
  // 获取session
  if (req.session.username || req.session.age) {
    res.send(req.session.username + '--' + req.session.age + '--已登录')
  } else {
    res.send('未登录')
  }
})
app.get('/login', (req, res) => {
  // 设置session
  req.session.username = '张三'
  req.session.age = 20
  res.send('执行登录')
})
// 销毁session
app.get('/loginOut', (req, res) => {
  // 方法一:设置session的过期时间为0(会把所有的session都销毁)
  // req.session.cookie.maxAge=0

  // 方法二:销毁指定session
  // req.session.username = ''

  // 方法三:
  req.session.destroy((err) => {})

  res.send('退出登录1')
})

app.listen(80)

执行nodemon app.js,打开cmd,查看对应的session表,此时还没有数据
在这里插入图片描述
访问80端口,会将未初始化的session强制存储
在这里插入图片描述
在这里插入图片描述
访问login页面,会刷新设置的session
在这里插入图片描述
在这里插入图片描述
此时再访问80端口,就可以获取到设置的session数据了。
在这里插入图片描述

7. 路由模块化

之前的项目中,我们都把路由放到app.js文件中,但是在实际的大型项目开发中,多人开发,因此需要将功能相似的路由抽离出去,实现模块化。

7.1 准备模块代码

(1)前期准备,将express05中的代码复制到新建的express09中
在这里插入图片描述
views/login.html:

<!DOCTYPE html>
<html lang="en">
  <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>Document</title>
  </head>

  <body>
    <h2>post提交数据</h2>

    <form action="/doLogin" method="post">
      用户名:<input type="text" name="username" /><br />
      密码:<input type="password" name="password" /><br />

      <input type="submit" value="提交" />
    </form>
  </body>
</html>

app.js:

/**
 * 获取post传过来的数据
 * 1、cnpm install body-parser --save
 * 2、const bodyParser = require('body-parser')
 * 3、配置中间件
 *  app.use(bodyParser.urlencoded({ extended: false }))
 *  app.use(bodyParser.json())
 * 4、接收post数据
 */

const express = require('express')
const ejs = require('ejs')
const bodyParser = require('body-parser')
const app = express()

app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
// 配置模板引擎为html
app.engine('html', ejs.__express)
app.set('view engine', 'html')

// 配置静态资源目录
app.use(express.static('static'))

app.get('/', (req, res) => {
  res.send('首页')
})

app.get('/login', (req, res) => {
  // req.query 获取get
  res.render('login', {})
})

app.post('/doLogin', (req, res) => {
  let body = req.body
  console.log(body)
  res.send('执行提交' + body.username)
})

app.listen(3000)

(3)package.json:

{
  "name": "express05",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.1",
    "ejs": "^3.1.6",
    "express": "^4.17.2"
  }
}

7.2 实现模块化

7.2.1 单个模块示例

详细API可以查看https://expressjs.com/en/guide/routing.html,连接末尾有对应的示例。
在这里插入图片描述
(1)新建routes文件夹,用于存放不同模块的路由文件。创建login.js,用于登录有关的路由。
在这里插入图片描述
routes/login.js:

const express = require('express')
const router = express.Router()

router.get('/', (req, res) => {
  // req.query 获取get
  res.render('login', {})
})

router.post('/doLogin', (req, res) => {
  let body = req.body
  console.log(body)
  res.send('执行提交' + body.username)
})

module.exports = router

(2)在app.js中引入并挂载login模块
在这里插入图片描述
(3)修改原来的提交路由
在这里插入图片描述
执行nodemon app.js,打开浏览器:
在这里插入图片描述
输入用户名、密码后,点击提交:
在这里插入图片描述

7.2.2 多模块多级路由模块化

(1)创建路由模块化文件夹routes,完成一级路由模块化
在这里插入图片描述
在这里插入图片描述
其中admin.js作为二级路由示例,图中标注代码引入二级路由模块,可以先注释,稍后在(2)中进行说明。
admin.js:

const express = require('express')
const router = express.Router()

// 引入外部模块
const login = require('../admin/login')
const nav = require('../admin/nav')
const user = require('../admin/user')

router.get('/', (req, res) => {
  res.send('后台管理中心')
})

// 配置外部路由模块
router.use('/login', login)
router.use('/nav', nav)
router.use('/user', user)

module.exports = router

api.js:

const express = require('express')
const router = express.Router()

router.get('/', (req, res) => {
  res.send('api接口')
})

module.exports = router

index.js:

const express = require('express')
const router = express.Router()

router.get('/', (req, res) => {
  res.send('首页')
})

module.exports = router

以上三个模块路由在app.js中引入使用:
在这里插入图片描述
执行nodemon app.js
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(2)创建admin文件夹,作为admin模块下二级模块路由文件夹,完成对应的二级路由模块化
在这里插入图片描述
login.js:

const express = require('express')
const router = express.Router()

router.get('/', (req, res) => {
  res.send('用户登录页面')
})
router.post('/doLogin', (req, res) => {
  res.send('执行登录')
})

module.exports = router

nav.js:

const express = require('express')
const router = express.Router()

router.get('/', (req, res) => {
  res.send('导航列表')
})
router.get('/add', (req, res) => {
  res.send('增加导航')
})
router.get('/edit', (req, res) => {
  res.send('修改导航')
})
router.post('/doAdd', (req, res) => {
  res.send('执行增加')
})
router.post('/doEdit', (req, res) => {
  res.send('执行修改')
})

module.exports = router

user.js:

const express = require('express')
const router = express.Router()

router.get('/', (req, res) => {
  res.send('用户列表')
})
router.get('/add', (req, res) => {
  res.send('增加用户')
})
router.get('/edit', (req, res) => {
  res.send('修改用户')
})
router.post('/doAdd', (req, res) => {
  res.send('执行增加')
})
router.post('/doEdit', (req, res) => {
  res.send('执行修改')
})

module.exports = router

再返回admin.js,引入和配置以上模块:
在这里插入图片描述
保存后执行nodemon app.js
二级路由页面:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
三级路由的post请求(postman展示)和get页面(浏览器展示):
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
user模块三级路由类似。

7.3 Express应用程序生成器

通过应用生成器工具 express-generator 可以快速创建一个应用的骨架,其实就是一个脚手架工具。
Express应用程序生成器中文网地址:https://www.expressjs.com.cn/starter/generator.html
使用方式:
(1)全局下载:

npx express-generator

或者

cnpm install -g express-generator

(2)创建项目骨架:

express --view=ejs express10

生成项目express10

在这里插入图片描述
生成了一个express项目,并且已经配置好了路由提供修改。
app.js:

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

routes/index.js:

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;

routes/users.js:

var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send('respond with a resource');
});

module.exports = router;

可以看到app.js中引入了routes文件夹中的index和users两个模块,但是app.js本身也作为一个模块输出,并且被bin/www文件所引用。所以,运行项目需要执行两个步骤。
第一步,下载依赖:

cnpm i

第二步,运行:

node bin/www

或者

nodemon app.js

注意:有时会因为nodemon版本过低而导致无法运行express-generator生成的项目,此时只需要重新全局下载下载更新下nodemon依赖即可。

cnpm i -g nodemon

在这里插入图片描述
在这里插入图片描述

8 结合multer上传文件和模块化封装

8.1 回顾如何获取post请求传来的数据

(1)复制项目demo09,重命名为demo11,删除node_modules重新下载。
(2)创建views/admin/nav/add.html:

<!DOCTYPE html>
<html lang="en">
  <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="/admin/nav/doAdd" method="post">
      标题:<input type="text" name="title" id="title" />
      <br />
      <br />
      <input type="file" name="pic" id="pic" />
      <br />
      <br />
      描述:<textarea name="desc" id="desc" cols="30" rows="10"></textarea>
      <br />
      <br />
      <input type="submit" value="提交" />
    </form>
  </body>
</html>


(3)添加路由和对应的doAdd请求,用于查看提交的数据
在这里插入图片描述

(3)打开对应路由,填写提交
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8.2 结合multer实现文件上传

以上可以获取到图片名称,但是不能真正实现文件的上传功能,这时候就需要multer模块实现文件上传,详情可查看https://www.npmjs.com/package/multer
(1)安装multer

cnpm i multer --save

(2)在form表单中加入enctype=“multipart/form-data”
在这里插入图片描述
(3)在对应的请求文件中配置multer
在这里插入图片描述
admin/nav.js:

const express = require('express')
const multer = require('multer')
const upload = multer({ dest: 'static/upload' }) // 上传之前目录必须存在

const router = express.Router()

router.get('/', (req, res) => {
  res.send('导航列表')
})
router.get('/add', (req, res) => {
  // res.send('增加导航')
  res.render('admin/nav/add')
})
router.get('/edit', (req, res) => {
  res.send('修改导航')
})

// 这里的pic是form表单对应的图片id
router.post('/doAdd', upload.single('pic'), (req, res) => {
  // 获取表单传过来的数据
  // let body = req.body
  // res.send(body)
  res.send({
    body: req.body,
    file: req.file,
  })
})
router.post('/doEdit', (req, res) => {
  res.send('执行修改')
})

module.exports = router

(3)上传文件
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到,文件虽然上传成功,但是上传的文件名称后缀名消失并且名称也发生了改变。明显不符合需求。
(4)自定义文件名
查看multer在npmjs.com上的对应说明
在这里插入图片描述
在这里插入图片描述
关键代码:

const path = require('path')

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    // 配置上传的目录
    cb(null, 'static/upload')
  },
  // 修改上传后的文件名
  filename: function (req, file, cb) {
    // 1.获取后缀名
    let extname = path.extname(file.originalname)
    // 根据时间戳生成文件名
    cb(null, Date.now() + extname)
  },
})

const upload = multer({ storage: storage })

运行:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(5)封装文件上传模块
创建model/tools.js,将上传文件的代码剪切到这里:

const multer = require('multer')
const path = require('path')

let tools = {
  multer() {
    const storage = multer.diskStorage({
      destination: function (req, file, cb) {
        // 配置上传的目录
        cb(null, 'static/upload')
      },
      // 修改上传后的文件名
      filename: function (req, file, cb) {
        // 1.获取后缀名
        let extname = path.extname(file.originalname)
        // 根据时间戳生成文件名
        cb(null, Date.now() + extname)
      },
    })

    const upload = multer({ storage: storage })

    return upload
  },
}

module.exports = tools

在admin/nav.js中引入tools模块,并进行使用
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8.3 按照日期生成上传文件目录

(1)安装 silly-datetime 依赖,用于生成生成当前日期字符串

cnpm i silly-datetime --save

(2)安装 mkdirp 依赖,用于生成文件夹

cnpm i mkdirp --save

(2)在model/tools.js中引入并使用

const multer = require('multer')
const path = require('path')
const sd = require('silly-datetime')
const mkdirp = require('mkdirp')

let tools = {
  multer() {
    const storage = multer.diskStorage({
      // 配置上传的目录
      destination: async (req, file, cb) => {
        // 1、获取当前日期
        let day = sd.format(new Date(), 'YYYYMMDD')
        let dir = path.join('static/upload', day)
        // 2、根据日期生成当前存储目录,mkdirp是一个异步方法,所以使用async和await
        await mkdirp(dir)
        cb(null, dir)
      },
      // 修改上传后的文件名
      filename: function (req, file, cb) {
        // 1.获取后缀名
        let extname = path.extname(file.originalname)
        // 2、根据时间戳生成文件名
        cb(null, Date.now() + extname)
      },
    })

    const upload = multer({ storage: storage })

    return upload
  },
}

module.exports = tools

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8.4 多文件上传

(1)复制admin/nav/add.html到admin/user,并且添加一个文件上传入口
在这里插入图片描述
(2)在admin/user.js中引入tools模块,并使用fields进行多文件上传。
在这里插入图片描述
上传函数可通过npm上的multer文档查看,单文件上传使用single,多文件上传且id相同时使用array,id不同时使用fields
在这里插入图片描述
运行:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Node.js项目实战开发是指利用Node.js技术栈进行实际的项目开发工作。通过使用Node.js及其相关工具和框架,开发人员可以快速构建高性能的Web应用、API接口、跨平台的桌面应用等。以下是一些常见的Node.js项目实战开发方法和步骤: 1. 安装Node.js和npm:首先需要在本地环境中安装Node.js和npm(Node Package Manager),这样才能使用Node.js的各种功能和工具。 2. 创建项目:使用命令行工具进入项目所在的文件夹,运行`npm init`命令来创建一个新的Node.js项目。这将生成一个`package.json`文件,用于管理项目的依赖和配置信息。 3. 安装依赖:根据项目需求,在`package.json`文件中添加所需的依赖项,并使用`npm install`命令安装这些依赖。 4. 使用Express框架:Express是一个流行的Node.js Web框架,可以简化项目的开发过程。通过使用Express,可以方便地创建路由、处理HTTP请求和响应、管理中间件等。可以使用`npm install express`命令安装Express,并在项目代码中引入和使用。 5. 使用其他框架和工具:除了Express,还可以根据项目需求选择和使用其他适合的框架和工具。例如,使用Electron框架可以构建跨平台的桌面应用,使用restify框架可以快速构建API接口,使用数据库框架可以实现数据的读写和操作等。 6. 开发和调试:在项目代码中编写业务逻辑和功能实现,并使用调试工具(如nodemon)对项目进行实时监测和自动重启。这样可以提高开发效率,节省手动重启项目的时间。 7. 测试和优化:在开发过程中,进行单元测试和集成测试,确保项目的功能和性能符合要求。根据测试结果进行性能调优和代码优化,提升项目的质量和效率。 8. 部署和发布:完成开发和测试后,将项目部署到服务器或云平台上,并配置好相关的环境和参数。根据项目需求,可以选择不同的部署方式,如使用Docker容器、服务器less架构等。 总结来说,Node.js项目实战开发需要掌握Node.js的基础知识和相关工具,选择适合的框架和工具,编写代码实现项目需求,并进行测试、优化和部署。通过不断的实践和学习,开发人员可以提升自己的Node.js项目实战能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sheldon一蓑烟雨任平生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值