Persistence with MongoDB and Mongoose

https://github.com/azat-co/practicalnode/blob/master/chapter5/chapter5.md

学习mongodb的官方网站:

https://university.mongodb.com/ (免费课程,-> study guide,-> exam)

https://docs.mongodb.com/manual/tutorial/getting-started/

 

Mongodb node.js driver3.x版本的 guide:

http://mongodb.github.io/node-mongodb-native/3.1/upgrade-migration/main/

⚠️:本书上的案例使用的是2.2版本的代码,如open命令,在3.0驱动版已经删除。

 

Mongoose :https://mongoosejs.com/ (17000✨)

写MongoDb Validation, casting和business logic boilerplate是很烦人的drag/boring。

因此使用mongoose代替。

 


 

我真的喜欢使用MongoDB with Node。因为这个数据库有JavaScript interface, 并使用JSON-like data structure。

MongoDB是一种NoSQL database。

 

NoSQL databases (DBs), also called non-relational databases非关系型数据库。

more horizontally scalable,在横向上有更多的扩展性

better suited for distributed systems than traditional SQL,比传统的SQL更适合分布式的系统。

NoSQL DBs内置方法让数据复制和自定义查询语法。这被叫做反规范化denormalization.

NoSQL databases deal routinely with larger data sizes than traditional ones.

 通常非关系数据库处理比传统关系数据库更大的数据。

 

关键的区别:

NoSQL DBs是没有schema的

没有table,仅仅是一个有IDS的简单store。

大量数据类型是不储存在数据库(没有更多的ALTER table 查询);  它们被移动到app,或者object-relational mapping (ORM) levels--我们的案例,移动到Node.js代码。

作者说:

这些NoSQL最棒的优点!我可以快速地prototype prototyping和iterate (more git pushes!)
一旦我或多或少的完成,我能执行 implement schema和validation in Node.
This workflow allows me to not waste time early in the project lifecycle 
while still having the security at a more mature stage. 这种工作流程,让我不会在工程的早期浪费时间,同时在更成熟的阶段仍有安全。?

 

 

MongoDB

文档储存的非关系型数据库。

与之对比的是key-value类型的数据库如Redis, 以及wide-column store NoSQL databases。

NoSQL DBs汇总:http://nosql-database.org/

MongoDB是最成熟mature,dependable的NoSQL数据库。

 

另外,MongoDB有一个JavaScript交互interface!这非常棒!

因为现在无需在前端(browser JavaScript)和后端(Node.js)之间来回切换switch context,和数据库。

这是我最喜欢的功能!

 

开发MongoDB的公司是一个工业的领导者,提供了学习MongoDB的在线网站   (https://university.mongodb.com).

https://docs.mongodb.com/manual/tutorial/getting-started/

 

准备开始MongoBD和Node.js , 本章将讲如下章节:

  • Easy and proper installation of MongoDB
  • How to run the Mongo server
  • Data manipulation from the Mongo console
  • MongoDB shell in detail
  • Minimalistic native MongoDB driver for Node.js example
  • Main Mongoskin methods
  • Project: Storing Blog data in MongoDB with Mongoskin

 

 

Easy and Proper Installation of MongoDB

macOS使用HomeBrew安装。✅

$ brew install mongodb

 

或者从官网下载文件并配置它。http://www.mongodb.org/downloads

其他安装方式见本文后续说明和连接。

 

可选:

如果想在你的系统的任何位置使用MongoDB命令, 需要把mongoDB path增加到$PATH变量。

对应macOS,你需要open-system path file, 它在/etc/paths:

$ sudo vi /etc/paths

然后,在/etc/paths文件内增加下面的代码:

/usr/local/mongodb/bin

 

创建一个data文件夹;默认 MongoDB使用根目录的/data/db。 

//创建文件夹
$ sudo mkdir -p /data/db
//改变group和owner $ sudo chown `id
-u` /data/db

 

这个数据文件夹是你的本地数据库实例的存放位置~,它存放所有databases, documents,和on-all data.

如果你想储存数据在其他地方,可以指定path, 当你登陆你的database实例时,使用--dbpath选项给mongod命令.

 

各种安装方法官网:

"Install MongoDB on OS X" 


 

 

How to Run the Mongo Server

使用mongod命令来启动Mongo server

如果你手动安装,并没有连接位置到PATH, 去到你解包MongoDB的这个folder。那里有个bin文件夹。

输入:

$ ./bin/mongod

 

如果你像大多数开发者一样,喜欢在你的电脑任意位置输入mongod, 我猜测你把MongoDB bin文件夹放入了你的PATH环境变量中。所以如果你为MongoDB location增加$PATH, 就直接输入:

//任意位置
$ mongod

 ⚠️注意,在增加一个新的path给$PATH变量需要重启terminal window。

 

当teminal上出现

waiting for connections on port 27017

意味着MongoDB数据库server正在运行!Congratulations!

默认它监听  http://localhost:27017。

This is the host and port for the scripts and applications to access MongoDB.

In our Node.js code, we use 27017 for for the database and port 3000 for the server.

 


 

Data Manipulation from the Mongo Console

和Node.js REPL类似,MongoDB也有一个console/shell,作为database server实例的客户端。

这意味着,保持一个terminal window跑server,再开启一个tab/window用于console。

 

开启console的命令:

$ ./bin/mongo  //或mongo

 

当你成功地连接到数据库实例,之后你应该看到这些:

MongoDB shell version: 4.0.5
connecting to: test

 

看到光标>, 现在你在一个不同的环境内而不是zsh或bash。

你不能再执行shell命令了,所以不要使用node server.js或者mkdir。

但是你可以使用JavaScript,Node.js和一些特定的MongoDB代码。

例如,执行下面的命令,来保存一个document{a: 1}, 然后查询这个集合,来看看这个新创建的document:

> db.test.save({a: 1})
WriteResult({ "nInserted" : 1 })
> db.test.find()
{ "_id" : ObjectId("5c3c68cb8126284dea64ff02"), "a" : 1 }

 命令find(), save()就是字面的意思。

你需要在它们前面加上db.COLLECTION_NAME,  用你自己的名字替代COLLECTION_NAME

⚠️在masOS,使用control + C关闭process,

 

下章讨论最重要的MongoDB console commands:


  

MongoDB Console in Detail

MongoDB控制台语法是JavaScript。这非常棒!

最后一件我们想要的是学习一个新的复制的语言,如SQL。

db.test.find()//db是类名//test是collection name//find是方法名

 

 

常用的MongoDB console(shell) commands:

  • help: 打印可以使用的命令
  • show dbs: 打印数据库名字, prints the names of the databases on the database server to which the console is connected。默认是localhost:27017; 但是如果传递参数到mongo, 我们可以连接到任何远程实例remote instance。
  • admin   0.000GB
    config  0.000GB
    local   0.000GB
    test    0.000GB
  • use db_name:  switches to db_name
  • show collections: 打印所选的数据库中的collections的名单。
  • db.collection_name.find(query), 找到所有匹配查询的items。
  • db.collection_name.findOne(query)
  • ⚠️:find方法是Read Operations(CURD)
  • db.collection_name.insert(document)  除此之外还有insertOne, insertMany方法。
  • ⚠️:insert是create operations创建操作符
  • db.collection_name.save(docuemnt)
  • .
  • db.collection_name.update(query, {$set: data}): 先找到匹配query条件的items,然后更新。
  • ⚠️:update是Update Operations
  • db.collection_name.remove(query): 移除所有匹配query的items。
  • deleteOne(query), deleteMany(query)是3.0以后的版本的方法。
  • printjson(document); 打印变量document的值。
  • > db.messages.save({a: 1})
    > var a = db.messages.findOne()
    //a的值是{ "_id" : ObjectId("5c3c8112cdb3fb6e147fb614"), "a" : 1 }
    > printjson(a) > a.text = "hi" > printjson(a) > db.messages.save(a)

     

save()方法有2种工作形式:

如果有_id, 这个document会被更新,无论什么新属性被传入到save()。

> db.messages.save(a)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.messages.find()
{ "_id" : ObjectId("5c3c8112cdb3fb6e147fb614"), "a" : 1, "text" : "hi" }

 

如果没有_id,将插入一个新document并创建一个新的document ID?(ObjectId)in _id

> db.messages.save({b: 2})
WriteResult({ "nInserted" : 1 })
> db.messages.find()
{ "_id" : ObjectId("5c3c8112cdb3fb6e147fb614"), "a" : 1, "text" : "hi" }
{ "_id" : ObjectId("5c3c81e6cdb3fb6e147fb615"), "b" : 2 }

 

可见save(),类似upsert(update or insert)

 

这里只列出了最少的API。详细见:

"Overview—The MongoDB Interactive Shell" (http://www.mongodb.org/display/DOCS/Overview+-+The+MongoDB+Interactive+Shell).

 

使用MongoUI(git上是3年前的版本,比较旧)

一个基于web数据库的管理员界面,可以编辑,搜索,移除MongoUI documents,无需手输入命令了。

它会在默认的浏览器打开app,并连接到你的本地DB实例。

 

更好的桌面工具是Compass,下载地址:https://www.mongodb.com/products/compass

 

mongoimport命令

把一个JSON文件引进到一个数据库。就是把种子数据,存入到一个数据库。也可以使用(CSV,TSV文件)

mongoimport --db dbName --collection collectionName --file fileName.json --jsonArray

进入控制台后,显示默认的这些数据库:

> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
test    0.000GB

当前数据库是test, 显示这个数据库的collections:

> show collections
messages
test 

显示messages中的documents:

> db.messages.find()
{ "_id" : ObjectId("5c3c8112cdb3fb6e147fb614"), "a" : 1, "text" : "hi" }
{ "_id" : ObjectId("5c3c81e6cdb3fb6e147fb615"), "b" : 2 }

为了让Node和MongoDB一起工作,需要驱动driver。

 


 

Minimalistic Native MongoDB Driver for Node.js Example

为了演示Mongoskin的优势,我会展示如何使用 Node.js native driver for MongoDB,它比用Mongoskin多一些work。下面我创建一个基本的script,存储database.

 

注意MongoDB的语法在不同驱动driver环境,不同: (点击查看文档)

  • Mongo Shell: 控制台语言
  • Compass: 是一个GUI图形界面工具
  • Python
  • Node.js: 使用MongoDB Node.js Driver
  • Ruby

 

了解Node.js下的方法:

Collection.insertMany(docs, options, callback)  (点击查看Node.js MongoDB Driver API)

//insertMany方法会返回promise对象,用于then的连接。
db.collection('name').insertMany([{ item: "journal", qty: 25, status: "A",size: { h: 14, w: 21, uom: "cm" }, tags: [ "blank", "red" ] },{ item: "postcard", qty: 45, status: "A",size: { h: 10, w: 15.25, uom: "cm" }, tags: [ "blue" ] }
])
.then(function(result) {//处理结果
}) 

 

首先, 

npm init -y

然后,保存一个指定的版本为dependency。

$ npm install mongodb@2.2.33 -SE

(⚠️:下面的代码是使用node-native-driver的2.2版本,已经被抛弃,下面主要是看一下设计结构,和一些方法的使用,新的3.0版本见下面?。)

因此,我们先建立一个小的案例,它测试是否我们能够从一个Node.js脚本连接到一个本地的MongoDB实例, 并运行一系列的声明类似上个章节:

  1. 声明依赖
  2. 定义数据库host,port
  3. 建立一个数据库连接
  4. 创建一个数据库文档
  5. 输出一个新创建的document/object

文件名是:code/ch5/mongo-native-insert.js

首先引入mongodb。然后我们使用host,port连接到数据库。这是建立连接到数据库的方法之一,db变量把一个reference在一个具体的host和port, 给这个数据库。

const mongo = require('mongodb')
const dbHost = '127.0.0.1'
cibst dbPort = 27017
const {Db, Server} = mongo
const db = new Db('local', new Server(dbHost, dbPort), {safe: true})

使用db.open来建立连接:(version3.0被抛弃,改用MongoClient类的实例创建连接)

db.open((error, dbConnection) => {//写database相关的代码//console.log(util.inspect(db))
  console.log(db._state)db.close()
})

执行完后,db.close()  (version3.0,也需要关闭数据库, MongoClient类提供实例方法close)

 

为在MongoDB创建一个document,需要使用insert()方法。insert()方法在异步环境下使用,可以传递callback作为参数,否则insert()会返回一个promise对象用于异步的then()。

 

回调函数有error作为第一个参数。这叫做error-first pattern。

第二个参数是回调函数,它新创建一个document.

在控制台,我们无需有多个客户执行查询,所以在控制台方法是同步的,不是异步的。

但是,使用Node, 因为当我们等待数据库响应的时侯,我们想要去处理多个客户,所以用回调函数。

 

下面的完整的代码,是第五步:创建一个document。

⚠️:整个的insert代码是在open()的回调函数内的。因为insert()是异步的。

const mongo = require('mongodb')
const dbHost = '127.0.0.1'
const dbPort = 27017
const {Db, Server} = mongo
const db = new Db('local', new Server(dbHost, dbPort), {safe: true})//open函数是异步的
db.open((error, dbConnection) => {if (error) {console.error(error)return process.exit(1)}console.log('db state:', db._state)const item = {name: 'Azat'} dbConnection.collection('messages').insert(item, (error, document) => {if (error) {console.error(error)return process.exit(1)}console.info('created/inserted:', document)db.close()process.exit(0)})
})

 


 

process.exit([code]) 

这个方法告诉Node.js关闭进程,同步地提供一个状态码:

  • 默认exit code是0, 代表success。
  • 1代表failure。

调用这个方法会强制的关闭进程,即使还有异步的操作⌛️。

一般无需明确调用process.exit方法。 Node.js会在没有additional work pending in the event loop后退出.


 

除了mongo-native-insert.js脚本,我们还可以建立更多的方法,如 findOne()。

例如mogo-native.js脚本查询任意对象并修改它:

  1. Get one item from the message collection
  2. Print it
  3. Add a property text ,并赋值
  4. 保存这个item到message collection
const mongo = require('mongodb')
const dbHost = '127.0.0.1'
const dbPort = 27017
const {Db, Server} = mongo
const db = new Db('local', new Server(dbHost, dbPort), {safe: true})

 

然后,打开一个连接,并加上错误的测试:

db.open((error, dbConnection) => {if (error) {console.error(error)process.exit(1)}console.log('db state: ', db._state)

现在可以进行第一步:

从message collection那里得到一个item

使用findOne(query, options, callback)方法 

第一个参数是query条件, 它的类型是object,

第二个参数是Collection~resultCallback函数,它内部处理返回的document。

(如果没有callback传入,返回Promise)

  dbConnection.collection('messages').findOne({}, (error, item) => {if (error) {console.error(error)process.exit(1)}

 

Mongo Shell Method: db.collection.findOne(query, projection)

返回一个document,要满足collection/view的指定的查询标准,并返回符合标准的第一个document,没有则返回null。

参数 projection,用于限制返回的fields。

{ field1: <boolean>, field2: <boolean> ... }

如上面的区别:

methods在console和在Node.js唯一区别是在node,开发者必须使用回调函数(异步)。

 

然后,第二步,打印:

console.info('findONe:', item)

第三,四步骤:增加新的属性并save。

    item.text = 'hi'var id = item._id.toString() // we can store ID in a stringconsole.info('before saving: ', item)dbConnection.collection('messages').save(item, (error, document) => {if (error) {console.error(error)return process.exit(1)}console.info('save: ', document)

解释:

save(doc, options, callback)方法,类似update or insert

  • 如果提供了_id,则document被更新
  • 否则,创建一个新的document.
        dbConnection.collection('messages')  .find({_id: new mongo.ObjectID(id)}).toArray((error, documents) => {if (error) {console.error(error)return process.exit(1)}console.info('find: ', documents)db.close()process.exit(0)})})})
})

 

find(query)方法 -> {Cursor}

query参数类型是object. 

{Cursor} 一个Cursor实例(内部的类型,不能直接地实例化。)

创建一个cursor实例,用于迭代从MongoDB查询的结果。

 

toArray(callback) -> {Promise}

返回documents的array格式。Promise if no callback passed  

解释:

.find({_id: new mongo.ObjectID(id)})

为了再次核查保存的对象,我们使用了document ID和find()方法,这个方法返回一个cursor,然后我们使用toArray()提取standard JavaScript array。

 

最后,执行, 要先运行mongod service:

$ node mongo-native

 

⚠️???执行node  mongo-native-insert.js命令。提示❌:

TypeError: db.open is not a function

这是版本的问题,需要使用的Node.js驱动的mongoDB版本

$ npm install mongodb@2.2.33 -SE

 如果使用稳定的驱动driver版本:

npm i mongodb@stable//3.0.1
//执行node xxx.js会报告❌
db.open((error, dbConnection) => {^TypeError: db.open is not a functionat Object.<anonymous> (/Users/chent

 

原因

见2.2api:http://mongodb.github.io/node-mongodb-native/2.2/api/Db.html#open 

和3.0api:   http://mongodb.github.io/node-mongodb-native/3.0/api/Db.html#open

2.2的DB#open方法,在3.0被删除了。

 

另外官网的代码从2.2开始已经使用的是MongoClient建立连接 

?????????????????????重要!

http://mongodb.github.io/node-mongodb-native/3.1/quick-start/quick-start/

下面是建立连接并插入一个document的演示代码:

version3-insert.js

const MongoClient = require('mongodb').MongoClient;
const dbHost = '127.0.0.1'
const dbPort = 27017const assert = require('assert');
// Database Name
const dbName = 'myproject';
// Connection URL
const url = `mongodb://${dbHost}:${dbPort}`;
// Create a new MongoClient
const client = new MongoClient(url);// Use connect method to connect to the Server
client.connect(function(err) {assert.equal(null, err) //如果有err,就会报告❌console.log("Connected successfully to server"); //MongoClient#db方法,创建一个Db实例const db = client.db(dbName);// 使用insetMany()方法,如果myproject不存在,则同时创建myproject数据库和documents collection// 并插入一个document。 insertMany()方法,相当于create or update方法的判断的整合。insertDocuments(db, function() {//回调函数用于执行close,关闭db和它的连接。
    client.close()})
});const insertDocuments = function(db, myCallback) {// 得到或者创建一个document: (这里命名为documents)const collection = db.collection('documents');// Insert some documents, 
// insertMany()的回调函数,可以进行err的判断(assert,expect),发出提示信息,并调用传入的回调函数myCallback
collection.insertMany([{a : 12}, {b : 2}, {b : 3}], function(err, result) {console.log("Inserted 3 documents into the collection, \n", result);myCallback();}); }

 

$ node version3-insert.js
//得到结果:
Connected successfully to server
Inserted 3 documents into the collection

除此之外还有find, findOne, 等方法。

 


  

Main Mongoskin Methods(⚠️,先看文章末尾)

文章末尾有新的更新的内容,本章推荐使用mongoose。如果不看的话,前面的白学了,因为不实用!

Mongoskin 提供了更好的API。 一个wrapper,包裹node-mongodb-native。(1600?)过时。

除本书外,流行的驱动: 

mongoose:(17800✨)✅。

一个MongoDB对象 模型化工具,用于在异步的环境下工作!

https://github.com/Automattic/mongoose

文档https://mongoosejs.com/docs/index.html

 

数据验证非常重要,大多数MongoDb库都需要开发者创建他们自己的验证validation, 但是mongoose是例外! 

Mongoose有内置的数据验证。所以mongoose被大型的apps所推荐使用✅!

在Express.js上,可以下载验证模块如:node-validator,或者express-validator。

 


mongoose

简介

elegant mongodb object modeling for node.js。

const mongose = rquire('mongose')
mongoose.connect('mongodb://localhost:27017/test', {useNewUrlParser: true});//声明一个类
const Cat = mongoose.model('Cat', { name: String })
//实例化类
const kitty = new Cat({name: 'Zildjian'});
//save方法保存实例,并返回promise。
kitty.save().then(() => {console.log('meow')
})

 

Mongoose提供了直接的,基于schema的解决方案,来构造你的app data。

它包括内置的类型构件, 验证, 查询,业务逻辑勾子和更多的功能,开箱即用out of the box!

 

快速指导开始

建立连接

//先安装好MongoDb和Node.js
$ npm install mongoose
// getting-started.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

 

在脚本文件中引入mongoose,然后打开一个连接到test数据库,这是在本地运行的MongoDB实例(本地开启mongod服务器).

现在,我们有了一个等待连接,它会连接到正在本地运行的test数据库。

现在需要判断连接是成功还是失败,并给出❌提示:

var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {// we're connected!
});

 

db.once()一旦连接开始,就调用回调函数。为了简化,让我们假设所有的代码在这个回调函数内。

 

设计一个Schema:

在Mongoose中,一切都从Schema中取用。

var kittySchema = new mongoose.Schema({name: String
})

 

现在有了一个schema和一个属性name,类型是string。

 

下一步是把schema编译进一个 Model.

var Kitten = mongoose.model("Kitten", kittySchema)

 

一个Model就是一个类,我们用它来构建documents。

 

在这个案例,每个document都是一只kitten, 它的属性和行为会在schema内声明!

现在创建一只kitten:(创建一个实例)

var silence = new Kitten({name: 'Silence'})
console.log(silence.name)  //'Silence'

 

添加行为函数:如speak:

kittySchema.methods.speak = function() {var greeting = this.name? "Meow name is " + this.name: "I don't have a name"
}var Kitten = mongoose.model("Kitten", kittySchema)

 

添加到schema的methods属性中的函数,被编译到Model prototype, 并会被exposed 到每个document实例。

var fluffy = new Kitten({ name: 'fluffy'})
fluffy.speak();
// "Meow name is fluffy"

 

上面的操作是在视图层到control层,最后要存入MongoDB数据库的!使用save方法。

save(callback)

fluffy.save(function(err, fluffy) {if (err) return console.error(err)fluffy.speak();
})

 

如何显示所有的kittens?

使用find(callback)方法

Kitten.find(function(err, kittens) {if (err) return console.error(err);console.log(kittens);
})

 

如果想要搜索指定的document,使用:

//db.collection('Kitten').find({}, callback) 
Kitten.find({name: /^fluff/}, callback)

 

这和原生的Node.js mongoDb driver的方法类似。collection就是mongoose中模型构建的“类”。

并且,都可以使用regexp表达式。

 

总结:创建一个mongoose model有2个步骤(部分):

  1. 首先创建 a schema ,
  2. 然后创建 a model .

 

 


 

Project: Storing Blog Data in MongoDB with mongoose(改原文使用mongoskip弃用)

本案例使用第2章建立的hello-world app

步骤:

1.增加种子文件 

2.写Mocha tests

3.增加persistence

 

第一步:添加seed

在程序根目录创建db文件夹,然后在terminal输入:

mongoimport --db blog --collection users --file ./db/users.json --jsonArray
mongoimport --db blog --collection articles --file ./db/articles.json --jsonArray

⚠️参数--jsonArray:  可以引入的文件内是array格式的数据,默认这种格式不能被引进,除非加上这个选项。

或者把?的代码存入一个seed.js脚本,脚本放在程序根目录文件夹后,执行:

sudo bash seed.sh

 

创建db/users.json

[{"email": "hi@azat.co","admin": true,"password": "1"
}]

创建 db/articles.json

[ {"title": "Node is a movement","slug": "node-movement","published": true,"text": "In one random deployment, it is often assumed that the number of scattered sensors are more than that required by the critical sensor density. Otherwise, complete area coverage may not be guaranteed in this deployment, and some coverage holes may exist. Besides using more sensors to improve coverage, mobile sensor nodes can be used to improve network coverage..."}, {"title": "Express.js Experience","slug": "express-experience","text": "Work in progress","published": false}, {"title": "Node.js FUNdamentals: A Concise Overview of The Main Concepts","slug": "node-fundamentals","published": true,"text": "Node.js is a highly efficient and scalable nonblocking I/O platform that was built on top of a Google Chrome V8 engine and its ECMAScript. This means that most front-end JavaScript (another implementation of ECMAScript) objects, functions, and methods are available in Node.js. Please refer to JavaScript FUNdamentals if you need a refresher on JS-specific basics."}
]

 

第2步:用mocha, chai写测试

需要安装mocha, chai。

创建一个测试文件:tests/index.js

const boot = require('../app').boot
const shutdown = require('../app').shutdown
const port = require('../app').port
const axios = require('axios')   
const { expect } = require('chai')

const seedArticles = require('../db/articles.json')

使用axios发收请求响应。使用chai.expect方法,并引入数据。

首先,补上views的html代码,然后再开始测试。

const boot = require('../app').boot
const shutdown = require('../app').shutdown
const port = require('../app').port
const axios = require('axios')
const expect = require('chai').expect
const boot = require('../app').boot
const shutdown = require('../app').shutdown
const port = require('../app').port
const axios = require('axios')
const expect = require('chai').expectconst seedArticles = require('../db/articles.json')describe('server', () => {before(() => {boot()})describe('homepage', () => {it('should respond to GET', (done) => {axios.get(`http://localhost:${port}`).then(function(response) {expect(response.status).to.equal(200)}).catch((err) => {console.log(err)}).then(done)  //promise的写法,告诉mocha这个it的test完全结束。})it('should contain posts', (done) => {axios.get(`http://localhost:${port}`).then((response) => {seedArticles.forEach((item, index, list) => {// ⚠️,respnse.text目前是undefined!,没有test这个属性,有data属性。if (item.published) {expect(response.data).to.include(`<h2><a href="/articles/${item.slug}">${item.title}`)} else {expect(response.data).not.to.include(`<h2><a href="/articles/${item.slug}">${item.title}`)}})}).catch((err) => {console.log("Throw err: ", err)}).then(done)})})after(() => {shutdown()})
})

在第一个describe内嵌套一个describle:

//和上面的describe不能一起运行,否则连接不到url。 原因未找到!!!describe('article page', () => {it('should display text or 401', (done) => {let n = seedArticles.lengthseedArticles.forEach((item, index, list) => {// console.log(`http://localhost:${port}/articles/${seedArticles[index].slug}`)
        axios.get(`http://localhost:${port}/articles/${seedArticles[index].slug}`).then((response) => {console.log("success!!", seedArticles[index].slug)if (item.published) {expect(response.data).to.include(seedArticles[index].text)} else {expect(response).to.existexpect(response.data).to.be(401)}}).catch((error) => {console.log("error!!!", seedArticles[index].slug)console.log(error.message)})})done()   //这里⚠️,axios异步是在一个循环内部执行多次,因此done放在这个it代码最后一行 
}) })

 

 

done()方法是mocha检测异步函数测试结束的回调函数!具体见文档搜索done()

⚠️原文response.text是❌的,应该改成response.data 

 

在完成下一章重写app.js后,执行mocha test

提示❌:最后检查app.js发现

require('./routes')返回一个空对象{}, 这是语法糖的原因。解决方法:

在routes/index.js文件,引入article.js和user.js

exports.article = require('./article')
exports.user = require('./user')exports.index = (req, res, next) => {req.collections.articles.find({published: true}, {sort: {_id: -1}}).toArray((error, articles) => {if (error) return next(error)res.render('index', {articles: articles})})
}

 

 cursor.sort(sort)
// 参数sort是一个document对象,格式是{field: value}
// 升降分类: 值为1是升序, -1为降序。
// 复杂的排序方式见MongoDB文档说明。

如此require('./routes')会返回一个对象:

{article: {show: [Function],list: [Function],add: [Function],edit: [Function],del: [Function],postArticle: [Function],admin: [Function]},user: {list: [Function],login: [Function],logout: [Function],authenticate: [Function]},index: [Function]
}

 

再次执行mocha test,仍然❌

Error: Route.get() requires callback functions but got a [object Undefined]

执行:

node inspect app.js
//app.js内可以加上debugger

发现:第76行❌

76 app.get('/post', routes.article.post) 

后面都报告错误,注释掉76-86行,则正常。

!!这是因为我没有安装pug视图引擎,也没有写view相关页面代码的原因。

在完成后续步骤后,再执行分别对2个describe块执行测试,成功。

问题:执行完测试,不能自动回到terminal, 需要ctrl + C返回terminal. 

 

第三步:Adding Persistence

修改app.js:

const express = require('express')
const http = require('http')
const path = require('path')
const ejs = require('ejs')  //使用html模版
const routes = require('./routes')  //引入routes文件下的脚本
const mongoose = require('mongoose')//引入mongoose
const dbUrl = process.env.MONGOHQ_URL || 'mongodb://127.0.0.1:27017/blog'mongoose.connect(dbUrl)
const db = mongoose.connection; //得到默认的连接的数据库数据。
db.on('error', console.error.bind(console, 'connection error:'));
// db.once('open', function() {
//   // we're connected!
// });
//从数据库取出数据,存入一个变量collections
const collections = {articles: db.collection('articles'),users: db.collection('users')
}

 

添加以下声明,用于Express.js中间件模块:

// 添加中间件:
const logger = require('morgan')  //用于logging
const errorHandler = require('errorhandler') //错误处理
const bodyParser = require('body-parser')    //解析进入的HTTP请求 bodies
const methodOverride = require('method-override') //to support clients that do not have all HTTP methods

 

// 创建Express实例, 
let app = express()
app.locals.appTitle = 'blog-express'

  

这一步?:通过req对象,在每个Express.js route内,引入MongoDB collections:

// decorator pattern:
//通过req对象, expose mongoose/MongoDB collections 在每个Express.js route内
app.use((req, res, next) => {if (!collections.articles || !collections.users) {return next(new Error("No collections."))}req.collections = collectionsreturn next()
})

 

上面的代码叫做decorator pattern

一种Node的设计模式,可以参考在线课程:Node Patterns: From Callbacks to Observer. (收费!)

这种设计思路是让req.collections在后续的中间件和路径内可以使用。

⚠️使用next()在中间件内部,否则请求会停滞。


 

插入的知识点:

app.use([path,] callback [, callback...])

Mounts the specified middleware function(s) at the specified path:

当请求的路径匹配path时,中间件函数被执行

回调函数,可以是:

  • 一个中间件函数
  • 系列的中间件函数
  • 数组形式的中间件函数[callback, callback...]
  • 上面的组合形式

例子,按顺序执行中间件函数:

// this middleware will not allow the request to go beyond it
app.use(function(req, res, next) {res.send('Hello World');
});// requests will never reach this route
app.get('/', function (req, res) {res.send('Welcome');
});

 


 

 

继续,下一步:

在第2章已经写好这些代码:

定义Express设置:建立端口,模版的路径位置,使用什么模版引擎。

// 定义Express设置:建立端口,模版的路径位置,使用什么模版引擎。
app.set('appName', "Blog")
app.set('port', process.env.PORT || 3000)
app.set('views', path.join(__dirname, 'views'))app.engine('html', ejs.__express)
app.set('view engine', 'html')

 

现在,是对你已经熟悉的常用功能: 请求登陆的中间件,转化JSON input, 使用Stylus for CSS,和serving of static content。

使用app.user()插入这些中间件到Express app。

app.use(logger('dev'))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: true}))
app.use(methodOverride())
app.use(require('stylus').middleware(path.join(__dirname, 'public')))
app.use(express.static(path.join(__dirname, 'public')))

 

使用标准的Express.js error handler, 之前使用require()引入的。

errorhandler是一个(300✨)的用于开发阶段的错误处理中间件。

if (app.get('env') === 'development') {app.use(errorHandler('dev'))
}

 

下一步,app.js会处理服务器路径。路径连接到views:

app.get('/', routes.index)app.get('/login', routes.user.login)
app.post('/login', routes.user.authenticate)
app.get('/logout', routes.user.logout)app.get('/admin', routes.article.admin)
app.get('/post', routes.article.post)
app.post('/post', routes.article.postArticle)app.get('/articles/:slug', routes.article.show)

 

 EEST api routes大多数时候用在admin page: 

// REST API routes
// 用在adimn page。 That's where our fancy AJAX browser JavaScript will need them.
// 他们使用GET,POST, PUT, DELETE方法,不会渲染网易模版,只输出JSON数据。
app.get('/api/articles', routes.article.list)
app.post('/api/articles', routes.article.add)
app.put('/api/articles/:id', routes.article.edit)
app.delete('/api/articles/:id', routes.article.del)

 

加上一个404route, 用于用户键入了错误的URL的提示:》

app.all("*", (req, res) => {res.status(404).send()
})

 

创建一个http.Server的实例

// 创建一个http.Server的实例
// 第3章的代码:
// 根据是否在terminal执行node app.js来决定:
//   1.执行server.listen(),
//   2.出口module到缓存。
const server = http.createServer(app)const boot = () => {server.listen(app.get('port'), () => {console.info(`Express server listening on port ${app.get('port')}`)console.log(`Express server listening on port ${app.get('port')}`)})
}
const shutdown = () => {server.close()
}if ( require.main === module ) {boot()
} else {console.log('Running app as a module')exports.boot = bootexports.shutdown = shutdownexports.port = app.get('port')
}

完成app.js✅

 

Routes module:

下一步添加index.jsarticle.js, and user.js文件到routes文件夹内。 

 

user.js

用于验证(见第6章)

The method for the GET /users route, which should return a list of existing users (which we'll implement later)

export.list = (req, res, next) => {res.send('respond with a resource')
}    

 这个为GET /login page route的方法,会渲染login form:(login.html或.pug)

exports.login = (req, res, next) => {res.render('login')
}

这个方法最终会destroy the session并返回到home page:

exports.logout = (req, res, next) => {res.redirect('/')
} 

这个为POST /authenticate route的方法,会处理验证并返回到admin page:

exports.authenticate = (req, res, next) => {res.redirect('/admin')
}

上面是 user.js的完全代码,一个输出4个方法。

 

article.js

现在最主要的database行为发生在article.js routes。

GET article page (异步函数的2种写法),查询一个article的,进入一个article的详情页面:

传统:推荐✅,因为文档里回调函数的参数已经提供好了。

exports.show = (req, res, next) => {if (!req.params.slug) return next(new Error('No article slug.'))req.collections.articles.findOne({slug: req.params.slug}, (error, article) => {if (error) return next(error)if (!article.published) return res.status(401).send()res.render('article', article)})
}

 使用promise,⚠️,如果使用promise,回调参数只有article

exports.show = (req, res, next) => {if (!req.params.slug) {return next(new Error('No article slug.'))}req.collections.articles.findOne({slug: req.params.slug}).then((article) => {if (!article.published) return res.status(401).send()res.render('article', article)})
}

 

下面4个函数,用于API 

 GET /api/articles API route(在admin page使用), 在网址输入框输入:

http://localhost:3000/api/articles会得到JSON格式的数据。

exports.list = (req, res, next) => {req.collections.articles.find({}).toArray((error, articles) => {if (error) return next(error)res.send({articles: articles})})
}

 

POST /api/articles API routes (used in the admin page),

exports.add = (req, res, next) => {if (!req.body.article) return next(new Error('No artilce payload'))let article = req.body.articlearticle.published = falsereq.collections.articles.insert(article, (error, articleResponse) => {if (error) {return next(error)}res.send(articleResponse)})
}

req.body得到提交的key/value数据(具体见文档,或者文章底部的解释。)

 

PUT /api/articles/:id API(admin页面):

(文章使用updateById方法,我改成updateOne方法)

exports.edit = (req, res, next) => {if (!req.params.id) return next(new Error('No article id'))// 不知道改的对不对??
  req.collections.articles.updateOne({"_id": req.params.id},{$set: req.body.article},(error, result) => {if (error) return next(error)res.send({affectedCount: result.modifiedCount})})
}

 DELETE /api/articles/:id API route

exports.del = (req, res, next) => {if (!req.params.id) return next(new Error('No article ID.'))req.collections.articles.deleteOne({"_id": req.params.id},(error, result) => {if (error) return next(error)res.send({affectedCount: result.deletedCount})})
}

 

 

后续的函数:

其实routes的脚本,相当于Rails中的controller的功能,和数据库交互。还包括一些model的功能,如一些验证。

// get article POST page 
// http://localhost:3000/post, 进入文章创建页面,创建一篇新文章。
exports.post = (req, res, next) => {if (!req.body.title) {return res.render('post')} }exports.postArticle = (req, res, next) => {// 表格必须都填入信息,否则渲染回post,并附带一个错误信息:if (!req.body.title || !req.body.slug || !req.body.text) {return res.render('post', {error: 'Fill title, slug and text.'})}const article = {title: req.body.title,slug: req.body.slug,text: req.body.text,published: false}req.collections.articles.insert(article, (error, articleResponse) => {if (error) return next(error)res.render('post', {message: 'Article was added. Publish it on Admin page'})}) }// Get /admin 页面,取数据 exports.admin = (req, res, next) => {req.collections.articles.find({}, {sort: {_id: -1}}).toArray((error, articles) => {if (error) return next(error)res.render('admin', {articles: articles})}) } 

 

最后

view的template: 下载代码,⚠️模版使用pug,需要下载并在app.js内培训app.set('view engine', 'pug')

下载public文件夹下的文件:其中包括bootstrap, 和jquery

教程使用的bootstrap已经过期。去官网下载一个相近的版本3.4.0,用编译好的。解压后复制到public/css。

jquery直接复制上面的连接。

补充 style.css, blog.js。

 

之后进行测试,

然后,打开本地数据库,mongod, 运行在localhost和使用默认的端口27017。

然后,启动app.js脚本: node app.js, 即可访问

备注:

❌:

admin页面,ation的delete和update行为。不能写入数据库! 

// PUT /api/articles/:id API route
exports.edit = (req, res, next) => {if (!req.params.id) return next(new Error('No article id'))console.log("edit!!!")req.collections.articles.updateOne( {_id: req.params.id},{$set: req.body.article},(error, result) => {console.log("callback, edit!!!")if (error) return next(error)res.send()})
}
// DELETE /api/articles/:id API route
exports.del = (req, res, next) => {if (!req.params.id) return next(new Error('No article ID.'))req.collections.articles.deleteOne({_id: req.params.id},(error, result) => {if (error) return next(error)res.send({affectedCount: result.deletedCount})})
}

 

花费了2个多小时问题未找到。暂时搁置!

req.collections.articles的值是一个复杂的对象,来自app.js脚本中:

const mongoose = require('mongoose')
const dbUrl = process.env.MONGOHQ_URL || 'mongodb://127.0.0.1:27017/blog'mongoose.connect(dbUrl)
const db = mongoose.connection;const collections = {articles: db.collection('articles'),users: db.collection('users')
}let app = express()app.use((req, res, next) => {if (!collections.articles || !collections.users) {return next(new Error("No collections."))} req.collections = collectionsreturn next()
})

 

req.collections.articles可以使用find方法。

猜测: 它连接到了mongoDB/mongoose。

但是我尝试下面的脚本❌,

db.collection("articles").find({})返回一个undefined。很奇怪

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/blog', {useNewUrlParser: true});var db = mongoose.connection;
console.log(db.collection("articles").find())

 

 

奇怪?db.collections无效?返回一个{}空hash。方法定义 :A hash of the collections associated with this connection

 



 

插入知识点Express的Response

res对象:代表一个Express app当得到一个http请求后发送的http响应。

为了方便使用res的写法,可以根据回调函数自己定义的参数名字写。

res对象是Node原生对象的升级版,它支持所有的内建fields和方法。

 

res.send([body])

发送http响应。

参数body,可以是Buffer object, string, object, array。

//例子
res.send('<p>some html</p>')
res.status(404).send({error: 'something blew up'})

 

res.render(view,[locals], [callback])

渲染一个view, 并发送这个渲染的HTML string给客户端。

参数:

view: 一个字符串,渲染视图的路径,可以是绝对或相对路径。

locals:一个对象,传入view的数据。

callback: function(err, html){} 。如果提供了回调函数,就不会自动执行响应,需要使用res.send()

 

res.redirect([status,] path)

res.redirect('/foo/bar');
res.redirect('http://example.com');
res.redirect(301, 'http://example.com');
res.redirect('../login');

  

req.params

This property is an object containing properties mapped to the named route “parameters”.

一个对象:包含请求的URl的参数/值对儿。

Route parameters

Route path: /users/:userId/books/:bookId
Request URL: http://localhost:3000/users/34/books/8989
req.params: { "userId": "34", "bookId": "8989" }

 

req.body

在request的body内包括了提交的key/value data.

默认是undefined。当你使用body-parsing中间件时,如body-parser, multer

 

body-parser

$ npm install body-parser//API
var bodyParser = require('body-parser')

// 在程序的app.js脚本内,express的实例app
//for parsing application/json
app.use(bodyParser.json())
// for parsing application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({extended: true}))

 

db.collection_name.drop()

删除这个collection


 

 

mongoose

https://mongoosejs.com/docs/api.html#connection_Connection-dropCollection

 

Connection.prototype.dropCollection()

//start.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test', {useNewUrlParser: true});var db = mongoose.connection;
//假设test数据库内有messages collection,下面的命令会彻底删除这个collection
db.dropCollection("messages")

 

类似命令

Connection.prototype.dropDatabase() 返回Promise

 

Connection.prototype.collection(),方法

console.log(db.collection("inventory")) 返回collection实例。 
Not typically needed by applications. Just talk to your collection through your model.
对程序来说没什么用途,只是通过你的model告诉你的collection。

 

转载于:https://www.cnblogs.com/chentianwei/p/10268346.html

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

相关文章

  1. Node.js与Golang使用感受与小结【三】--JS异步流程控制(序列模式、并发模式、有限并发模式)...

    十一、Node.js异步流程控制(序列模式、并发模式、有限并发模式)Javascript在基本语法上与其它大部份C派生的语言没有太多区别,你可能很容易从其它语言过度到Javascript。很多从其它语言转到Javascript来的用户,在用一段时间之后很可能对这门语言又爱又恨,特别是对于异步流…...

    2024/4/24 13:29:59
  2. 【Nodejs开发技术】浅谈Nodejs应用的主文件index.js的组成部分

    前言Node妹子的问世,着实让我们前端攻城狮兴奋了一把,尤其本屌听说Javascript可以写服务端后,兴奋的像是看到了二次元萝莉的胖子...(●◡●)。呃哼...YY先到这里,原谅本屌是个二次元萝莉控。正文想谈好对象,并不是要new一个就行了,必须要先认识人家后慢慢的才可以上手,然…...

    2024/4/24 13:29:58
  3. NodeJS综述

    heX - 网易有道出品的Using HTML5 and Node.js to Build Desktop Application http://hex.youdao.com/zh-cn/index.html heX 最初的目标是,采用纯前端 (HTML,CSS,JavaScript) 的方式开发客户端软件,解决传统桌面开发中大量繁琐的 UI 工作。以实现跨平台 (Windows,OS X,…...

    2024/4/28 15:50:37
  4. 如何使用Node.js发邮件?

    电子邮件是—种用电子手段提供信息交换的通信方式,是互联网应用最广的服务。通过网络的电子邮件系统,用户可以以非常低廉的价格(不管发送到哪里,都只需负担网费)、非常快速的方式(几秒钟之内可以发送到世界上任何指定的目的地),与世界上任何一个角落的网络用户联系。在…...

    2024/4/24 13:29:58
  5. 蚂蚁金服 Node.js 开荒史 - 摸爬滚打才不负功名尘土

    做最好的今天,回顾最好的昨天,迎接最美好的明天。作者 | 蚂蚁金服体验技术部来源 | https://zhuanlan.zhihu.com/p/84176287?utm_source=wechat_session&utm_medium=social&s_r=0楔子蚂蚁金服体验技术部,是阿里巴巴经济体内,一支以用户体验、大前端和创新产品为核…...

    2024/4/24 13:29:57
  6. Node.js实现网游服务器高性能和可扩展

    随着Node.js的不断发展与壮大,应用范围也越来越广泛,从传统的企业应用,到互联网使用,再到云计算的发展,它的身影也是随处可见。当然,它的受欢迎程度能在短时间内得到这么快的发展,除却与其本身的事件模型及V8的性能优化等一系列特性有关之外,还和国内外很多互联网公司的…...

    2024/4/24 13:29:56
  7. (Pomelo系列入门教材)Node.js之网游服务器实践

    随着Node.js的不断发展与壮大,应用范围也越来越广泛,从传统的企业应用,到互联网使用,再到云计算的发展,它的身影也是随处可见。当然,它的受欢迎程度能在短时间内得到这么快的发展,除却与其本身的事件模型及V8的性能优化等一系列特性有关之外,还和国内外很多互联网公司的…...

    2024/4/24 9:30:32
  8. Node.js学习 - 模块系统

    模块化概述 Javascript不是一种模块化编程语言,它不支持“类”(class),更遑论“模块”(module)了,随着前端发展对模块需求越来越大,模块也经历了从最初的简单模块写法到AMD和CMD规范的出现,再到ES6发布,目前已经可以很方便的在Javascript中使用“类”和“模块”了。 …...

    2024/4/24 13:29:52
  9. node.js爬虫01

    转载:http://blog.csdn.net/kissliux/article/details/19560603Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台, 用来方便地搭建快速的, 易于扩展的网络应用 Node.js 借助事件驱动, 非阻塞 I/O 模型变得轻量和高效, 非常适合 run across distributed devices …...

    2024/4/24 13:29:51
  10. Node.js的一些缺点分析(转)

    身为重度Node.js用户,我也来说说我的理解吧。计算机语言和技术平台很多时候不是谁更好谁更差的问题,而是谁更合适的问题,这里我们从语言和生态系统两个维度来坐下简要的对比。语言的对比JavaScript和Java从语言的设计上来说,虽然都含有Java但是是完全不同的两种语言,简单的…...

    2024/5/2 20:02:12
  11. 【Node.js】Nodejs学习路线图

    前言 用Nodejs已经1年有余,陆陆续续写了48篇关于Nodejs的博客文章,用过的包有上百个。和所有人一样,我也从Web开发开始,然后到包管 理,再到应用系统的开发,最后开源自己的Nodejs项目。一路走来,Nodejs已经成为我做Web项目的标配。我非常愿意把原Java、PHP的 Web系统向No…...

    2024/4/24 13:29:49
  12. 网易云音乐NodeJSAPI环境搭建

    1.首先下载Git工具,我这里使用的是msysgit(http://msysgit.github.io/)2.然后按照网易云音乐的官网API的安装步骤进行(https://binaryify.github.io/NeteaseCloudMusicApi/#/?id=%E5%AE%89%E8%A3%85)3.遇到ssh key不存在时,可参考https://www.cnblogs.com/wmr95/p/78528…...

    2024/4/15 3:55:58
  13. nodejs能做什么

    Node.JS 学习路线图从零开始nodejs系列文章, 将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮 助我…...

    2024/5/2 12:15:56
  14. Node.js入门(含NVM、NPM、NVM的安装)-(转载)

    Node.js的介绍 引擎 引擎的特性: JS的内核即引擎。因为引擎有以下特性: (1)转化的作用:汽油柴油等等->动能模板+数据--->页面js引擎:js 代码--->机器码\字节码(2)移植性。 有哪些引擎:备注:Node是用V8引擎去解析 js,此时,我们不用去考虑浏览器的兼容性问题…...

    2024/4/15 3:55:55
  15. [webGL学习]基于three.js构建WebGL实例第一讲

    我们主要做些的基本功能:创建一个场景,相机,渲染器,控制器(OrbitControls)。我们也将创建简单的定向光,加上一些对象(不同的几何形状)的阴影。为了使事情更快,我们决定采取一个最流行的WebGL框架——three.js。为什么使用three.js? 事实上,它是开源的JavaScript框架…...

    2024/4/18 5:27:20
  16. node.js 概述与安装以及环境搭建

    Nodejs介绍简单的说 Node.js 就是运行在服务端的 JavaScrip(编写高性能网络服务器的JavaScript工具包(用js开发服务端程序))JS是脚本语言,脚本语言都需要一个解析器才能运行。对于写在HTML页面里的JS,浏览器充当了解析器的角色。而对于需要独立运行的JS,NodeJS就是一个解…...

    2024/4/24 13:29:48
  17. 深入浅出Node.js游戏服务器开发--分布式聊天服务器搭建

    游戏服务器概述 没开发过游戏的人会觉得游戏服务器是很神秘的东西。但事实上它并不比web服务器复杂,无非是给客户端提供网络请求服务,本质上它只是基于长连接的socket服务器。当然在逻辑复杂性、消息量、实时性方面有更高的要求。 相关厂商内容 国内最大的Python应用——Sohu…...

    2024/5/2 7:58:05
  18. win10正确食用网易云灰色歌曲攻略

    win10正确食用网易云音乐灰色歌曲攻略 下载相关文件 相关资源百度网盘下载链接,提取码2333 解压文件顺便验证灰色下载文件后,解压缩到任意位置 打开网易云,搜索杰伦,伤心灰色安装node.js 解压后的文件中有一个node-v10.15.3-x64.msi安装软件,点击next安装即可。最好选择默…...

    2024/5/2 16:28:48
  19. Node.js相关资料网址

    Node的官网 http://nodejs.org/ IBM阐述Node.js是什么 http://www.ibm.com/developerworks/cn/opensource/os-nodejs/index.html?ca=drs oschina上面列出了很多Node.js的扩展 http://www.oschina.net/project/tag/334/nodejs-extends 一个VS2012开发Node.js项目的插件 https:/…...

    2024/5/2 17:52:44
  20. 从零开始用node.js爬虫

    现在互联网上python培训班广告铺天盖地,与之有着紧密联系的爬虫一词对于我这样一个新手来说也是耳熟能详。本学期学院开设了web编程课,第一次实验项目的主要内容也是爬虫,不过是用node.js来实现,这对于没有接触过JavaScript的我来说是又新奇又富有挑战呀。 本次实验的内容是…...

    2024/5/2 12:33:21

最新文章

  1. Reactor模型详解

    目录 1.概述 2.Single Reactor 3.muduo库的Multiple Reactors模型如下 1.概述 维基百科对Reactor模型的解释 The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs.…...

    2024/5/5 11:12:50
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. 汽车统一诊断服务UDS协议参考文章和视频

    UDS基础知识介绍-电子发烧友网 【图解UDS】UDS汽车诊断标准协议&#xff08;ISO14229&#xff09;带你入门到精通-有驾 车载测试之UDS诊断协议9&#xff1a;0x27服务&#xff1a;解锁ECU_哔哩哔哩_bilibili UDS诊断的22服务&#xff08;DID读取数据&#xff09;和2E服务&…...

    2024/5/5 4:34:45
  4. 【干货】零售商的商品规划策略

    商品规划&#xff0c;无疑是零售业的生命之源&#xff0c;是推动业务腾飞的强大引擎。一个精心策划的商品规划策略&#xff0c;不仅能帮助零售商在激烈的市场竞争中稳固立足&#xff0c;更能精准捕捉客户需求&#xff0c;实现利润最大化。以下&#xff0c;我们将深入探讨零售商…...

    2024/5/1 13:01:46
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/4 23:54:56
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/4 23:54:56
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/4 23:55:17
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/4 23:55:16
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/4 18:20:48
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/4 23:55:06
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/4 23:55:01
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57