Node — 第六天(前后端分离)及(身份验证)

综合应用服务端知识点搭建项目

下载安装所需的第三方模块

npm init -y
npm i express cors mysql
# express 用于搭建服务器
# cors 用于解决跨域
# mysql 用于操作数据库# 后面用到什么,再下载

创建app.js

之前,我们开启一个服务器,js文件名,一般都是 01-xxx.js 03-xxx.js

对于正常的一个项目来说,用于开启服务的js文件,一般都叫做 app.js

// 开启服务
const express = require('express');
const app = express();
app.listen(3006, () => console.log('server running~'));

使用路由模块精简项目结构

在这里插入图片描述

使用路由模块的原因(意义)

  • 试想,如果项目有100个路由(接口),全部放到app.js中,这样的代码不好维护,效率也比较低下
  • 所以需要按照路由(接口)功能的不同,分别使用小文件存放这些路由(接口),这些小文件就叫做路由模块
    • 比如,登录注册相关的,全部放到 ./routers/login.js
    • 比如,文章处理相关的,全部放到 ./routers/article.js
    • 比如,书籍管理相关的,全部放到 ./routers/books.js

代码如何实现

  • 路由文件中的代码
    • 加载express
    • 创建路由对象
    • 将路由(接口)挂载到路由对象上
    • 导出路由对象
/*** 1. 加载express* 2. 创建路由对象* 3. 把路由挂载到路由对象上* 4. 导出路由对象*/const express = require('express');
const router = express.Router();// 下面是图书管理相关的路由
router.get('/getbooks', (req, res) => {res.send('我是获取书籍的接口');
});router.post('/addbook', (req, res) => {});router.get('/delbook', (req, res) => {});module.exports = router;
  • app.js 中的代码
    • 加载路由模块,并注册成中间件。注册的时候,可以统一加前缀
// 开启服务
const express = require('express');
const app = express();
app.listen(3006, () => console.log('server running~'));// 加载路由模块,并注册成中间件
let books = require('./routers/books');
app.use('/api', books);

开启服务(nodemon app.js) ,然后通过浏览器来测试一下你的接口是否能够正常访问

创建数据库、数据表

数据库,还可以使用昨天的 user 数据库。

在里面创建一张数据表(books)

在这里插入图片描述

添加几条模拟数据:

在这里插入图片描述

粗略的完成获取书籍的接口

  • 目前:

    • 数据准备好了
    • 接口能够正常访问了
  • 下面要做的事情:

    • getbooks接口中,通过mysql模块,查询所有的书籍,并把结果响应给客户端
    • 中间件解决跨域问题

getbooks接口

router.get('/getbooks', async (req, res) => {// 查询数据库user、数据表books里面的数据,把查询的结果响应给客户端const mysql = require('mysql'); // require加载一个模块后,会缓存起来const conn = mysql.createConnection({host: 'localhost',user: 'root',password: '12345678',database: 'user' // 填写数据库名});conn.connect();conn.query('select * from books', (err, result) => {if (err) return console.log(err);res.json({status: 200,msg: '获取成功',data: result});});conn.end();
});

应用级别的中间件解决跨域

  • 应用级别的中间件
    • 写到app.js中的中间件
    • 该中间件会影响所有的路由
  • 路由级别的中间件
    • 写到 路由文件中的中间件
    • 该中间件只会影响当前的路由文件

因为整个项目的全部路由都需要解决跨域问题,所以需要定义应用级别的中间件

所以,在app.js中,加入如下代码:

// 配置中间件,解决跨域问题
app.use((req, res, next) => {res.set({'Access-Control-Allow-Origin': '*'});next();
});

封装db.js

试想,后面还有很多路由中需要对数据库进行操作,难道每次执行SQL语句,都要写5个步骤(操作MySQL的5个步骤)吗?

:肯定不是,那样的话,代码太多。代码复用性太差了

解决办法:封装

具体做法

  • 创建 db.js
  • 里面封装 使用mysql模块的5个步骤
  • 导出函数
  • 其他路由中,需要加载(导入)db模块,然后调用函数即可

db.js 中的代码:

function db(sql, params, callback) {const mysql = require('mysql');const conn = mysql.createConnection({host: 'localhost',user: 'root',password: '12345678',database: 'user' // 填写数据库名});conn.connect();conn.query(sql, params, callback);conn.end();
}// 导出函数
module.exports = db;

getbooks接口中使用:

// 获取书籍接口
router.get('/getbooks', async (req, res) => {// 查询数据库user、数据表books里面的数据,把查询的结果响应给客户端db('select * from books', null, (err, result) => {if (err) return console.log(err);res.json({status: 200,msg: '获取成功',data: result});});
});

使用Promise

如果查询书籍信息的时候,还要查询总记录数,怎么办?

:嵌套查询,嵌套的层数太多,就会形成回调地狱

解决办法:使用Promise

具体做法

  • 在db.js中,加个一个封装Promise的函数db2
  • db2中调用db函数,从而完成Promise的封装
  • 导出db2
function db(sql, params, callback) {const mysql = require('mysql');const conn = mysql.createConnection({host: 'localhost',user: 'root',password: '12345678',database: 'user' // 填写数据库名});conn.connect();conn.query(sql, params, callback);conn.end();
}function db2 (sql, params) {return new Promise((resolve, reject) => {// 这里写异步代码db(sql, params, (err, result) => {err ? reject(err) : resolve(result);});});
}// 导出函数
module.exports = db2;

优化上述两个函数

function db (sql, params = null) {const mysql = require('mysql'); // require加载一个模块后,会缓存起来const conn = mysql.createConnection({host: 'localhost',user: 'root',password: '12345678',database: 'user' // 填写数据库名});return new Promise((resolve, reject) => {// 这里写异步代码conn.connect();conn.query(sql, params, (err, result) => {err ? reject(err) : resolve(result);});conn.end();});
}// 导出函数
module.exports = db;

完整的图示

在这里插入图片描述

Web 开发模式

目前主流的 Web 开发模式有两种,分别是:

  1. 基于服务端渲染的传统 Web 开发模式
  2. 基于前后端分离的新型 Web 开发模式

服务端渲染的开发模式

特点:

  • 所有的web资源由同一个服务器统一管理(前后端代码必须放到一起)

  • 页面和页面中使用的数据,由服务器组装,最后将完整的HTML页面响应给客户端

在这里插入图片描述

代码:

// 用于开启服务const fs = require('fs');
const express = require('express');
const app = express();
app.listen(3000, () => console.log('启动了'));// 显示首页的接口
app.get('/index.html', (req, res) => {// res.send('1111')fs.readFile('./public/index.html', 'utf-8', (err, data) => {if (err) return console.log(err);data = data.replace('{{title}}', '悯农');data = data.replace('{{content}}', '锄禾日当午,汗滴禾下土');res.send(data);});
});

真实的服务端渲染模式和前后端分离的模式,难度上差不多。

优点:

  • **前端耗时少。**因为服务器端负责动态生成 HTML 内容,浏览器只需要直接渲染页面即可。尤其是移动端,更省电。
  • 有利于SEO。因为服务器端响应的是完整的 HTML 页面内容,所以爬虫更容易爬取获得信息,更有利于 SEO(搜索引擎)。

缺点:

  • **占用服务器端资源。**即服务器端完成 HTML 页面内容的拼接,如果请求较多,会对服务器造成一定的访问压力。

  • 不利于前后端分离,开发效率低。使用服务器端渲染,则无法进行分工合作,尤其对于前端复杂度高的项目,不利于 项目高效开发。

前后端分离的开发模式

特点:

  • 依赖于Ajax技术
  • 后端不提供完整的 HTML 页面内容,而 是提供一些 API 接口
  • 前端通过 Ajax 调用后端提供的 API 接口,拿到 json 数据 之后再在前端进行 HTML 页面的拼接,最终展示在浏览器上。

简而言之,前后端分离的 Web 开发模式,就是后端只负责提供 API 接口,前端使用 Ajax 调用接口的开发模式。

优点:

  • **开发体验好。**前端专注于 UI 页面的开发,后端专注于api 的开发,且前端有更多的选择性。
  • **用户体验好。**Ajax 技术的广泛应用,极大的提高了用户的体验,可以轻松实现页面的局部刷新。
  • **减轻了服务器端的渲染压力。**因为页面最终是在每个用户的浏览器中生成的。

缺点:

  • **不利于SEO。**因为完整的 HTML 页面需要在客户端动态拼接完成,所以爬虫对无法爬取页面的有效信息。(解决方 案:利用 Vue、React 等前端框架的 SSR (server side render)技术能够很好的解决 SEO 问题!)

如何选择 Web 开发模式

不谈业务场景而盲目选择使用何种开发模式都是耍流氓。

  • 比如企业级网站(公司的网站),主要功能是展示而没有复杂的交互,并且需要良好的 SEO,则这时我们就需要使用服务器端渲染;

  • 而类似后台管理页面,交互性比较强,不需要 SEO 的考虑,那么就可以使用前后端分离的开发模式。

  • 另外,具体使用何种开发模式并不是绝对的,为了同时兼顾首页的渲染速度前后端分离的开发效率,一些网站采用了 首屏服务器端渲染,即对于用户最开始打开的那个页面采用的是服务器端渲染,而其他的页面采用前后端分离开发模式。

身份认证机制

对于服务端渲染和前后端分离这两种开发模式来说,分别有着不同的身份认证方案:

  • 服务端渲染推荐使用 Session 认证机制(Session也会用到Cookie)
  • 前后端分离推荐使用 JWT 认证机制

Cookie

原理

在这里插入图片描述

实现身份认证

  • 搭建基础的服务器
    • 下载安装第三方模块 expresscookie-parser

    • 在这里插入图片描述

    • 创建app.js

    • 加载所需模块

      • const express = require('express');
      • const cookieParser = require('cookie-parser');
  • 中间件配置 cookie-parser
    • app.use(cookieParser())
  • 实现三个路由
    • /login.html (里面直接响应login.html页面)
    • /api/login
    • /index.html (里面直接响应index.html页面)
  • 创建存放index页面的public文件夹
    • 创建index.html
    • 创建login.html
  • 完成登录接口
    • 如果登录成功,设置cookie。res.cookie('key', 'value', 配置项);
    • 跳转到 /index.html 路由
  • /index.html 路由中,根据cookie判断是否登录,从而完成身份认证
    在这里插入图片描述

详见代码

const express = require('express');
const cookieParser = require('cookie-parser');
const path = require('path');const app = express();
app.listen(3000, () => console.log('启动了'));// 接收POST请求体
app.use(express.urlencoded({extended: false}));
// 配置cookie-parser
app.use(cookieParser());// 准备三个路由// 用于显示登录页面
app.get('/login.html', (req, res) => {// sendFile方法,可以读取文件,并将读取的结果响应给客户端// 要求,参数必须是一个绝对路径res.sendFile(path.join(__dirname, 'public', 'login.html'));
});// 用于完成登录验证的(判断账号密码是否正确的接口)
app.post('/api/login', (req, res) => {// console.log(req.body);// 约定,假设账号是 admin、密码是123if (req.body.username === 'admin' && req.body.password === '123') {// 登录成功,跳转到index.html// 设置cookie// res.cookie('key', 'value', '选项');// res.cookie('isLogin', 1); // 没有填选项,默认cookie有效期是会话结束res.cookie('isLogin', 1, {maxAge: 2*60*1000});res.send('<script>alert("登录成功"); location.href="/index.html";</script>');} else {// 登录失败}
});// 显示index.html页面的
app.get('/index.html', (req, res) => {// 获取cookie// console.log(req.cookies);if (req.cookies.isLogin && req.cookies.isLogin === '1') {res.sendFile(path.join(__dirname, 'public', 'index.html'));} else {// 没有登录res.send('<script>alert("请先登录"); location.href="/login.html";</script>');}
});

优缺点

  • 优点
    • 体积小
    • 客户端存放,不占用服务器空间
    • 浏览器会自动携带,不需要写额外的代码,比较方便
  • 缺点
    • 客户端保存,安全性较低。但可以存放加密的字符串来解决
    • 可以实现跨域,但是难度大,难理解,代码难度高
    • 不适合前后端分离式的开发

适用场景

  • 传统的服务器渲染模式
  • 存储安全性较低的数据,比如视频播放位置等

Session

原理

在这里插入图片描述

实现身份认证

  • 搭建基础的服务器

    • 下载安装第三方模块 expressexpress-session

    • 在这里插入图片描述

    • 创建app.js

    • 加载所需模块

      • const express = require('express');
      • const session = require('express-session');
  • 中间件配置 session

    app.use(session({secret: 'adfasdf', // 这个随便写saveUninitialized: false,resave: false
    }))
    
  • 实现三个路由

    • /login.html (里面直接响应login.html页面)
    • /api/login
    • /index.html (里面直接响应index.html页面)
  • 创建存放index页面的public文件夹

    • 创建index.html
    • 创建login.html
      在这里插入图片描述
  • 完成登录接口

    • 如果登录成功,使用session记录用户信息。

      req.session.isLogin = 1;
      req.session.username = req.body.username;
      
    • 跳转到 /index.html 路由

  • /index.html 路由中,根据session判断是否登录,从而完成身份认证

详见代码

const express = require('express');
const session = require('express-session');
const path = require('path');const app = express();
app.listen(3000, () => console.log('启动了'));// 接收POST请求体
app.use(express.urlencoded({extended: false}));
// 配置session
app.use(session({secret: 'asdf23sfsd23',// 下面两项,设置成true或者false,都可以。使用内存存储session的时候,下面两项没作用saveUninitialized: false,resave: false
}));// 准备三个路由// 用于显示登录页面
app.get('/login.html', (req, res) => {// sendFile方法,可以读取文件,并将读取的结果响应给客户端// 要求,参数必须是一个绝对路径res.sendFile(path.join(__dirname, 'public', 'login.html'));
});// 用于完成登录验证的(判断账号密码是否正确的接口)
app.post('/api/login', (req, res) => {// console.log(req.body);// 假设账号任意,密码必须是123if (req.body.password === '123') {// 假设登录成功// req.session.xxxx = 'yyyy'req.session.isLogin = 1;
/*<p>欢迎你:{{username}}</p>
*/req.session.username = req.body.username;// 做出响应res.send('<script>alert("登录成功"); location.href="/index.html";</script>');}
});// 显示index.html页面的
app.get('/index.html', (req, res) => {// 获取session    req.sessionif (req.session.isLogin && req.session.isLogin == 1) {const fs = require('fs');fs.readFile('./public/index.html', 'utf-8', (err, data) => {if (err) return console.log(err);// 更换用户名data = data.replace('{{username}}', req.session.username);res.send(data);});} else {res.send('<script>alert("请登录"); location.href="/login.html";</script>');}});

优缺点

  • 优点
    • 服务端存放,安全性较高
    • 浏览器会自动携带cookie,不需要写额外的代码,比较方便
    • 适合服务器端渲染模式
  • 缺点
    • 会占用服务器端空间
    • session实现离不开cookie,如果浏览器禁用cookie,session不好实现
    • 不适合前后端分离式的开发

适用场景

  • 传统的服务器渲染模式
  • 安全性要求较高的数据可以使用session存放,比如用户私密信息、验证码等

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/247989.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

继承上机作业

1、实现如下类之间的继承关系&#xff0c;并编写Music类来测试这些类 2、编写一个Java应用程序&#xff0c;该程序包括3个类&#xff1a;Monkey类、People类和主类E。要求&#xff1a; (1) Monkey类中有个构造方法&#xff1a;Monkey (String s)&#xff0c;并且有个public voi…

ApplePay集成教程

Apple Pay运行环境&#xff1a;iPhone6以上设备&#xff0c;操作系统最低iOS9.0以上&#xff0c;部分信息设置需要iOS9.2以上。目前还不支持企业证书添加。 环境搭建好后可以在模拟器上面运行&#xff0c;xcode7.2.1iPhone6SP9.2系统下&#xff0c;系统会绑定几种虚拟的银行卡…

Node — 第七天 (大事件项目接口实现一)

关于JS错误处理 node中和mysql中的错误处理 node和MySQL提供的方法&#xff0c;已经对错误信息进行了封装&#xff0c;只需要使用 err.message 即可获取到错误信息。 比如&#xff1a; const fs require(fs); // 读取一个不存在的文件 fs.readFile(abcd.txt, (err, data) …

1.Consul 简介和环境搭建

1.什么是 Consul Consul 是 service mesh(服务网格)的一个解决方案&#xff0c;它提供了诸如服务发现&#xff0c;配置和隔离等功能的一整套控制平面(control plane)。开发人员可以根据需要单独使用这些功能点&#xff0c;也可以将他们整合成为一个完整的service mesh。Consul …

Node — 第八天 (大事件项目接口实现二)

如何处理MySQL的错误 MySQL的错误信息&#xff0c;可以通过err来获取。这是没有问题的。 但是&#xff0c;我们加入了Promise&#xff0c;Promise中的错误&#xff0c;在外部是获取不到的&#xff0c;只能使用Promise相关方法来获取错误信息。 解决方法一 使用 JS原生的 tr…

在local模式下的spark程序打包到集群上运行

一、前期准备 前期的环境准备&#xff0c;在Linux系统下要有Hadoop系统&#xff0c;spark伪分布式或者分布式&#xff0c;具体的教程可以查阅我的这两篇博客&#xff1a; Hadoop2.0伪分布式平台环境搭建 Spark2.4.0伪分布式环境搭建 然后在spark伪分布式的环境下必须出现如下八…

APS系统对制造企业到底有多重要?看完这5点你就明白了

第一个问题&#xff1a;需要APS吗&#xff1f; APS是否重要&#xff0c;不能从其所体现的软件工具或系统角度来说&#xff0c;而应该从业务角度来说。对于制造工厂和车间的运行而言&#xff0c;计划是核心的业务。就如同那句俗话说的&#xff0c;没有规矩不成方圆&#xff0c;领…

Node — 第九天 (大事件项目接口实现三)

文章管理接口 设计数据表 添加文章接口 编写接口&#xff0c;使用postman模拟提交formdata类型的数据 在article.js 中&#xff0c;加入 /add 路由 postman模拟提交formdata类型的数据 multer处理文件上传 下载安装multer 加载模块 const multer require(multer) 配置上…

Node — 第九天 (ES6降级 and 发布属于自己的[第三方模块]包)

ES6降级处理 因为 ES 6 有浏览器兼容性问题&#xff0c;可以使用一些工具进行降级处理&#xff0c;例如&#xff1a;babel 降级处理 babel 的使用步骤 安装 Node.js命令行中安装 babel配置文件 .babelrc运行命令&#xff0c;完成降级 项目初始化 (项目文件夹不能有中文) npm …

Vue — 第一天(极速入门)

基本介绍 vue是什么 目标&#xff1a;了解vue的一些基础概念。 官方网站&#xff1a; https://cn.vuejs.org/ vue是&#xff1a;渐进式javascript框架。 两组概念 &#xff08;1&#xff09;框架 库。只提供一些API给开发者使用。jquery 是一个js库框架。拥有自己的规则和…

Vue — 第二天(v-model和过滤器)

VUE-02-v-model和过滤器 昨日反馈与回顾 代码仓库的问题 不要修改你克隆下来的仓库中任意代码&#xff0c;否则&#xff0c;下次pull时&#xff0c;可能会报错&#xff0c;从而得到不到最新的代码。 如果已经遇到了这个冲突&#xff1a; 解决冲突(git 中解决冲突)把关键代码…

牛人iOS开发系列--音频播放、录音、视频播放、拍照、视频录制

概览 随着移动互联网的发展&#xff0c;如今的手机早已不是打电话、发短信那么简单了&#xff0c;播放音乐、视频、录音、拍照等都是很常用的功能。在iOS中对于多媒体的支持是非常强大的&#xff0c;无论是音视频播放、录制&#xff0c;还是对麦克风、摄像头的操作都提供了多套…

Vue — 第三天(计算属性和json-server)

计算属性 使用场景 如果一个结果需要依赖data中的数据&#xff0c;但是需要经过一些逻辑处理&#xff0c;才能得到你想要的数据。此时就可以使用计算属性。 例如&#xff1a;要对给定的字符串做翻转处理之后再来显示。 <div id"app"><!-- 此处逻辑复杂 …

Vue — 第四天(components组件)

问题导入 下面的代码是一个折叠面板的效果。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Docu…

iOS开发常用的RGB色值和宏

iOS中RGB常用的色值,同时可将对颜色的设置定义成宏,方便开发应用,如: // name 颜色相关 // 参数格式为&#xff1a;0xFFFFFF #define kColorWithRGB(rgbValue) \ [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16)) / 255.0 \ …

防火墙综合实验

防火墙技术综合实验 一、实验目的&#xff1a;本次实验是将多种访问控制列表以及防火墙部分的知识做一个汇总 二、实验内容 A&#xff1a;Established控制列表 拓扑图 配置步骤 1:配置各端口ip地址&#xff0c;配置登陆密码 R4: 登陆账号&#xff1a;ys 密码&#xff1a;123 2:…

iOS获取当前设备型号等信息总结 包含iPhone7和iPhone7P

#include <sys/types.h> #include <sys/sysctl.h>//获得设备型号(NSString *)getCurrentDeviceModel {int mib[2];size_t len;char *machine;mib[0] CTL_HW;mib[1] HW_MACHINE;sysctl(mib, 2, NULL, &len, NULL, 0);machine malloc(len);sysctl(mib, 2, mac…

Vue — 第五天(路由)

前端路由 问题导入 在前面完成的资产管理案例中&#xff0c; 我们是把列表区域和添加表单区域实现在了一个区域。当页面功能比较复杂时&#xff0c;我们需要它们拆分开来&#xff1a;一个页面中只显示一个区域。 一个比较直观的解决方案是把它们分别做成两个独立的网页文件&…

Vue — 第六天(vue-cli-介绍)

vue-cli-介绍 vue-cli是官方提供的开发vue项目的脚手架工具。 脚手架是为了保证各施工过程顺利进行而搭设的工作平台。 在开发过程中&#xff0c;脚手架工具是有用的&#xff0c;开发完成&#xff08;项目上线&#xff09;&#xff0c;它就没有用了。 vue-cli可以提供基于vue项…

Vue — 第七天(vue-cli-案例)

资料获取地址&#xff1a; github: https://gitee.com/wang_yu5201314/VUE_vuecli SSH&#xff1a; gitgitee.com:wang_yu5201314/VUE_vuecli.git hero案例-项目介绍 功能介绍&#xff1a; 三个模块 英雄列表(只做这个)装备列表技能列表 英雄列表 列表组件删除功能添加组件编…