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.pswp.cn/news/247989.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/247989.shtml
英文地址,請注明出處:http://en.pswp.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偽分布式的環境下必須出現如下八…

Effective Objective-C 2.0 初讀小結

1.對于OC中的對象聲明例如NSObject *obj1 [NSObject new];, obj1這個指針變量是分配在棧上的, 他指向的是這一個分配在堆上面的實例對象, 如果進行下面的賦值操作NSObject *obj2 obj1;,那么并沒有新生成一個實例對象, 只是在棧上分配了一個新的指針變量obj2, 而obj2和obj1指向…

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) 配置上…

Python之爬蟲-段子網

Python之爬蟲-段子網 https://ishuo.cn #!/usr/bin/env python # -*- coding:utf-8 -*- import re import requestsresponse requests.get(https://ishuo.cn) data response.text print(data) r re.findall(<div class"content">(.*?)</div>,data) f…

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庫框架。擁有自己的規則和…

python類和實例化

簡答介紹類和實例python是面向對象的語言&#xff0c;最主要的就是類和實例&#xff0c;類是抽象的模版創建一個類class Studen(object),class 后接類名&#xff0c;定義的類名大些字母開頭&#xff0c;object為類的繼承&#xff0c;沒有合適的繼承類用object類&#xff0c;這是…

pjsip庫分析

http://blog.chinaunix.net/space.php?uid287570&doblog&cuid728411 如果你對SIP/VoIP技術感興趣,哪希望你不要錯過:),如果你對寫出堪稱優美的Code感興趣,那么你也不可錯過:)這期間我想分析一下一個實際的協議棧的設計到實現的相關技術,算是自己的一個學習經歷記錄.最…

Vue — 第二天(v-model和過濾器)

VUE-02-v-model和過濾器 昨日反饋與回顧 代碼倉庫的問題 不要修改你克隆下來的倉庫中任意代碼&#xff0c;否則&#xff0c;下次pull時&#xff0c;可能會報錯&#xff0c;從而得到不到最新的代碼。 如果已經遇到了這個沖突&#xff1a; 解決沖突(git 中解決沖突)把關鍵代碼…

Count

題目鏈接&#xff1a;點這里 題目意思&#xff1a;令f(x)表示<x的正整數中與x互質的數的平均數*2&#xff0c;求sigma(f(i)^k),L<i<R Solution: 首先&#xff0c;我們定義\(S(x)\sum_{gcd(a,x)1}a\)&#xff0c;因為gcd(a,x)1&#xff0c;所以對于任意a&#xff0c;滿…

牛人iOS開發系列--音頻播放、錄音、視頻播放、拍照、視頻錄制

概覽 隨著移動互聯網的發展&#xff0c;如今的手機早已不是打電話、發短信那么簡單了&#xff0c;播放音樂、視頻、錄音、拍照等都是很常用的功能。在iOS中對于多媒體的支持是非常強大的&#xff0c;無論是音視頻播放、錄制&#xff0c;還是對麥克風、攝像頭的操作都提供了多套…

Vue — 第三天(計算屬性和json-server)

計算屬性 使用場景 如果一個結果需要依賴data中的數據&#xff0c;但是需要經過一些邏輯處理&#xff0c;才能得到你想要的數據。此時就可以使用計算屬性。 例如&#xff1a;要對給定的字符串做翻轉處理之后再來顯示。 <div id"app"><!-- 此處邏輯復雜 …

JQuery的ready函數與JS的onload的區別詳解

JQuery的ready函數與JS的onload的區別&#xff1a;1.執行時間window.onload必須等到頁面內包括圖片的所有元素加載完畢后才能執行。$(document).ready()是DOM結構繪制完畢后就執行&#xff0c;不必等到加載完畢。 2.編寫個數不同window.onload不能同時編寫多個&#xff0c;如果…

Vue — 第四天(components組件)

問題導入 下面的代碼是一個折疊面板的效果。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Docu…