Node + Express 后臺開發 —— 起步
前面陸續學習了一下 node、npm、模塊,也稍嘗試 Express,感覺得換一個思路加快進行。
比如筆者對前端的開發已較熟悉,如果領導給一個內部小網站
的需求,難道說你得給我配置一個后端
?
又不是做一個復雜的后端,只是簡單的數據存儲(增刪改查)、上傳文件、下載csv或excel,無需考慮性能、穩定性、負載均衡等等,怎么就不能做
目標
實現簡單后臺的開發和部署
Express 項目生成器
生成項目
可通過應用生成器工具?express-generator?可以快速創建一個應用的骨架
創建項目文件夾 spug-back-end,進入項目后執行?npx express-generator
:
Administrator@ /e/spug-back-end
$ npx express-generator
npm WARN exec The following package was not found and will be installed: express-generator@4.16.1
npm WARN deprecated mkdirp@0.5.1: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)warning: the default view engine will not be jade in future releaseswarning: use `--view=jade' or `--help' for additional optionscreate : public\create : public\javascripts\create : public\images\create : public\stylesheets\create : public\stylesheets\style.csscreate : routes\create : routes\index.jscreate : routes\users.jscreate : views\create : views\error.jadecreate : views\index.jadecreate : views\layout.jadecreate : app.jscreate : package.jsoncreate : bin\create : bin\wwwinstall dependencies:$ npm installrun the app:$ DEBUG=spug-back-end:* npm start
生成如下內容:
Administrator@ /e/spug-back-end
$ ll
total 5
-rw-r--r-- 1 Administrator 197121 1075 Apr 14 15:06 app.js
drwxr-xr-x 1 Administrator 197121 0 Apr 14 15:06 bin/
-rw-r--r-- 1 Administrator 197121 301 Apr 14 15:06 package.json
drwxr-xr-x 1 Administrator 197121 0 Apr 14 15:06 public/
drwxr-xr-x 1 Administrator 197121 0 Apr 14 15:06 routes/
drwxr-xr-x 1 Administrator 197121 0 Apr 14 15:06 views/
Tip: 對于較老的 Node 版本(8.2.0 以下),請通過 npm 將 Express 應用程序生成器安裝到全局環境中并使用
$ npm install -g express-generator
$ express
根據上文提示安裝依賴?npm install
:
Administrator@ /e/spug-back-end
$ npm install
npm WARN deprecated constantinople@3.0.2: Please update to at least constantinople 3.1.1
npm WARN deprecated transformers@2.1.0: Deprecated, use jstransformer
npm WARN deprecated jade@1.11.0: Jade has been renamed to pug, please install the latest version of pug instead of jadeadded 99 packages, and audited 100 packages in 22s1 package is looking for fundingrun `npm fund` for details8 vulnerabilities (1 low, 4 high, 3 critical)To address all issues (including breaking changes), run:npm audit fix --forceRun `npm audit` for details.
全局安裝 nodemon(在Node.js應用程序開發過程中使用的簡單監視器腳本),編碼過程中無需重啟 node 服務即可生效:
PS E:\spug-back-end> npm i -g nodemonchanged 32 packages, and audited 33 packages in 1s3 packages are looking for fundingrun `npm fund` for detailsfound 0 vulnerabilities
修改啟動腳本:
"version": "0.0.0","private": true,"scripts": {
- "start": "node ./bin/www"
+ "start": "nodemon ./bin/www"
通過?npm run start
?本地啟動服務:
PS E:\spug-back-end> npm run start> spug-back-end@0.0.0 start
> nodemon ./bin/www[nodemon] 2.0.22
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node ./bin/www`
入口文件?/bin/www.js
?默認使用的是 3000 端口(var port = normalizePort(process.env.PORT || '3000');
)
瀏覽器訪問?http://localhost:3000/
,頁面顯示:
Express
Welcome to Express
項目解讀
模板
views
?目錄中存放的是 jade?模板
文件:
Administrator@ /e/spug-back-end/views (master)
$ ls
error.jade index.jade layout.jade
首頁
對應的模板是 index.jade:
Administrator@ /e/spug-back-end/views (master)
$ cat index.jade
extends layoutblock contenth1= titlep Welcome to #{title}
Express 生成器默認使用 jade 模板,對前端不是很友好,筆者通過?npx express-generator --view=ejs
?選用?ejs
模板(對前端更友好)重新創建。
首頁對應模板 index.ejs:
<!DOCTYPE html>
<html><head><title><%= title %></title><link rel='stylesheet' href='/stylesheets/style.css' /></head><body><h1><%= title %></h1><p>Welcome to <%= title %></p></body>
</html>
入口文件
bin/www.js
?是應用的入口文件。
核心代碼如下:
// 加載 app.js
var app = require('../app');
var http = require('http');var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);// 創建 http server
var server = http.createServer(app);
server.listen(port);
完整代碼如下:
#!/usr/bin/env node/*** Module dependencies.*/var app = require('../app');
var debug = require('debug')('spug-back-end:server');
var http = require('http');/*** Get port from environment and store in Express.*/var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);/*** Create HTTP server.*/var server = http.createServer(app);/*** Listen on provided port, on all network interfaces.*/server.listen(port);
server.on('error', onError);
server.on('listening', onListening);/*** Normalize a port into a number, string, or false.*/function normalizePort(val) {var port = parseInt(val, 10);if (isNaN(port)) {// named pipereturn val;}if (port >= 0) {// port numberreturn port;}return false;
}/*** Event listener for HTTP server "error" event.*/function onError(error) {if (error.syscall !== 'listen') {throw error;}var bind = typeof port === 'string'? 'Pipe ' + port: 'Port ' + port;// handle specific listen errors with friendly messagesswitch (error.code) {case 'EACCES':console.error(bind + ' requires elevated privileges');process.exit(1);break;case 'EADDRINUSE':console.error(bind + ' is already in use');process.exit(1);break;default:throw error;}
}/*** Event listener for HTTP server "listening" event.*/function onListening() {var addr = server.address();var bind = typeof addr === 'string'? 'pipe ' + addr: 'port ' + addr.port;debug('Listening on ' + bind);
}
Tip: 和我們之前創建的最簡單的服務器類似
app.js
入口文件中引入 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');
// Creates an Express application
var app = express();// view engine setup
// 模板引擎相關代碼
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');app.use(logger('dev'));
// for parsing application/json
app.use(express.json());
// for parsing application/x-www-form-urlencoded
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
// 放開靜態資源
app.use(express.static(path.join(__dirname, 'public')));// 定義兩個路由。一個返回頁面一個返回后端數據
// app.use([path,] callback [, callback...]) - 在指定路徑掛載指定的一個或多個中間件函數:當請求路徑的基匹配路徑時,執行中間件函數。
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 developmentres.locals.message = err.message;res.locals.error = req.app.get('env') === 'development' ? err : {};// render the error pageres.status(err.status || 500);res.render('error');
});module.exports = app;
和我們之前用 Express 實現的報名系統?非常類似。這里創建了一個 Express 應用,并定義了兩個示例路由:
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');// 返回頁面
app.use('/', indexRouter);
// 返回后端數據
app.use('/users', usersRouter);
路由
app.js 使用了兩個路由。內容如下:
// 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;
瀏覽器訪問?http://localhost:3000/users
,頁面顯示?respond with a resource
。
mongodb
數據庫通常會安裝到 linux 中,這里以 ubuntu 為例,通過?apt-get install mongodb
?即可安裝,非常方便:
// 筆者剛已執行
root@linux:/home/pjl# apt-get install mongodb
Reading package lists... Done
Building dependency tree
Reading state information... Done
mongodb is already the newest version (1:3.6.9+really3.6.8+90~g8e540c0b6d-0ubuntu5.3).
0 upgraded, 0 newly installed, 0 to remove and 150 not upgraded.
數據庫現在已經啟動,我們通過mongo -version
驗證安裝成功:
root@linux:/home/pjl# mongo -version
MongoDB shell version v3.6.8
git version: 8e540c0b6db93ce994cc548f000900bdc740f80a
OpenSSL version: OpenSSL 1.1.1f 31 Mar 2020
allocator: tcmalloc
modules: none
build environment:distarch: x86_64target_arch: x86_64
mongodb 配置文件是?/etc/mongodb.conf
。請注意下面兩項配置:
// 遠程連接
#bind_ip = 127.0.0.1
bind_ip = 0.0.0.0// 開機自啟動
#auth = true
auth = true
MongoDB shell
輸入 mongo 即可進入?MongoDB shell
(操作mongo):
root@linux:/home/pjl# mongo
MongoDB shell version v3.6.8
connecting to: mongodb://127.0.0.1:27017
Implicit session: session { "id" : UUID("aedc6541-4a67-4e60-8eb4-d1325c82d061") }
MongoDB server version: 3.6.8
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, seehttp://docs.mongodb.org/
Questions? Try the support grouphttp://groups.google.com/group/mongodb-user
Server has startup warnings:
2023-04-15T14:34:32.327+0800 I STORAGE [initandlisten]
2023-04-15T14:34:32.327+0800 I STORAGE [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2023-04-15T14:34:32.327+0800 I STORAGE [initandlisten] ** See http://dochub.mongodb.org/core/prodnotes-filesystem
2023-04-15T14:34:34.861+0800 I CONTROL [initandlisten]
2023-04-15T14:34:34.861+0800 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.
2023-04-15T14:34:34.861+0800 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.
2023-04-15T14:34:34.861+0800 I CONTROL [initandlisten]
> helpdb.help() help on db methodsdb.mycoll.help() help on collection methodssh.help() sharding helpersrs.help() replica set helpershelp admin administrative helphelp connect connecting to a db helphelp keys key shortcutshelp misc misc things to knowhelp mr mapreduceshow dbs show database namesshow collections show collections in current databaseshow users show users in current databaseshow profile show most recent system.profile entries with time >= 1msshow logs show the accessible logger namesshow log [name] prints out the last segment of log in memory, 'global' is defaultuse <db_name> set current databasedb.foo.find() list objects in collection foodb.foo.find( { a : 1 } ) list objects in foo where a == 1it result of the last line evaluated; use to further iterateDBQuery.shellBatchSize = x set default number of items to display on shellexit quit the mongo shell
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
通過?help
?可以查看幫助,通過show dbs
?發現現在有三個數據庫。
新建數據庫
通過 use 可以創建或切換數據庫。比如:
// use - 創建數據庫 pjl_db。如果存在則直接切換
> use pjl_db
switched to db pjl_db
// db - 目前操作的是 pjl_db 數據庫
> db
pjl_db
由于新建的數據庫 pjl_db 什么都沒有,所以通過?show dbs
?顯示不出來,可通過 createCollection 創建表,再次查詢即可顯示該數據庫。比如
// 新建數據庫未能展示
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
// 通過 tab 建有提示
> db.create
db.createCollection( db.createRole( db.createUser( db.createView(
// 創建表 users。這里叫集合
> db.createCollection("users")
{ "ok" : 1 }
// 再次查詢則可顯示新建數據庫 pjl_db
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
pjl_db 0.000GB
Tip:db.createCollection("pjl_db", {size: 1024*1024, capped: true, max: 1000})
?- 創建 pjl_db 數據庫,同時這個數據庫最多 1M,記錄數只能有1000條,在多一條則把第一條給替代。db.createCollection("users")
?則不作限制。
刪除數據庫
通過 db.dropDatabase() 即可刪除數據庫。比如:
> db.dropDatabase()
{ "dropped" : "pjl_db", "ok" : 1 }
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
新增、查看和刪除表
通過?db.createCollection("users")
?新建 users 表。比如:
// 創建表 users。這里叫集合
> db.createCollection("users")
{ "ok" : 1 }
// 再次查詢則可顯示新建數據庫 pjl_db
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
pjl_db 0.000GB
- db.getCollectionNames - 查看有哪些表
- db.tableName.drop - 刪除某張表
示例:
// 新建兩張表: table-b、table-c
> db.createCollection("table-b")
{ "ok" : 1 }
> db.createCollection("table-c")
{ "ok" : 1 }
// tab 提示
> db.getCollection
db.getCollection( db.getCollectionInfos( db.getCollectionNames(
// 有哪些表
> db.getCollectionNames()
[ "table-b", "table-c", "users" ]
// 刪除table-b表失敗
> db.table-b.drop()
2023-04-15T15:17:10.232+0800 E QUERY [thread1] ReferenceError: b is not defined :
@(shell):1:1
// 刪除table-b表成功
> db["table-b"].drop()
true
> db.getCollectionNames()
[ "table-c", "users" ]
表格-新增數據
通過?db.users.save
?可以向 users 表中單條、批量插入數據。甚至字段名不同,字段數量不同也能插入。請看示例:
// 給 users 表插入一條數據 {"name": 'pjl', age: 18}
> db.users.save({"name": 'pjl', age: 18})
WriteResult({ "nInserted" : 1 })
// 查詢users表
> db.users.find()
{ "_id" : ObjectId("643a513d73f16a13ae248f42"), "name" : "pjl", "age" : 18 }
// 批量插入數據
> db.users.save([{"name": 'pjl2', age: 19}, {"name": 'pjl3', age: 20}])
BulkWriteResult({"writeErrors" : [ ],"writeConcernErrors" : [ ],"nInserted" : 2,"nUpserted" : 0,"nMatched" : 0,"nModified" : 0,"nRemoved" : 0,"upserted" : [ ]
})
// 批量插入成功
> db.users.find()
{ "_id" : ObjectId("643a513d73f16a13ae248f42"), "name" : "pjl", "age" : 18 }
{ "_id" : ObjectId("643a51db73f16a13ae248f44"), "name" : "pjl2", "age" : 19 }
{ "_id" : ObjectId("643a51db73f16a13ae248f45"), "name" : "pjl3", "age" : 20 }
// 換字段名和字段長度,也能插入成功
> db.users.save({"name2": 'pjl', age2: 18, tel: 131111111})
WriteResult({ "nInserted" : 1 })
> db.users.find()
{ "_id" : ObjectId("643a513d73f16a13ae248f42"), "name" : "pjl", "age" : 18 }
{ "_id" : ObjectId("643a51db73f16a13ae248f44"), "name" : "pjl2", "age" : 19 }
{ "_id" : ObjectId("643a51db73f16a13ae248f45"), "name" : "pjl3", "age" : 20 }
{ "_id" : ObjectId("643a529a73f16a13ae248f46"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }
Tip:向 mongo 表中插入數據非常自由,什么字段、什么類型都可以。
表格-刪除數據
通過?db.users.remove({age2:18})
?可以刪除 age2=18 的數據,通過?db.users.remove({})
?刪除 users 表中所有數據。請看示例:
// 目前有4條數據
> db.users.find()
{ "_id" : ObjectId("643a513d73f16a13ae248f42"), "name" : "pjl", "age" : 18 }
{ "_id" : ObjectId("643a51db73f16a13ae248f44"), "name" : "pjl2", "age" : 19 }
{ "_id" : ObjectId("643a51db73f16a13ae248f45"), "name" : "pjl3", "age" : 20 }
{ "_id" : ObjectId("643a529a73f16a13ae248f46"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }// 可以刪除 age2=18 的數據
> db.users.remove({age2:18})
WriteResult({ "nRemoved" : 1 })
> db.users.find()
{ "_id" : ObjectId("643a513d73f16a13ae248f42"), "name" : "pjl", "age" : 18 }
{ "_id" : ObjectId("643a51db73f16a13ae248f44"), "name" : "pjl2", "age" : 19 }
{ "_id" : ObjectId("643a51db73f16a13ae248f45"), "name" : "pjl3", "age" : 20 }// 刪除 users 表中所有數據是 `db.users.remove({})`。`db.users.remove()` 會報錯
> db.users.remove()
2023-04-16T09:10:55.859+0800 E QUERY [thread1] Error: remove needs a query :
DBCollection.prototype._parseRemove@src/mongo/shell/collection.js:357:1
DBCollection.prototype.remove@src/mongo/shell/collection.js:382:18
@(shell):1:1
// 刪除 users 表中所有數據
> db.users.remove({})
WriteResult({ "nRemoved" : 3 })
> db.users.find()
>
表格-修改數據
通過?db.users.update({name:'pjl2'}, {$set: {age: 20}})
?修改 name=pjl2 的數據,將 age 改為 20。
Tip:直接?db.users.update({name:'pjl'}, {age: 19})
?會替換整條數據。
\$inc
?指增加,如果需要減去,則將數字改成負數。
示例如下:
> db.users.find()
{ "_id" : ObjectId("643b4d0cd21fdd4d6f0b0484"), "name" : "pjl", "age" : 18 }
{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0486"), "name" : "pjl2", "age" : 19 }
{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0487"), "name" : "pjl3", "age" : 20 }
{ "_id" : ObjectId("643b4d1fd21fdd4d6f0b0488"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }
// 修改 name=pjl 的數據,將 age改為19
> db.users.update({name:'pjl'}, {age: 19})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
// 替換了條數據
> db.users.find()
{ "_id" : ObjectId("643b4d0cd21fdd4d6f0b0484"), "age" : 19 }
{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0486"), "name" : "pjl2", "age" : 19 }
{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0487"), "name" : "pjl3", "age" : 20 }
{ "_id" : ObjectId("643b4d1fd21fdd4d6f0b0488"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }
// 通過 $set 成功更改 age 而不影響其他字段
> db.users.update({name:'pjl2'}, {$set: {age: 20}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("643b4d0cd21fdd4d6f0b0484"), "age" : 19 }
{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0486"), "name" : "pjl2", "age" : 20 }
{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0487"), "name" : "pjl3", "age" : 20 }
{ "_id" : ObjectId("643b4d1fd21fdd4d6f0b0488"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }
// 給 age 增加1
> db.users.update({name:'pjl2'}, {$inc: {age: 1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("643b4d0cd21fdd4d6f0b0484"), "age" : 19 }
{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0486"), "name" : "pjl2", "age" : 21 }
{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0487"), "name" : "pjl3", "age" : 20 }
{ "_id" : ObjectId("643b4d1fd21fdd4d6f0b0488"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }
// 給 age 減去1
> db.users.update({name:'pjl2'}, {$inc: {age: -1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find()
{ "_id" : ObjectId("643b4d0cd21fdd4d6f0b0484"), "age" : 19 }
{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0486"), "name" : "pjl2", "age" : 20 }
{ "_id" : ObjectId("643b4d17d21fdd4d6f0b0487"), "name" : "pjl3", "age" : 20 }
{ "_id" : ObjectId("643b4d1fd21fdd4d6f0b0488"), "name2" : "pjl", "age2" : 18, "tel" : 131111111 }
表格-查詢數據
首先清空 users 表,并插入6條數據。
find
通過?db.users.find()
?查詢所有數據:
> db.users.find()
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li", "age" : 39, "tel" : "0730-1233" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia", "age" : 49, "tel" : "0730-1234" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }
>
大于小于
- $gt - 大于
- $gte - 大于等于
- $lt - 小于
- $lte - 小于等于
// 查詢 age 大于 50 的數據
> db.users.find({age:{$gt: 50}})
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }
// 查詢 age 大于 69 的數據
> db.users.find({age:{$gt: 69}})
// 查詢 age 大于等于 69 的數據
> db.users.find({age:{$gte: 69}})
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }
// age 小于 20
> db.users.find({age:{$lt: 20}})
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }
// 查詢 age 大于 10,小于 40 的數據
> db.users.find({age:{$gt:10, $lt:40}})
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li", "age" : 39, "tel" : "0730-1233" }
>
單條件
db.users.find({name: "jia"})
?- 查詢 name?等于
?jia 的數據db.users.find({name: /jia/})
?- 查詢 name 中包含
?jia 的數據db.users.find({name: /jia$/})
?- 查詢 name 中以 jia?結尾
的數據
// 查詢 name 等于 jia 的數據
> db.users.find({name: "jia"})
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }
// 查詢 name 中包含 jia 的數據
> db.users.find({name: /jia/})
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia", "age" : 49, "tel" : "0730-1234" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }
// 查詢 name 中以 jia 結尾的數據
> db.users.find({name: /jia$/})
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia", "age" : 49, "tel" : "0730-1234" }
多條件
db.users.find({$or: [{age: 19}, {name: 'jia'}]})
?查詢 age=19 或 name=jia
// 查詢 age=19 或 name=jia
> db.users.find({$or: [{age: 19}, {name: 'jia'}]})
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }
列過濾
db.users.find({}, {name: 1})
?- 只顯示 name 這個字段。默認主鍵回返回。1 是顯示,0指不顯示db.users.find({}, {name: 1, age: 1, _id: 0})
?- 只要 name 和 age 字段,主鍵不要
// 只顯示 name 這個字段。默認主鍵回返回
> db.users.find({}, {name: 1})
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali" }
// 只顯示 name 這個字段,主鍵也不要
> db.users.find({}, {name: 1, _id: 0})
{ "name" : "peng" }
{ "name" : "jia" }
{ "name" : "li" }
{ "name" : "pengjia" }
{ "name" : "pengjiali" }
{ "name" : "jiali" }
// 只要 name 和 age 字段,主鍵不要
> db.users.find({}, {name: 1, age: 1, _id: 0})
{ "name" : "peng", "age" : 19 }
{ "name" : "jia", "age" : 29 }
{ "name" : "li", "age" : 39 }
{ "name" : "pengjia", "age" : 49 }
{ "name" : "pengjiali", "age" : 59 }
{ "name" : "jiali", "age" : 69 }
排序
通過?db.users.find().sort({age: -1})
?執行 age 逆序,正序則為1。
// age 逆序
> db.users.find({}, {age:1}).sort({age: -1})
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "age" : 69 }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "age" : 59 }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "age" : 49 }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "age" : 39 }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "age" : 29 }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "age" : 19 }
// age 正序
> db.users.find({}, {age:1}).sort({age: 1})
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "age" : 19 }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "age" : 29 }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "age" : 39 }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "age" : 49 }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "age" : 59 }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "age" : 69 }
分頁
分頁可以通過 skip 和 limit實現。例如?db.users.find().skip(2).limit(2)
?查詢第二頁。跳過2條,查詢2條。
> db.users.find()
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li", "age" : 39, "tel" : "0730-1233" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia", "age" : 49, "tel" : "0730-1234" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }
// 查詢前3條
> db.users.find().limit(3)
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li", "age" : 39, "tel" : "0730-1233" }
// 第一頁。跳過0條,查詢2條
> db.users.find().skip(0).limit(2)
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"), "name" : "peng", "age" : 19, "tel" : "0730-1231" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048b"), "name" : "jia", "age" : 29, "tel" : "0730-1232" }
// 第二頁。跳過2條,查詢2條
> db.users.find().skip(2).limit(2)
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048c"), "name" : "li", "age" : 39, "tel" : "0730-1233" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048d"), "name" : "pengjia", "age" : 49, "tel" : "0730-1234" }
// 第三頁。跳過4條,查詢2條
> db.users.find().skip(4).limit(2)
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }
// 先 age 逆序,在取2條
> db.users.find().sort({age: -1}).skip(0).limit(2)
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048f"), "name" : "jiali", "age" : 69, "tel" : "0730-1236" }
{ "_id" : ObjectId("643bb3dfd21fdd4d6f0b048e"), "name" : "pengjiali", "age" : 59, "tel" : "0730-1235" }
通過 count() 查詢記錄數,例如?db.users.find({name: 'jia'}).count()
// 總共 6 條
> db.users.find().count()
6
// 符合條件的共 1 條
> db.users.find({name: 'jia'}).count()
1
> db.users.find({name: /jia/}).count()
4
db.users.findOne()
?- 查詢第一條
// 查詢第一條
> db.users.findOne()
{"_id" : ObjectId("643bb3dfd21fdd4d6f0b048a"),"name" : "peng","age" : 19,"tel" : "0730-1231"
}
項目目錄劃分
程序那么復雜,不可能全寫在一起,筆者做如下分層:
路由層
?- 匹配路由,調用控制層控制層
?- 取得前端請求數據,加工處理,比如調用數據庫(services層),在返回數據給前端服務層
?- 引用創建的數據庫模型,對數據進行增刪改查模型層
?- 創建數據庫模型(這里是創建表)
以用戶
模塊為例,在這4層中分別創建如下 js:
- services // 服務層- UserService.js // User 模塊的服務層
- routes // 路由- UserRouter.js // User 模塊的路由。比如登錄、登錄
- models // 模型層- UserModel.js // User Model
- controllers // 控制層- UserController.js
連接數據庫
前面我們操作數據庫是直接進入 mongo shell 操作:
// 創建并切換到數據庫 pjl_db
use pjl_db// 創建 users 表(這里叫集合)
db.createCollection("users")
現在我們需要通過 node 來操作數據庫。
啟動mongo服務
通過?mongod --dbpath=/var/lib/mongodb --bind_ip=0.0.0.0 --port=27017
?啟動mongo服務(未設置數據庫密碼)。其中?0.0.0.0
?用戶遠程連接,端口是?27017
。
如果需要后臺啟動(關閉終端服務也不會停止),需要指定日志路徑,就像這樣:
root@linux:/home/pjl# mongod --dbpath=/var/lib/mongodb --fork --logpath=/var/log/mongodb/mongodb.log --bind_ip=0.0.0.0 --port=27017
about to fork child process, waiting until server is ready for connections.
forked process: 133354
child process started successfully, parent exiting
查看 mongod 進程:
root@linux:/home/pjl# ps aux |grep mongod
root 133354 7.5 1.5 976348 62496 ? Sl 09:46 0:00 mongod --dbpath=/var/lib/mongodb --fork --logpath=/var/log/mongodb/mongodb.log --bind_ip=0.0.0.0 --port=27017
root 133383 0.0 0.0 12120 716 pts/0 S+ 09:47 0:00 grep --color=auto mongod
root@linux:/home/pjl#
Tip:還可以通過指定配置文件啟動:
root@linux:/home/pjl# mongod -f /etc/mongodb.conf
// 沒反應,通過 `tail -10 日志` 能看到輸出
另起窗口進入 mongo shell,運行?show dbs
?報錯如下:
// 筆者將配置文件中端口改為 27027
root@linux:/var/log/mongodb# mongo --port=27027
MongoDB shell version v3.6.8
connecting to: mongodb://127.0.0.1:27027/
Implicit session: session { "id" : UUID("dc184887-824d-474a-a942-3d42ff1a21bf") }
MongoDB server version: 3.6.8
> show dbs
2023-04-21T09:52:04.824+0800 E QUERY [thread1] Error: listDatabases failed:{"ok" : 0,"errmsg" : "there are no users authenticated","code" : 13,"codeName" : "Unauthorized"
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:67:1
shellHelper.show@src/mongo/shell/utils.js:860:19
shellHelper@src/mongo/shell/utils.js:750:15
@(shellhelp2):1:1
>
網友說是?安裝mongo數據庫時,配置文件中加了安全權限的設置
,解決請看這里
安裝 mongoose
通過?npm i mongoose -D
?安裝 mongoose —— Mongoose is a MongoDB object modeling tool)。
通過 mongoose 操作數據庫非常方便
連接數據庫
連接數據庫很簡單,只需要先啟動數據庫服務,然后在啟動應用前連接數據庫即可。代碼如下:
// bin/www.jsvar http = require('http');
+ // 引入數據庫
+ require('../config/db.config')
var port = normalizePort(process.env.PORT || '3000');
// config/db.config.jsconst mongoose = require("mongoose")
// mongo服務 ip
mongoose.connect("mongodb://192.168.1.223:27017/pjl_db")
mongoose.connection.once("open", () => {console.log('數據庫連接成功')
})
通過 mongo shell 查到目前只有3個數據庫:
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
啟動應用,控制臺輸出?數據庫連接成功
:
PS E:\spug-back-end> npm run start> spug-back-end@0.0.0 start
> nodemon ./bin/www[nodemon] 2.0.22
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node ./bin/www`
數據庫連接成功
發現 pjl_db 數據庫自動被創建。
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
pjl_db 0.000GB
Tip:有了數據庫,還得需要表才能對數據進行增、刪、改、查。在 mongoose 中,需要創建一個 model(模型),可以把他當做一張表(或一條記錄),比如下文登錄
模塊的?UserModel.js
?就是一個 model,向 model 插入一條數據時,mongoose 會自動創建一張名為 users 的表(或集合)。
登錄
這里我們完成系統登錄模塊的開發。
app.js
app.js 引入用戶路由。
// app.js
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
+ const UserRouter = require('./routes/UserRouter')app.use('/users', usersRouter);
+ app.use(UserRouter);
UserRouter.js
定義登錄路由(/user/login
),路由匹配成功,進入控制層處理。
// routes/UserRouter.js// 用戶路由
var express = require('express');
var router = express.Router();
const UserController = require('../controllers/UserController.js')
/* POST users listing. */
router.post('/user/login', UserController.login);module.exports = router;
UserController.js
控制層調用服務層,如果數據庫能通過用戶名和密碼查詢到該用戶則表明登錄成功,否則返回用戶名密碼不匹配
。
// controllers/UserController.jsconst UserService = require('../services/UserService')const UserModel = require('../models/UserModel')
const UserController = {login: async (req, res) => {// req.body - 例如 {"username":"pjl","password":"123456"}console.log('res.body', JSON.stringify(res.body))var result = await UserService.login(req.body)if(result.length === 0){res.send({code: '-1',error: '用戶名密碼不匹配'})}else{res.send({code: '0',error: ''})}}
}module.exports = UserController
UserService.js
模型層通過 model 查找數據庫
// services/UserService.jsconst UserModel = require('../models/UserModel.js')
const UserService = {login: async ({username, password}) => {return UserModel.find({username,password})}
}module.exports = UserService
UserModel.js
mongoose 通過 model 創建表。
// models/UserModel.js// model 與表一一對應
const mongoose = require('mongoose')
const Schema = mongoose.Schema
// 通過 schema 限制一下集合(表),否則什么都能傳入,太自由了不好
const Usertype = {username: String,password: String,// 性別gender: Number,// 頭像avatar: String,// 角色role: Number, // 管理員1,編輯2
}const UserModel = mongoose.model("user", new Schema(Usertype))
module.exports = UserModel
測試
這里筆者在 git bash 中使用?curl
(客戶端的url) 模擬登錄(post 用戶名+密碼):
Administrator@ ~/Desktop
$ curl -X POST -d 'username=pjl' -d 'password=123456' http://localhost:3000/user/login% Total % Received % Xferd Average Speed Time Time Time CurrentDload Upload Total Spent Left Speed
100 76 100 48 100 28 1425 831 --:--:-- --:--:-- --:--:-- 2533{"code":"-1","error":"用戶名密碼不匹配"}
由于 users 表中沒有數據,當然也就查詢不到(返回?{"code":"-1","error":"用戶名密碼不匹配"}
)。
筆者手動插入該條用戶信息:
> use pjl_db
switched to db pjl_db
> db.users.save({username: 'pjl', password: '123456'})
WriteResult({ "nInserted" : 1 })
再次登錄就能查詢到?{"code":"0","error":""}
:? ?
Administrator@ ~/Desktop
$ curl -X POST -d 'username=pjl' -d 'password=123456' http://localhost:3000/user/login% Total % Received % Xferd Average Speed Time Time Time CurrentDload Upload Total Spent Left Speed
100 51 100 23 100 28 1730 2106 --:--:-- --:--:-- --:--:-- 5100{"code":"0","error":""}