www.expressjs.com.cn/javascript
基於 Node.js 平臺,快速、開放、極簡的 Web 開發框架。提供了一系列強大特性幫助你建立各類Web 應用,和豐富的 HTTP 工具。使用 Express 能夠快速地搭建一個完整功能的網站。html
Express 框架核心特性:前端
能夠設置中間件來響應HTTP 請求。java
定義了路由表用於執行不一樣的HTTP 請求動做。node
能夠經過向模板傳遞參數來動態渲染HTML 頁面。mysql
經過npm安裝:sql
$ npm i -S express
複製代碼
安裝完成後,咱們可經過以下指令查看express版本號:shell
$ npm list express
└── express@4.17.1
複製代碼
如下模塊是須要與express 框架一塊兒安裝的:數據庫
body-parser
: nodeJS中間件,用於處理 JSON, Raw, Text 和 URL 編碼的數據。express
cookie-parser
:解析Cookie的工具 。經過req.cookies能夠取到傳過來的cookie,並把它們轉成對象。
multer
:nodeJS中間件,用於處理 enctype="multipart/form-data"(設置表單的MIME編碼)的表單數據。
$ npm i -S body-parser cookie-parser multer
複製代碼
在項目根目錄建立app.js文件
// 1. 導入express
const express = require("express");
// 2. 建立express實例
const app = express();
// 3. 監聽 http://127.0.0.1:8081
app.listen(8081, "127.0.0.1");
// 4. 監聽GET請求,用戶訪問路徑 /
app.get("/", (req, res) => {
// req -> request -> 請求對象
// res -> response -> 響應對象
// 響應,向前端發送數據
res.send("Hello, express!");
});
// 5. 打印輸出提示信息
console.log("server running at http://127.0.0.1:8081");
複製代碼
終端執行腳本,運行app.js
$ node app.js
複製代碼
在瀏覽器輸入:http://127.0.0.1:8081,可看到頁面輸出「Hello, express!」
注意:listen 方法中的ip地址設置的是哪些ip能訪問該服務器,而不是服務器地址,若是容許局域網內的全部設備訪問該服務器,在設置 listen 方法的 ip 參數時能夠設置爲 」0.0.0.0「,這樣,局域網內的其餘設備就能夠經過服務器所在電腦的 」ip地址:端口號「 訪問啦。
app.all("*", (req, res, next) => {
// 設置容許跨域的域名,*表明容許任意域名跨域
res.header("Access-Control-Allow-Origin","*");
// 容許的header類型
res.header("Access-Control-Allow-Headers","content-type");
// 跨域容許的請求方式
res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options')
res.send(200); // 讓options嘗試請求快速結束
else
next();
});
複製代碼
假定容許 「http://127.0.0.1:5500」 訪問:
app.all("*", (req, res, next) => {
// 設置容許跨域的域名,*表明容許任意域名跨域
res.header("Access-Control-Allow-Origin","http://127.0.0.1:5500");
// 容許的header類型
res.header("Access-Control-Allow-Headers","content-type");
// 跨域容許的請求方式
res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options')
res.send(200); //讓options嘗試請求快速結束
else
next();
});
複製代碼
app.all("*", (req,res,next) => {
//定義容許跨域的域名集合
const origin_list = [
"http://127.0.0.1:5500",
"http://127.0.0.1:5501",
"http://127.0.0.1:5502"
];
if(origin_list.includes(req.headers.origin.toLowerCase())) {
// 設置容許跨域的域名,*表明容許任意域名跨域
res.header("Access-Control-Allow-Origin", req.headers.origin);
}
// 容許的header類型
res.header("Access-Control-Allow-Headers","content-type");
// 跨域容許的請求方式
res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options')
res.send(200); // 讓options嘗試請求快速結束
else
next();
});
複製代碼
路由是指如何定義應用的端點(URIs)以及如何響應客戶端的請求。路由是一個由URI、HTTP請求(GET/POST等)和若干個句柄組成,它的語法結構以下:
app.method(path:string, hander:function) 複製代碼
語法解讀:
app
:express對象的一個實例method
:http請求方法(get/post),小寫path
:路徑,接口地址 = 服務器地址 + pathhande
:當路由匹配時要執行的處理函數// GET請求
// http://127.0.0.1:8081/
app.get("/", (req, res) => {
console.log("有人經過GET訪問:/");
});
app.get("/register", (req, res)=>{
})
// POST請求
// http://127.0.0.1:8081/login
app.post("/login", (req, res) => {
console.log("有人經過POST訪問:/login");
});
複製代碼
將路由定義在app.js文件中,不利於閱讀維護,特別是在項目比較大的狀況下,因此咱們須要將路由模塊化。好比項目有用戶、和商品功能,那咱們就定義2個路由,文件結構以下:
|- proj
|- routes
|- user.js
|- order.js
|- app.js
複製代碼
路由分析:
- 用戶接口 user
> 登陸接口:POST /login
參數1 => username:string required
參數2 => password:string required
> 註冊接口:POST /register
參數1 => username:string required
參數2 => password:string required
參數3 => tel: string options
- 訂單接口 order
> 增 POST /order/add
> 刪 GET /order/delete
> 查 GET /order/query
> 改 POST /order/modify
複製代碼
user.js
// 1. 建立路由實例
const router = require("express").Router();
// 2. 配置路由
router.post("/login", (req, res) => {
console.log("「登陸接口」被調用");
});
router.post("/register", (req, res) => {
console.log("「註冊接口」被調用");
});
// 3. 導出路由
module.exports = router;
複製代碼
order.js
// 1. 建立路由實例
const router = require("express").Router();
// 2. 配置路由
router.get("/", (req, res) => {
console.log("「查詢訂單接口」被調用");
});
router.post("/add", (req, res) => {
console.log("「添加訂單接口」被調用");
});
router.post("/delete", (req, res) => {
console.log("「刪除訂單接口」被調用");
});
router.post("/modify", (req, res) => {
console.log("「修改訂單接口」被調用");
});
// 3. 導出路由
module.exports = router;
複製代碼
app.js
// 1. 導入express
const express = require("express")();
// 2. 獲取express實例
const app = express();
// 3. 處理跨域
app.all("*", (req, res, next) => {
// 設置容許跨域的域名,*表明容許任意域名跨域 CROS
res.header("Access-Control-Allow-Origin","*");
// 容許的header類型
res.header("Access-Control-Allow-Headers","content-type");
// 跨域容許的請求方式
res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options')
res.send(200); // 讓options嘗試請求快速結束
else
next();
});
// 4. 處理路由
const userRouter = require("./routes/user");
const orderRouter = require("./routes/order");
app.use("/", userRouter);
app.use("/orders", orderRouter);
// 5. 監聽 http://127.0.0.1:8081
app.listen(8081, "0.0.0.0");
// 6. 打印輸出提示信息
console.log("server running at http://127.0.0.1:8081");
複製代碼
經過 req.query
便可獲取GET請求參數,並且最終獲取的仍是已經被轉換爲對象了的參數,在原生nodeJS實現中,咱們還須要使用 queryString
模塊來進行顯式的轉換。
這裏須要使用咱們剛纔安裝的 body-parser
模塊,獲取POST參數,需注意如下幾點:
前端代碼須要傳遞請求頭,請求參數需以JSON字符串形式傳遞。
後端代碼須要添加中間件 body-parser
(牢記)
通過中間件的處理,參數會自動的添加到req請求對象上,做爲其一個屬性body
前端代碼:
fetch("http://127.0.0.1:8081/login", {
method: "post",
body: JSON.stringify({
username: "admin",
password: "123"
}),
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
}
})
.then(res => res.json())
.then(res => {
console.log(res);
});
複製代碼
後端代碼:
// 4. 中間件
const bodyParser = require("body-parser");
// application/x-www-form-urlencoded 解析
app.use(bodyParser.urlencoded({extended:false}));
// application/json 解析
app.use(bodyParser.json());
// 5. 路由配置
app.post("/login", (req, res) => {
// 打印參數
console.log(req.body);
// 響應數據
res.send({
nikename: "木子李",
tel: "152-2888-5771",
address: "成都市高新區雅和南四路216號"
});
});
複製代碼
關於body-parser中間件詳細內容可參考以下文章:
在Express中,請求對象與響應對象做爲路由處理函數的參數返回,以下所示:
app.get("/", (req, res) => {
req.body // => 讀取post參數
req.query // => 讀取get參數
res.send // => 響應客戶端
});
複製代碼
關於req、res經常使用屬性和方法,點擊上述連接進入API文檔查看。
經過Express 提供的內置中間件 express.static ,咱們能夠實現訪問靜態資源(如圖片、CSS、JavaScript等)的需求。例如,若是將圖片、CSS、JavaScript 文件放在 www 目錄下,你能夠這麼寫:
app.use(express.static("www"));
複製代碼
那咱們便可在瀏覽器中經過」http://127.0.0.1:8081/images/logo.png「訪問靜態資源了。
服務器端靜態資源的文件結構以下:
|- proj
└── routes
└── app.js
└── www
└── images
└── javascripts
└── stylesheets
複製代碼
總結:
\1. 在最開始處使用express內置的static中間件,來實現靜態文件的訪問。
\2. 前端訪問時,地址欄只需輸入服務器地址 + express.static 參數內的路徑和文件名稱便可。
a)、安裝mysql模塊
$ npm i -S mysql
複製代碼
b)、導入mysql模塊
const mysql = require("mysql");
複製代碼
c)、連接數據庫
const connection = mysql.createConnection({
// 主機名
host: '127.0.0.1',
// 端口
port: '3306',
// 用戶名
user: 'root',
// 密碼
password: '1234',
// 數據庫名
database: 'db_test'
});
connection.connect(); // 啓動鏈接
// 數據庫操做...
connection.end(); // 關閉鏈接
複製代碼
更多參數參考:www.npmjs.com/package/mys…
在鏈接數據庫的狀況下咱們開始操做數據庫連接對象:connection。
mysql 數據庫heros表中的字段信息:
mysql 數據庫heros表中的數據信息:
-> 語法形式:
connection.query(sql,function (err, sqlRes) {});
複製代碼
-> 參數解讀:
sql
:操做數據庫的指令(sql語句)function(){}
:返回查詢結果的回調函數-> 代碼示例:
connection.connect();
const sql = "SELECT * FROM heros";
function fn(err, sqlRes) {
if(err) {
console.log(err.message);
}else {
console.log(sqlRes);
}
}
connection.query(sql, fn)
connection.end(); // 操做完數據庫記得關閉連接
複製代碼
-> 注意事項:
1)若是你想經過定時器來循環操做數據庫,那麼數據庫的開啓連接語句必定要放在這些語句以外。
2)關閉連接語句要寫的話也應該寫在定時執行的函數裏面,否則定時器執行一次,連接就會關掉,就沒法在操做數據庫了。
3)關閉連接後,獲取數據庫連接對象只能從新執行*mysql.createConnection()*方法。
4)不能屢次重複調用 .connect() 方法,不然會報錯。
5)不能屢次重複調用 .end() 方法,不然會報錯。
-> 查詢結果:
-> 遍歷結果:
實際上查詢出的結果是一個對象數組,咱們能夠經過下標與鍵取出其中詳細數據,也能經過循環將其全部值遍歷出來
-> 1. 單獨取值:
reqRes.[0];
reqRes.[0].name;
複製代碼
-> 2. 遍歷取值
for(let i = 0, len = reqRes.length; i < len; i++) {
console.log(reqRes[i]);
}
複製代碼
-> 3. 將查詢結果返回給前端
能夠看到,都不須要作什麼處理,直接把查詢結果用 JSON.parse(sqlRes) 轉換爲字符串就能夠發到前端了,前端在轉換爲對象數組就能直接用了
-> 4. 條件查詢
-> 5. 預編譯寫法
其實這裏能夠寫成預編譯的方式,能夠避免sql注入的問題,所謂預編譯就是說對於本來在sql裏面寫值換成?
佔位,而後傳入一個數組進去補全這個佔位符,也就是connect.query 方法會多一個參數,爲一個數組,放入第二個位置。
-> 語法形式:
connection.query(sql, sqlParams, function (err, sqlRes) {});
複製代碼
-> 參數解讀:
sql
:sql指令,如:UPDATE heros SET location=? WHERE name=?sqlParams
:sql參數,如:["上路", "周瑜"]function(){}
:執行結果回調函數-> 代碼示例:
提示:咱們可經過影響行數 affectedRows 屬性判斷是否修改爲功。
-> 語法形式:
connection.query(sql, sqlParams, function (err, sqlRes) {});
複製代碼
-> 參數解讀:
sql
:sql指令,如:INSERT INTO heros (name, skill) VALUES (?, ?)sqlParams
:sql參數,如:["貂蟬", "綻·風華"]function(){}
:執行結果回調函數-> 代碼示例:
-> 語法形式:
connection.query(sql, sqlParams, function (err, sqlRes) {});
複製代碼
-> 參數解讀:
sql
:sql指令,如:DELETE FROM heros WHERE name=?sqlParams
:sql參數,如:["貂蟬"]function(){}
:執行結果回調函數-> 代碼示例:
在上面咱們每次操做數據庫都要先鏈接下數據庫,而後才能操做,那麼咱們是否可以把數據的連接封裝起來呢,這樣每次直接獲取連接對象,來操做數據庫便可,會方便很多。
const mysql = require("mysql");
const defaultOptions = {
host: '127.0.0.1',
port: '3306',
user: 'root',
password: '1234',
database: 'db_test'
}
function getConnection(options = defaultOptions) {
return mysql.createConnection(options);
}
module.exports = getConnection;
複製代碼
使用
const getConnection = require("./mysqlConnection");
const db = getConnection();
app.get("/heros", (req, res) => {
db.connect();
const sql = "SELECT * FROM heros";
function fn(err, sqlRes) {
if(err) {
console.log(err.message);
}else {
res.end(JSON.stringify(sqlRes));
}
}
db.query(sql, fn)
db.end();
});
複製代碼
- 連接數據庫時報錯:client does not support authentication protocol requested by server consider,
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '本身的密碼';
複製代碼