revert: chinese curriculum updates break structure (#39416)
This commit is contained in:
committed by
GitHub
parent
b1da66b80d
commit
38dee5b577
@@ -1,20 +1,17 @@
|
||||
---
|
||||
id: 587d7fb9367417b2b2512c12
|
||||
title: Chain Search Query Helpers to Narrow Search Results
|
||||
localeTitle: 链搜索查询帮助缩小搜索结果
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301533
|
||||
localeTitle: 通过链搜索查询帮助器缩小搜索结果
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
如果不给 <code>Model.find()</code> (或者别的搜索方法)传递回调函数,作为的最后一个参数, 则不执行查询。你可以将查询存储在变量中供以后使用,这类对象可以使用链接语法构建查询。 当你最终链接 <code>.exec()</code> 方法时,将执行实际的数据库操作。最后将回调传递给 <code>exec()</code> 方法。 有很多的查询助手, 这里我们使用最 '著名' 的一种。
|
||||
<section id='description'> <code>0</code>如果未将回调作为Model.find()(或其他搜索方法)的最后一个参数传递,则不执行查询。您可以将查询存储在变量中以供以后使用。这种对象使您可以使用链接语法构建查询。当您最终链接方法.exec()时,将执行实际的数据库搜索。将回调传递给最后一个方法。有很多查询助手,这里我们将使用最“着名”的助手。 <code>0</code>找到的人喜欢“burrito”。按名称对它们进行排序,将结果限制为两个文档,并隐藏它们的年龄。链.find(),. sort(),. limit(),。select(),然后是.exec()。将done(错误,数据)回调传递给exec()。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
找到喜欢 "burrito" 的人,按照名字排序, 限制结果为两个 document, 并且隐藏他们的年龄。 使用链路 <code>.find()</code>, <code>.sort()</code>, <code>.limit()</code>, <code>.select()</code>, 然后 <code>.exec()</code>。将 <code>done(err, data)</code> 回调传给 <code>exec()</code> 方法。
|
||||
<section id='instructions'>
|
||||
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -22,7 +19,7 @@ localeTitle: 通过链搜索查询帮助器缩小搜索结果
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: Chaining query helpers should succeed
|
||||
- text: 链接查询助手应该成功
|
||||
testString: 'getUserInput => $.ajax({url: getUserInput(''url'') + ''/_api/query-tools'', type: ''POST'', contentType:''application/json'', data: JSON.stringify([{name: ''Pablo'', age: 26, favoriteFoods: [''burrito'', ''hot-dog'']}, {name: ''Bob'', age: 23, favoriteFoods: [''pizza'', ''nachos'']}, {name: ''Ashley'', age: 32, favoriteFoods: [''steak'', ''burrito'']}, {name: ''Mario'', age: 51, favoriteFoods: [''burrito'', ''prosciutto'']} ]) }).then(data => { assert.isArray(data, ''the response should be an Array''); assert.equal(data.length, 2, ''the data array length is not what expected''); assert.notProperty(data[0], ''age'', ''The returned first item has too many properties''); assert.equal(data[0].name, ''Ashley'', ''The returned first item name is not what expected''); assert.notProperty(data[1], ''age'', ''The returned second item has too many properties''); assert.equal(data[1].name, ''Mario'', ''The returned second item name is not what expected'');}, xhr => { throw new Error(xhr.responseText); })'
|
||||
|
||||
```
|
||||
@@ -38,11 +35,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -1,61 +1,29 @@
|
||||
---
|
||||
id: 587d7fb6367417b2b2512c07
|
||||
title: Create a Model
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
localeTitle: 创建一个模型
|
||||
challengeType: 2
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
<b>C</b>RUD 之 - CREATE 创建
|
||||
|
||||
首先,我们需要一个 Schema,每一个 Schema 对应一个 MongoDB collection,并且在那个 collection 里面定义 documents 的模型。
|
||||
Schemas 是 Models 的构建块。它们可以嵌套来创建复杂的模型,但是这里,我们只学习简单的用法。
|
||||
Model 可以被实例化,实例化后的对象称为 documents。
|
||||
|
||||
注意: Glitch 是一个真实的服务,并且通过 handler 函数和 db 进行交互。 这些函数通过一些事件去触发(例如:有人从终端调用了你的 API),我们在这些练习中遵循同样的方法。 比如,我们在完成 nserting、searching、updating 或者 deleting 这样的异步操作后接着回调<code>done()</code>函数。它遵循 Node 的惯例,需要在 success 时回调<code>done(null, data)</code>,在 error 时回调<code>done(err)</code>。
|
||||
Warning - 当与远程服务器交互时可能发生错误!
|
||||
|
||||
```js
|
||||
/* Example */
|
||||
|
||||
var someFunc = function(done) {
|
||||
//... do something (risky) ...
|
||||
if(error) return done(error);
|
||||
done(null, result);
|
||||
};
|
||||
```
|
||||
<code>- Person Prototype -</code>
|
||||
<code>--------------------</code>
|
||||
<code>name : string [required]</code>
|
||||
<code>age : number</code>
|
||||
<code>favoriteFoods : array of strings (*) </code>
|
||||
你可以使用基础的 SchemaTypes 去添加更多的字段,比如使用 required 或者 unique 这样的简单验证去设置默认值。参考 <a href='http://mongoosejs.com/docs/guide.html'>Mongoose 文档</a>。
|
||||
[C]RUD Part I - CREATE
|
||||
|
||||
<code>/* 示例 */</code>
|
||||
<code>var someFunc = function(done) {</code>
|
||||
<code> // 执行一些可能产生错误的代码</code>
|
||||
<code> if(error) return done(error);</code>
|
||||
<code> done(null, result);</code>
|
||||
<code>};</code>
|
||||
<section id='description'> <code>0</code>首先,我们需要一个Schema。每个模式都映射到MongoDB集合。它定义了该集合中文档的形状。 <code>0</code>模式是模型的构建块。它们可以嵌套来创建复杂的模型,但在这种情况下,我们会保持简单。 <code>0</code>模型允许您创建对象的实例,称为文档。 <code>0</code>创建一个拥有此原型的人:
|
||||
<code>- Person Prototype -</code>
|
||||
<code>--------------------</code>
|
||||
<code>name : string [required]</code>
|
||||
<code>age : number</code>
|
||||
<code>favoriteFoods : array of strings (*)</code> <code>0</code>使用mongoose基本模式类型。如果需要,还可以添加<code>0</code>个字段,使用简单的验证器,如required或unique, <code>0</code>并设置默认值。请参阅<a href='http://mongoosejs.com/docs/guide.html'>mongoose文档</a> 。
|
||||
[C] RUD第一部分 - 创建<code>0</code>注意:Glitch是一个真实的服务器,在真实服务器中,与db的交互发生在处理函数中。当某些事件发生时执行这些功能(例如某人在您的API上命中端点)。我们将在这些练习中遵循相同的方法。 done()函数是一个回调,告诉我们在完成插入,搜索,更新或删除等异步操作后我们可以继续。它遵循Node约定,应该在成功时调用done(null,data),或者在出错时调用(err)。 <code>0</code>警告 - 与远程服务交互时,可能会发生错误!
|
||||
<code>/* Example */</code>
|
||||
<code>var someFunc = function(done) {</code>
|
||||
<code>//... do something (risky) ...</code>
|
||||
<code>if(error) return done(error);</code>
|
||||
<code>done(null, result);</code>
|
||||
<code>};</code>
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
创建一个拥有以下 Prototype 的 Person 对象:
|
||||
<blockquote>
|
||||
- Person Prototype -<br>
|
||||
--------------------<br>
|
||||
name : string [required]<br>
|
||||
age : number<br>
|
||||
favoriteFoods : array of strings (*)
|
||||
</blockquote>
|
||||
<section id='instructions'>
|
||||
|
||||
你可以使用基础的 SchemaTypes 去添加更多的字段,
|
||||
比如使用 required 或者 unique 这样的简单验证去设置默认值。
|
||||
参考 <a href='http://mongoosejs.com/docs/guide.html'>Mongoose 文档</a>。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -63,7 +31,7 @@ favoriteFoods : array of strings (*)
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 成功创建一个 Schema 实例。
|
||||
- text: 从mongoose模式创建实例应该会成功
|
||||
testString: 'getUserInput => $.post(getUserInput(''url'') + ''/_api/mongoose-model'', {name: ''Mike'', age: 28, favoriteFoods: [''pizza'', ''cheese'']}).then(data => { assert.equal(data.name, ''Mike'', ''"model.name" is not what expected''); assert.equal(data.age, ''28'', ''"model.age" is not what expected''); assert.isArray(data.favoriteFoods, ''"model.favoriteFoods" is not an Array''); assert.include(data.favoriteFoods, ''pizza'', ''"model.favoriteFoods" does not include the expected items''); assert.include(data.favoriteFoods, ''cheese'', ''"model.favoriteFoods" does not include the expected items''); }, xhr => { throw new Error(xhr.responseText); })'
|
||||
|
||||
```
|
||||
@@ -79,11 +47,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -1,28 +1,21 @@
|
||||
---
|
||||
id: 587d7fb6367417b2b2512c09
|
||||
title: Create and Save a Record of a Model
|
||||
localeTitle: 创建并保存模型记录
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
localeTitle: 用模型来创建并保存一条记录
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
在本挑战中创建保存一条记录。
|
||||
<section id='description'> <code>0</code>使用之前构建的Person构造函数创建文档实例。将具有字段名称,年龄和favoriteFoods的对象传递给构造函数。它们的类型必须符合Person Schema中的类型。然后在返回的文档实例上调用方法document.save()。使用Node约定传递回调。这是一种常见模式,以下所有CRUD方法都将这样的回调函数作为最后一个参数。
|
||||
<code>/* Example */</code>
|
||||
<code>// ...</code>
|
||||
<code>person.save(function(err, data) {</code>
|
||||
<code>// ...do your stuff here...</code>
|
||||
<code>});</code>
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 Person 的 constructor(构造器)函数可以创建一个 document 对象,该对象包含<code>name</code>、<code>age</code>和<code>favoriteFoods</code>字段。这些字段的类型必须符合 Person Schema 里面定义的类型。然后调用<code>document.save()</code>。使用 Node 惯例传递 callback。通常情况下,所有的 CRUD(增查改删)方法都会像下面一样作为最后一个参数去执行一个<code>callback()</code>。
|
||||
|
||||
```js
|
||||
/* Example */
|
||||
|
||||
// ...
|
||||
person.save(function(err, data) {
|
||||
// ...do your stuff here...
|
||||
});
|
||||
```
|
||||
<section id='instructions'>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -31,8 +24,9 @@ person.save(function(err, data) {
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 成功创建一条 db 并保存。
|
||||
- text: 创建和保存数据库项应该会成功
|
||||
testString: 'getUserInput => $.get(getUserInput(''url'') + ''/_api/create-and-save-person'').then(data => { assert.isString(data.name, ''"item.name" should be a String''); assert.isNumber(data.age, ''28'', ''"item.age" should be a Number''); assert.isArray(data.favoriteFoods, ''"item.favoriteFoods" should be an Array''); assert.equal(data.__v, 0, ''The db item should be not previously edited''); }, xhr => { throw new Error(xhr.responseText); })'
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
@@ -46,11 +40,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -1,19 +1,17 @@
|
||||
---
|
||||
id: 587d7fb7367417b2b2512c0a
|
||||
title: Create Many Records with model.create()
|
||||
localeTitle: 使用model.create()创建许多记录
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
localeTitle: 使用 model.create() 创建许多记录
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
有时你需要创建很多的 model 实例。例如:在使用初始数据为数据库初始化时,<code>Model.create()</code>接受一组像<code>[{name:'John', ...}, {...}, ...]</code>的数组作为第一个参数,并将其保存到数据库。
|
||||
<section id='description'> <code>0</code>有时您需要创建模型的许多实例,例如,在使用初始数据为数据库播种时。 Model.create()接受一个对象数组,如[{name:'John',...},{...},...]作为第一个参数,并将它们全部保存在db中。使用函数参数arrayOfPeople使用Model.create()创建许多人。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用<code>arrayOfPeople</code>作为<code>Model.create()</code>的参数创建很多个 people 实例。
|
||||
<section id='instructions'>
|
||||
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -21,7 +19,7 @@ localeTitle: 使用 model.create() 创建许多记录
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应当可以一次性创建多个 item(项目)。
|
||||
- text: 一次创建多个数据库项应该会成功
|
||||
testString: 'getUserInput => $.ajax({url: getUserInput(''url'') + ''/_api/create-many-people'', type: ''POST'', contentType:''application/json'', data: JSON.stringify([{name: ''John'', age: 24, favoriteFoods: [''pizza'', ''salad'']}, {name: ''Mary'', age: 21, favoriteFoods: [''onions'', ''chicken'']}])}).then(data => { assert.isArray(data, ''the response should be an array''); assert.equal(data.length, 2, ''the response does not contain the expected number of items''); assert.equal(data[0].name, ''John'', ''The first item is not correct''); assert.equal(data[0].__v, 0, ''The first item should be not previously edited''); assert.equal(data[1].name, ''Mary'', ''The second item is not correct''); assert.equal(data[1].__v, 0, ''The second item should be not previously edited''); }, xhr => { throw new Error(xhr.responseText); })'
|
||||
|
||||
```
|
||||
@@ -37,11 +35,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -1,20 +1,18 @@
|
||||
---
|
||||
id: 587d7fb8367417b2b2512c11
|
||||
title: Delete Many Documents with model.remove()
|
||||
localeTitle: 使用model.remove()删除许多文档
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
localeTitle: 使用 model.remove() 删除许多文档
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
<code>Model.remove()</code> 可用于删除与给定条件匹配的所有 document。
|
||||
<section id='description'>
|
||||
Model.remove()用于删除与给定条件匹配的所有文档。使用Model.remove()删除名称为“Mary”的所有人员。将其传递给查询文档,其中设置了“name”字段,当然还有回调。 <code>0</code>注意:Model.remove()不返回已删除的文档,而是返回包含操作结果和受影响项目数的JSON对象。不要忘记将它传递给done()回调,因为我们在测试中使用它。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
如果想要删除所有叫 “Mary” 的人, 可以使用 <code>Model.remove()</code>。 传入一个设置了 “name” 字段的 document 作为查询条件,当然还需要一个回调函数。
|
||||
注意: <code>Model.remove()</code> 不会返回被删除的 document, 但是会返回一个包含操作结果和受影响的 item 数量的 JSON 对象。 不要忘记将它传递给 <code>done()</code> 回调,因为我们在测试中使用它。
|
||||
<section id='instructions'>
|
||||
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -22,7 +20,7 @@ localeTitle: 使用 model.remove() 删除许多文档
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 一次性成功的删除多个 item(项目)。
|
||||
- text: 一次删除多个项目应该会成功
|
||||
testString: 'getUserInput => $.ajax({url: getUserInput(''url'') + ''/_api/remove-many-people'', type: ''POST'', contentType:''application/json'', data: JSON.stringify([{name: ''Mary'', age: 16, favoriteFoods: [''lollipop'']}, {name: ''Mary'', age: 21, favoriteFoods: [''steak'']}])}).then(data => { assert.isTrue(!!data.ok, ''The mongo stats are not what expected''); assert.equal(data.n, 2, ''The number of items affected is not what expected''); assert.equal(data.count, 0, ''the db items count is not what expected''); }, xhr => { throw new Error(xhr.responseText); })'
|
||||
|
||||
```
|
||||
@@ -38,11 +36,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -1,19 +1,16 @@
|
||||
---
|
||||
id: 587d7fb8367417b2b2512c10
|
||||
title: Delete One Document Using model.findByIdAndRemove
|
||||
localeTitle: 使用model.findByIdAndRemove删除一个文档
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301539
|
||||
localeTitle: 使用 model.findByIdAndRemove() 删除一个文档
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
你可以使用 <code>findByIdAndRemove()</code> 或者 <code>findOneAndRemove()</code> 方法, 通过 _id 删除一个人员。 它和上面的更新方法很像,都是先通过 personId 找到对应的 document,再删除,并返回被删除的 document。
|
||||
<section id='description'> <code>0</code>通过她的_id删除一个人。您应该使用findByIdAndRemove()或findOneAndRemove()方法之一。它们与之前的更新方法类似。他们将删除的文件传递给cb。像往常一样,使用函数参数personId作为搜索关键字。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
<section id='instructions'>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -22,7 +19,7 @@ localeTitle: 使用 model.findByIdAndRemove() 删除一个文档
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 成功删除一个 item(项目)。
|
||||
- text: 删除项目应该会成功
|
||||
testString: 'getUserInput => $.post(getUserInput(''url'') + ''/_api/remove-one-person'', {name:''Jason Bourne'', age: 36, favoriteFoods:[''apples'']}).then(data => { assert.equal(data.name, ''Jason Bourne'', ''item.name is not what expected''); assert.equal(data.age, 36, ''item.age is not what expected''); assert.deepEqual(data.favoriteFoods, [''apples''], ''item.favoriteFoods is not what expected''); assert.equal(data.__v, 0); assert.equal(data.count, 0, ''the db items count is not what expected''); }, xhr => { throw new Error(xhr.responseText); })'
|
||||
|
||||
```
|
||||
@@ -38,11 +35,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -1,23 +1,16 @@
|
||||
---
|
||||
id: 587d7fb6367417b2b2512c06
|
||||
title: Install and Set Up Mongoose
|
||||
localeTitle: 安装和设置Mongoose
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
localeTitle: 安装和设置 Mongoose
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
在 package.json 文件中添加 MongoDB 和 Mongoose 依赖,将 mLab 数据库的 URI 作为 MONGO_URI 变量存储在私有 .env 文件中。然后<code>require('mongoose')</code>,使用<code>mongoose.connect(<Your URI>)</code>命令来连接数据库。
|
||||
|
||||
```js
|
||||
mongoose.connect(<Your URI>, { useNewUrlParser: true, useUnifiedTopology: true });
|
||||
```
|
||||
|
||||
<section id='description'> <code>0</code>将mongodb和mongoose添加到项目的package.json中。然后需要猫鼬。将mLab数据库URI作为MONGO_URI存储在私有.env文件中。使用mongoose.connect连接到数据库( <Your URI> )
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
<section id='instructions'>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -26,11 +19,11 @@ mongoose.connect(<Your URI>, { useNewUrlParser: true, useUnifiedTopology: true }
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: "在 package.json 文件中应该有 'mongodb' 依赖。"
|
||||
- text: '"mongodb"依赖应该在package.json'
|
||||
testString: 'getUserInput => $.get(getUserInput(''url'') + ''/_api/file/package.json'').then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, ''mongodb''); }, xhr => { throw new Error(xhr.responseText); })'
|
||||
- text: "在 package.json 文件中应该有 'mongoose' 依赖。"
|
||||
- text: '"mongoose"依赖应该在package.json'
|
||||
testString: 'getUserInput => $.get(getUserInput(''url'') + ''/_api/file/package.json'').then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, ''mongoose''); }, xhr => { throw new Error(xhr.responseText); })'
|
||||
- text: "'mongoose' 应该已经连接数据库。"
|
||||
- text: '"mongoose"应该连接到数据库'
|
||||
testString: 'getUserInput => $.get(getUserInput(''url'') + ''/_api/is-mongoose-ok'').then(data => {assert.isTrue(data.isMongooseOk, ''mongoose is not connected'')}, xhr => { throw new Error(xhr.responseText); })'
|
||||
|
||||
```
|
||||
@@ -46,11 +39,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -1,23 +1,17 @@
|
||||
---
|
||||
id: 587d7fb8367417b2b2512c0e
|
||||
title: 'Perform Classic Updates by Running Find, Edit, then Save'
|
||||
localeTitle: '通过运行查找,编辑然后保存来执行经典更新'
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
localeTitle: 通过运行 find、edit、save 来执行经典更新
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
|
||||
传统应用里,如果你想要编辑 document,然后在某处使用它。你就必须在服务器响应中将其返回。Mongoose 有一个专用的更新方法:<code>Model.update() </code>, 它与低级的 mongo 驱动绑定,可以批量编辑符合特定条件的多个 document,而不用返回更新后的 document,取而代之返回'状态'消息。此外,它使模型校验变得更棘手,因为它是直接调用了 mongo 的驱动程序。
|
||||
<section id='description'> <code>0</code>在过去的好时光中,如果您想编辑文档并能够以某种方式使用它,例如在服务器响应中将其发回,则需要执行此操作。 Mongoose有一个专用的更新方法:Model.update()。它与低级mongo驱动程序绑定。它可以批量编辑符合特定条件的许多文档,但它不会发回更新的文档,只会发送“状态”消息。此外,它使模型验证变得困难,因为它只是直接调用mongo驱动程序。 <code>0</code>使用参数personId作为搜索关键字,通过_id(使用上述任何方法)查找人物。将“hamburger”添加到她最喜欢的食物列表中(您可以使用Array.push())。然后 - 在find回调中 - save()更新的Person。
|
||||
[*]提示:如果你的Schema中你将favoriteFoods声明为一个数组而没有指定类型(即[String]),这可能会很棘手。在那种情况下,favorsFoods默认为Mixed type,你必须使用document.markModified('edited-field')手动将其标记为已编辑。 (http://mongoosejs.com/docs/schematypes.html - #Mixed)
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
使用参数 personId 作为搜索关键字,然后通过 _id 找到一个 person(使用上述任何一种方法). 将 “hamburger” 添加到她的 favoriteFoods 列中去 (可以使用 <code>Array.push()</code>)。然后在 <code>.find()</code> 的回调里通过 <code>.save()</code> 方法更新。
|
||||
|
||||
[*] 提示: 如果你在 Schema 中将 favoriteFoods 声明为一个 Array(数组), 而没有指定数组的类型(如:[String]),结果会让人很意外。 在这种情况下,favoriteFoods 默认为 Mixed 类型。如果想要编辑它,就必须执行 <code>document.markModified('edited-field')</code>。(http://mongoosejs.com/docs/schematypes.html - #Mixed)
|
||||
<section id='instructions'>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -26,8 +20,9 @@ localeTitle: 通过运行 find、edit、save 来执行经典更新
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 对一个 item(项目)的 Find-edit-update 操作成功
|
||||
- text: 查找 - 编辑 - 更新项目应该成功
|
||||
testString: "getUserInput => $.post(getUserInput('url') + '/_api/find-edit-save', {name:'Poldo', age: 40, favoriteFoods:['spaghetti']}).then(data => { assert.equal(data.name, 'Poldo', 'item.name is not what is expected'); assert.equal(data.age, 40, 'item.age is not what expected'); assert.deepEqual(data.favoriteFoods, ['spaghetti', 'hamburger'], 'item.favoriteFoods is not what expected'); assert.equal(data.__v, 1, 'The item should be previously edited'); }, xhr => { throw new Error(xhr.responseText); })"
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
@@ -41,11 +36,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -1,21 +1,17 @@
|
||||
---
|
||||
id: 587d7fb8367417b2b2512c0f
|
||||
title: Perform New Updates on a Document Using model.findOneAndUpdate()
|
||||
localeTitle: 使用model.findOneAndUpdate()对文档执行新更新
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301542
|
||||
localeTitle: 使用 model.findOneAndUpdate() 对文档执行新的更新
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
mongoose 的最新版本简化了 documents 的更新。 但是一些高级的用法 (比如 pre/post 钩子, 验证) 更复杂, 所以老方法更常用。当通过 Id 进行搜索时还可以使用 <code>findByIdAndUpdate()</code>。
|
||||
<section id='description'> <code>0</code>最新版本的mongoose具有简化文档更新的方法。一些更高级的功能(即前/后挂钩,验证)与此方法的行为不同,因此Classic方法在许多情况下仍然有用。在按Id搜索时可以使用findByIdAndUpdate()。 <code>0</code>按名称查找人员并将其年龄设置为20.使用函数参数personName作为搜索关键字。 <code>0</code>提示:我们希望您返回更新的文档。为此,您需要将选项文档{new:true}作为findOneAndUpdate()的第三个参数传递。默认情况下,这些方法返回未修改的对象。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 personName 作为搜索的关键词查找 person,并将查到的 person 的年龄设为 20 岁。
|
||||
提示: 我们想要你返更新后的 document,你可以把 <code>findOneAndUpdate()</code> 的第三个参数设置为 <code>{new: true}</code>。 默认情况下,这些方法返回未被修改的数据。
|
||||
<section id='instructions'>
|
||||
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -23,7 +19,7 @@ mongoose 的最新版本简化了 documents 的更新。 但是一些高级的
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 对一个 item(项目)的 findOneAndUpdate 操作成功
|
||||
- text: findOneAndUpdate项应该成功
|
||||
testString: 'getUserInput => $.post(getUserInput(''url'') + ''/_api/find-one-update'', {name:''Dorian Gray'', age: 35, favoriteFoods:[''unknown'']}).then(data => { assert.equal(data.name, ''Dorian Gray'', ''item.name is not what expected''); assert.equal(data.age, 20, ''item.age is not what expected''); assert.deepEqual(data.favoriteFoods, [''unknown''], ''item.favoriteFoods is not what expected''); assert.equal(data.__v, 0, ''findOneAndUpdate does not increment version by design !!!''); }, xhr => { throw new Error(xhr.responseText); })'
|
||||
|
||||
```
|
||||
@@ -39,11 +35,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -1,20 +1,16 @@
|
||||
---
|
||||
id: 587d7fb7367417b2b2512c0b
|
||||
title: Use model.find() to Search Your Database
|
||||
localeTitle: 使用model.find()搜索数据库
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301543
|
||||
localeTitle: 使用 model.find() 查找数据库
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
使用<code>Model.find() -> [Person]</code>来查询给定名称的所有的人。
|
||||
最简单的用法:<code>Model.find()</code>接受一个查询的 document(一个 JSON 对象)作为第一参数,然后是回调。它将返回匹配到的项目组成的数组。这个支持极其广泛的搜索选项。使用人名作为搜索的关键词,来校验它。
|
||||
<section id='description'> <code>0</code>使用Model.find() - > [Person]查找具有给定名称的所有人<code>0</code>在最简单的用法中,Model.find()接受查询文档(JSON对象)作为第一个参数,然后接受回调。它返回一个匹配数组。它支持极其广泛的搜索选项。在文档中查看它。使用函数参数personName作为搜索关键字。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
<section id='instructions'>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -23,7 +19,7 @@ localeTitle: 使用 model.find() 查找数据库
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 成功找到所有符合条件的 item(项目)。
|
||||
- text: 找到与标准对应的所有项目都应该成功
|
||||
testString: 'getUserInput => $.post(getUserInput(''url'') + ''/_api/find-all-by-name'', {name: ''r@nd0mN4m3'', age: 24, favoriteFoods: [''pizza'']}).then(data => { assert.isArray(data, ''the response should be an Array''); assert.equal(data[0].name, ''r@nd0mN4m3'', ''item.name is not what expected''); assert.equal(data[0].__v, 0, ''The item should be not previously edited''); }, xhr => { throw new Error(xhr.responseText); })'
|
||||
|
||||
```
|
||||
@@ -39,11 +35,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -1,19 +1,17 @@
|
||||
---
|
||||
id: 587d7fb7367417b2b2512c0d
|
||||
title: Use model.findById() to Search Your Database By _id
|
||||
localeTitle: 使用model.findById()按_id搜索数据库
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
localeTitle: 使用 model.findById() 按 _id 搜索数据库
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
当我们保存一个 document, MongoDB 自动添加 _id 字段,并给该字段设置 unique(唯一)属性。通过 _id 搜索是一个非常频繁的操作,所以 Mongose 为它提供了一个专门的方法。
|
||||
<section id='description'> <code>0</code>保存文档时,mongodb会自动添加字段_id,并将其设置为唯一的字母数字键。按_id搜索是一种非常频繁的操作,因此mongoose为它提供了一种专用方法。使用Model.findById() - > Person找到具有给定_id的(仅!!)人物。使用函数参数personId作为搜索关键字。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用人物 Id 作为参数,执行<code>Model.findById() -> Person</code>,找到这个 _id 对应的唯一的一个人。
|
||||
<section id='instructions'>
|
||||
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -21,8 +19,9 @@ localeTitle: 使用 model.findById() 按 _id 搜索数据库
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 通过 Id 成功找到对应的 item(项目)。
|
||||
- text: 找到Id应该成功的项目
|
||||
testString: "getUserInput => $.get(getUserInput('url') + '/_api/find-by-id').then(data => { assert.equal(data.name, 'test', 'item.name is not what expected'); assert.equal(data.age, 0, 'item.age is not what expected'); assert.deepEqual(data.favoriteFoods, ['none'], 'item.favoriteFoods is not what expected'); assert.equal(data.__v, 0, 'The item should be not previously edited'); }, xhr => { throw new Error(xhr.responseText); })"
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
@@ -36,11 +35,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -1,20 +1,18 @@
|
||||
---
|
||||
id: 587d7fb7367417b2b2512c0c
|
||||
title: Use model.findOne() to Return a Single Matching Document from Your Database
|
||||
localeTitle: 使用model.findOne()从数据库中返回单个匹配文档
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301545
|
||||
localeTitle: 使用 model.findOne() 从数据库中返回一个匹配的文档
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
<code>Model.findOne()</code>表现像<code>Model.find()</code>,但是它仅仅返回一个 document(而不是一个数组),即使数据库里有很多条 item(项目)。当你按声明成<code>unique</code>的属性进行搜索时,<code>Model.findOne()</code>尤其有用。
|
||||
<section id='description'>
|
||||
Model.findOne()的行为类似于.find(),但它只返回一个文档(不是数组),即使有多个项目也是如此。在按声明为唯一的属性进行搜索时,它尤其有用。使用Model.findOne() - > Person,找到一个在她的收藏夹中有某种食物的人。使用函数参数food作为搜索键。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
把 food 作为<code>Model.findOne() -> Person</code>的参数,来找到一个在她的爱好中有某一食物的人。
|
||||
<section id='instructions'>
|
||||
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -22,7 +20,7 @@ localeTitle: 使用 model.findOne() 从数据库中返回一个匹配的文档
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 成功找到一个 item(项目)。
|
||||
- text: 找一个项应该成功
|
||||
testString: 'getUserInput => $.post(getUserInput(''url'') + ''/_api/find-one-by-food'', {name: ''Gary'', age: 46, favoriteFoods: [''chicken salad'']}).then(data => { assert.equal(data.name, ''Gary'', ''item.name is not what expected''); assert.deepEqual(data.favoriteFoods, [''chicken salad''], ''item.favoriteFoods is not what expected''); assert.equal(data.__v, 0, ''The item should be not previously edited''); }, xhr => { throw new Error(xhr.responseText); })'
|
||||
|
||||
```
|
||||
@@ -38,11 +36,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,38 +2,24 @@
|
||||
id: 589fc832f9fc0f352b528e78
|
||||
title: Announce New Users
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301546
|
||||
localeTitle: 用户公告
|
||||
videoUrl: ''
|
||||
localeTitle: 宣布新用户
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>GitHub</a> 上克隆。
|
||||
许多聊天室都有这个功能:所有已连接到服务器的在线用户都会看到有人加入或退出的提醒。我们已经写好了处理连接和断开事件的代码,只要对这个方法稍作修改就可以实现这个功能。在事件中,我们需要发送这三条信息:连接或断开的用户名、当前用户数量、事件类型(即需要知道用户究竟是连接还是断开)。
|
||||
<hr>请将事件名称更改为 'user',其中应包含如下字段:'name'、'currentUsers'、'connected'(布尔值,对于连接是 true,断开是 false)。记得要修改之前我们写好的处理 'user count' 的那部分代码,现在我们应传入布尔值:<code>io.emit('user', {name: socket.request.user.name, currentUsers, connected: true});</code>
|
||||
现在,我们的客户端已经有足够的信息显示现有用户数量和发送用户上下线通知。接下来我们需要在客户端监听 'user' 事件,然后使用 jQuery 把<code>#num-users</code>节点的文本内容更新为 '{NUMBER} users online'。同时,我们需要为<code><ul></code>添加一个 id 为 'messages' 且带有 '{NAME} has {joined/left} the chat.' 文本的<code><li></code>:An implementation of this could look like the following:<br>
|
||||
其中一种实现如下:<br>
|
||||
|
||||
```js
|
||||
socket.on('user', function(data){
|
||||
$('#num-users').text(data.currentUsers+' users online');
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-socketio/">GitHub</a>克隆的。许多聊天室能够在用户连接或断开连接时进行通知,然后将其显示给聊天中的所有连接用户。看起来您已经在连接和断开连接上发出事件,您只需修改此事件即可支持此功能。最合乎逻辑的方法是使用事件发送3个数据:连接/断开用户的名称,当前用户计数以及连接或断开连接的名称。 <hr>将事件名称更改为“user”,并且数据传递一个对象,包含字段'name','currentUsers'和boolean'connected'(如果是连接则为true,对于断开发送的用户,则为false)。确保对我们有'用户计数'事件的两个点进行更改,并将断开连接设置为对'field'字段发送false,而不是像在connect上发出的事件那样为true。 <code>io.emit('user', {name: socket.request.user.name, currentUsers, connected: true});</code>现在,您的客户端将拥有所有必要信息,以便在用户连接或断开连接时正确显示当前用户数和通知!要在客户端处理此事件,我们应该监听“用户”,然后使用jQuery将<code>#num-users</code>的文本更改为“{NUMBER}在线用户”,并附加<code><li></code> ,以更新当前用户数<code><li></code>使用ID为'messages'的无序列表,其中'{NAME}已{加/左}聊天。'。此实现可能如下所示: <pre> socket.on('user',function(data){
|
||||
$('#num-users')。text(data.currentUsers +'users online');
|
||||
var message = data.name;
|
||||
if(data.connected) {
|
||||
message += ' has joined the chat.';
|
||||
if(data.connected){
|
||||
消息+ ='已加入聊天。';
|
||||
} else {
|
||||
message += ' has left the chat.';
|
||||
消息+ ='离开了聊天。';
|
||||
}
|
||||
$('#messages').append($('<li>').html('<b>'+ message +'<\/b>'));
|
||||
});
|
||||
```
|
||||
|
||||
完成上述要求后,你就可以在左边提交你的页面链接。
|
||||
</section>
|
||||
$('#messages')。append($('<li>')。html('<b>'+ message +'<\ / b>'));
|
||||
}); </pre>当您认为自己已经做对时,请提交您的页面。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -41,9 +27,9 @@ socket.on('user', function(data){
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: user 事件应发送包含 name、currentUsers、connected 字段的对象。
|
||||
- text: 使用name,currentUsers和connected发出事件“user”
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /io.emit.*('|")user('|").*name.*currentUsers.*connected/gi, 'You should have an event emitted named user sending name, currentUsers, and connected'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: '客户端应处理和显示 "user" 对象中的信息。'
|
||||
- text: 客户正确处理和显示事件'用户'中的新数据
|
||||
testString: "getUserInput => $.get(getUserInput('url')+ '/public/client.js') .then(data => { assert.match(data, /socket.on.*('|\")user('|\")[^]*num-users/gi, 'You should change the text of #num-users within on your client within the \"user\" even listener to show the current users connected'); assert.match(data, /socket.on.*('|\")user('|\")[^]*messages.*li/gi, 'You should append a list item to #messages on your client within the \"user\" event listener to announce a user came or went'); }, xhr => { throw new Error(xhr.statusText); })"
|
||||
|
||||
```
|
||||
@@ -59,11 +45,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,40 +2,25 @@
|
||||
id: 5895f70df9fc0f352b528e68
|
||||
title: Authentication Strategies
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301547
|
||||
localeTitle: 身份验证策略
|
||||
videoUrl: ''
|
||||
localeTitle: 认证策略
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a> 上克隆。
|
||||
现在,我们需要构建验证用户的策略,策略的选择有很多。比如,如果我们已经让用户在注册时填写了用户信息,那我们就可以基于这些信息验证;或者也可以引入第三方登录,如 Google 或者 Github。为此,你可以参考 <a href='http://passportjs.org/'>Passports 中提供的策略插件</a>。对于这个项目的验证策略,我们会采用自己搭建的方式完成。
|
||||
首先,我们需要引入 <em>passport-local</em> 作为依赖,然后将它添加到服务器,就像这样:<code>const LocalStrategy = require('passport-local');</code>
|
||||
然后,我们需要让 passport 使用实例化的本地策略对象。请注意,接下来的所有代码都应写在连接数据库的回调中,因为它们的执行都依赖数据库。
|
||||
|
||||
```js
|
||||
passport.use(new LocalStrategy(
|
||||
function(username, password, done) {
|
||||
db.collection('users').findOne({ username: username }, function (err, user) {
|
||||
console.log('User '+ username +' attempted to log in.');
|
||||
if (err) { return done(err); }
|
||||
if (!user) { return done(null, false); }
|
||||
if (password !== user.password) { return done(null, false); }
|
||||
return done(null, user);
|
||||
});
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-advancednode/">GitHub</a>克隆的。策略是一种验证用户的方法。您可以使用策略允许用户根据本地保存的信息(如果您首先注册)或从Google或Github等各种提供商进行身份验证。对于这个项目,我们将制定一个本地战略。要查看的策略100的列表,请访问网站护照<a href="http://passportjs.org/">这里</a> 。将<em>passport-local</em>添加为依赖项并将其添加到服务器,如下所示: <code>const LocalStrategy = require('passport-local');</code>现在,您必须告诉护照<b>使用</b>实例化的LocalStartegy对象,并定义一些设置。确保这一点以及此时的所有内容都封装在数据库连接中,因为它依赖于它! <pre> passport.use(新的LocalStrategy(
|
||||
function(用户名,密码,已完成){
|
||||
db.collection('users')。findOne({username:username},function(err,user){
|
||||
console.log('用户'+用户名+'试图登录。');
|
||||
if(err){return done(err); }
|
||||
if(!user){return done(null,false); }
|
||||
if(password!== user.password){return done(null,false); }
|
||||
return done(null,user);
|
||||
});
|
||||
}
|
||||
));
|
||||
```
|
||||
|
||||
这就是我们的用户验证逻辑:首先根据用户输入的用户名在数据库中寻找用户;然后检查密码是否匹配;最后如果没有发生错误,那么就会返回用户对象并通过验证。
|
||||
我们也可以采用上面链接中提供的验证策略,一般来说,根据该策略仓库中的 README 来进行配置就足够了。一个很好的例子是 Github 策略,在该策略中,我们不需要写用户名或密码的相关验证逻辑,因为用户将被引导到 Github 页面进行验证。只要他们登录并同意,Github 就会返回他们的个人信息供我们使用。
|
||||
以上就是本次挑战的内容。在下一个挑战中,我们会基于表单数据调用上面写好的验证策略。完成上述要求后,你就可以在左边提交你的页面链接。
|
||||
</section>
|
||||
)); </pre>这是我们尝试在本地验证某人时要采取的过程。首先,它尝试使用输入的用户名在我们的数据库中查找用户,然后检查要匹配的密码,最后如果没有弹出我们检查过的错误,如错误的密码,则返回用户对象,它们是认证。许多策略都是使用不同的设置设置的,一般来说,根据该策略库中的README很容易设置它。一个很好的例子是Github策略,我们不需要担心用户名或密码,因为用户将被发送到Github的auth页面进行身份验证,只要他们登录并同意,然后Github返回他们的个人资料我们用。在下一步中,我们将设置如何实际调用身份验证策略以根据表单数据验证用户!如果您认为自己已经完成了这一点,请提交您的页面。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -43,9 +28,9 @@ passport.use(new LocalStrategy(
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 你的项目需要使用<code>passport-local</code>作为依赖。
|
||||
- text: Passport-local是一种依赖
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/package.json') .then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, 'passport-local', 'Your project should list "passport-local " as a dependency'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 应正确地引入和设置<code>passport-local</code>。
|
||||
- text: Passport-local正确需要和设置
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /require.*("|')passport-local("|')/gi, 'You should have required passport-local'); assert.match(data, /new LocalStrategy/gi, 'You should have told passport to use a new strategy'); assert.match(data, /findOne/gi, 'Your new local strategy should use the findOne query to find a username based on the inputs'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -61,11 +46,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,35 +2,20 @@
|
||||
id: 589fc831f9fc0f352b528e77
|
||||
title: Authentication with Socket.IO
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301548
|
||||
localeTitle: 用 Socket.IO 进行身份验证
|
||||
videoUrl: ''
|
||||
localeTitle: 使用Socket.IO进行身份验证
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>GitHub</a> 上克隆。
|
||||
目前,我们还无法确定连接到服务器的用户身份。虽然 req.user 包含用户信息,但这个只在用户直接与服务器交互(即不通过 web socket 访问服务器资源)时产正。当我们的用户通过 web socket 与服务器连接时,由于不存在 req 对象,因此我们无法获取用户数据。解决这个问题的方法之一是通过读取和解析请求中包含 passport session 的 cookie,然后反序列化,进而获取用户信息对象。幸运的是,npm 上有可以让这个复杂的流程简单化的库。
|
||||
<hr>添加 'passport.socketio' 作为依赖,然后赋值给 'passportSocketIo'。
|
||||
现在我们只需要做一些简单的配置,然后让 Socket.IO 使用它就可以了。请注意,这部分代码应添加在目前的 socket 代码之前,不应添加在目前的连接事件监听中:
|
||||
|
||||
```js
|
||||
io.use(passportSocketIo.authorize({
|
||||
cookieParser: cookieParser,
|
||||
key: 'express.sid',
|
||||
secret: process.env.SESSION_SECRET,
|
||||
store: sessionStore
|
||||
}));
|
||||
```
|
||||
|
||||
你也可以为验证设置回调函数,为它传入 'success' 或 'fail'。这个函数会在用户尝试连接并完成验证后调用。
|
||||
现在,我们可以通过<code>socket.request.user</code>访问用户数据。例如,现在你在代码中添加以下内容:<code>console.log('user ' + socket.request.user.name + ' connected');</code>。这样我们就可以在服务端的控制台打印出通过 socket 连接到服务器的用户信息。
|
||||
完成上述要求后,你就可以在左边提交你的页面链接。如果运行出错,你可以在 <a href='https://gist.github.com/JosephLivengood/a9e69ff91337500d5171e29324e1ff35'>here</a> 这里检查这个项目的完成代码。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-socketio/">GitHub</a>克隆的。目前,您无法确定谁连接到您的Web套接字。虽然'req.user'对用户对象进行了容器处理,但只有当您的用户与Web服务器进行交互并且使用Web套接字时,您才没有req(请求),因此没有用户数据。解决知道谁连接到您的Web套接字的问题的一种方法是解析和解码包含护照会话的cookie,然后对其进行反序列化以获取用户对象。幸运的是,NPM上有一个包,只是为了将一次复杂的任务变成简单的事情! <hr>将“passport.socketio”添加为依赖项,并将其命名为“passportSocketIo”。现在我们只需要告诉Socket.IO使用它并设置选项。确保在现有套接字代码之前添加它,而不是在现有连接侦听器中添加。对于您的服务器,它应如下所示: <pre> io.use(passportSocketIo.authorize({
|
||||
cookieParser:cookieParser,
|
||||
key:'express.sid',
|
||||
secret:process.env.SESSION_SECRET,
|
||||
store:sessionStore
|
||||
})); </pre>您还可以选择将“成功”和“失败”与在客户端尝试连接时身份验证过程完成后调用的函数一起传递。现在可以在套接字对象上以<code>socket.request.user</code>访问用户对象。例如,现在您可以添加以下内容: <code>console.log('user ' + socket.request.user.name + ' connected');</code>它将登录已连接的服务器控制台!当您认为自己已经做对时,请提交您的页面。如果您遇到错误,可以<a href="https://gist.github.com/JosephLivengood/a9e69ff91337500d5171e29324e1ff35">在此处</a>查看项目。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -38,12 +23,12 @@ io.use(passportSocketIo.authorize({
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应添加 passportSocketIo 作为依赖。
|
||||
- text: passportSocketIo是一个依赖项
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/package.json') .then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, 'passport.socketio', 'Your project should list "passport.socketio" as a dependency'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 应正确引入 passportSocketIo 并实例化。
|
||||
- text: passportSocketIo是正确需要的
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js').then(data => { assert.match(data, /require\((['"])passport\.socketio\1\)/gi, 'You should correctly require and instantiate "passport.socketio"');}, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 应正确配置 passportSocketIo。
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /io\.use\(\s*\w+\.authorize\(/, 'You should register "passport.socketio" as socket.io middleware and provide it correct options'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: passportSocketIo已正确设置
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /io\.use\(.+\.authorize\(/gi, 'You should register "passport.socketio" as socket.io middleware and provide it correct options'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
|
||||
@@ -58,11 +43,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,35 +2,16 @@
|
||||
id: 589690e6f9fc0f352b528e6e
|
||||
title: Clean Up Your Project with Modules
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301549
|
||||
videoUrl: ''
|
||||
localeTitle: 使用模块清理项目
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a> 上克隆。
|
||||
目前,我们把所有的代码都放到了 server.js 文件里,这会导致代码难以维护,且扩展性差。
|
||||
现在让我们来创建两个新文件:routes.js 和 auth.js。
|
||||
在每个文件的开头,我们都需要写上这段代码:
|
||||
|
||||
```js
|
||||
module.exports = function (app, db) {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
然后,在 server.js 文件的开头,我们需要像这样引入文件:<code>const routes = require('./routes.js');</code>
|
||||
在成功连接数据库之后,我们需要像这样进行实例化:<code>routes(app, db)</code>。
|
||||
最后,我们需要把所有路由相关的代码从 server.js 移动到新文件 routes.js。不要忘了,<code>ensureAuthenticated</code>方法的定义也要移动到新文件中,这个是我们在之前的挑战中,为在路由中判断用户是否已登录创建的函数。然后,我们还需要在 routes.js 文件开头添加所需要的依赖,如:<code>const passport = require('passport');</code>。
|
||||
如果在这些步骤后没有报错,那么恭喜你,你已成功地从 server.js 文件中分离出了路由文件。
|
||||
现在,我们来把 server.js 中与验证相关的代码分离到 auth.js 中,例如序列化,设置验证策略等。请正确添加依赖,并在 server.js 中调用<code>auth(app,db)</code>。另外,由于我们的注册路由依赖 passport,所以我们需要先调用<code>auth(app, db)</code>,再调用<code>routes(app, db)</code>。
|
||||
Node 和 Express 高级课程至此就告一段落了,你可以在此基础上继续优化和完善这个项目。但至少,现在你也有一些可以展示给大家看的代码了。完成上述要求后,你就可以在左边提交你的页面链接。如果运行出错,你可以在 <a href='https://glitch.com/#!/project/delicious-herring'>here</a> 这里检查你的项目完成情况。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-advancednode/">GitHub</a>克隆的。现在你拥有的一切都在你的server.js文件中。这可能导致难以管理不易扩展的代码。创建2个新文件:Routes.js和Auth.js两者都应该从以下代码开始: <pre> module.exports = function(app,db){
|
||||
<p> } </p></pre>现在位于服务器文件的顶部,需要这样的文件: <code>const routes = require('./routes.js');</code>在您与数据库建立成功连接之后,实例化它们中的每一个如下: <code>routes(app, db)</code>最后,获取服务器中的所有路由并将它们粘贴到新文件中并从服务器文件中删除它们。也可以使用ensureAuthenticated,因为我们专门为路由创建了中间件功能。您现在必须正确添加所使用的依赖项,例如<code>const passport = require('passport');</code> ,在routes.js文件中导出行的最上方。继续添加它们直到不再存在错误,并且您的服务器文件不再有任何路由!现在在auth.js文件中执行相同的操作,其中包含与身份验证相关的所有内容,例如序列化和本地策略的设置,并从服务器文件中删除它们。确保添加依赖项并在同一位置调用服务器中的<code>auth(app,db)</code> 。确保<code>routes(app, db)</code> <code>auth(app, db)</code>之前有<code>auth(app, db)</code> <code>routes(app, db)</code>因为我们的注册路由取决于发起的护照!恭喜 - 您已经处于Advanced Node和Express的这一部分的末尾,并且有一些漂亮的代码可供展示!当您认为自己已经做对时,请提交您的页面。如果您遇到错误,可以<a href="https://glitch.com/#!/project/delicious-herring">在此处查看</a>已完成项目的示例。 <p></p></section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -38,7 +19,7 @@ Node 和 Express 高级课程至此就告一段落了,你可以在此基础上
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应正确引入新文件。
|
||||
- text: 存在的模块
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /require\s*\(('|")\.\/routes(\.js)?\1\)/gi, 'You should have required your new files'); assert.match(data, /mongo.connect[^]*routes/gi, 'Your new modules should be called after your connection to the database'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -54,11 +35,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,33 +2,17 @@
|
||||
id: 589fc831f9fc0f352b528e75
|
||||
title: Communicate by Emitting
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301550
|
||||
localeTitle: 通过 Emitting 通信
|
||||
videoUrl: ''
|
||||
localeTitle: 通过发射进行沟通
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>GitHub</a> 上克隆。
|
||||
<dfn>Emit</dfn> 是你会用到的,最常见的通信方式。如果我们从服务器发送信息给 'io',就相当于把事件的名称和数据发送给了所有处于连接状态的 socket。我们可以利用这个特性实现这样的功能:只要有新用户连接到服务器,我们就可以把目前连接的总用户数发给所有已连接的用户,这样所有用户随时都可以看到实时的在线人数。
|
||||
<hr>首先,我们需要在监听连接的地方之前添加一个用于追踪用户数的变量:<code>var currentUsers = 0;</code>
|
||||
然后,只要有人连接到服务器,我们需要在发出用户数量之前先给这个变量加 1:<code>++currentUsers;</code>
|
||||
最后,在监听连接的地方发出(emit)该事件即可。这个事件应命名为 'user count',且数据应该为 'currentUsers':<code>io.emit('user count', currentUsers);</code>
|
||||
<hr>接下来,我们还需要让客户端监听从服务端发出的事件。为此,我们还是需要用到 <em>on</em> 这个方法:
|
||||
|
||||
```js
|
||||
socket.on('user count', function(data){
|
||||
console.log(data);
|
||||
});
|
||||
```
|
||||
|
||||
现在你可以尝试启动你的 app 并登录,你会看到在客户端的控制台打印出了 1,这就表示目前连接到服务器的用户数为 1。你可以试着通过打开多个 app 来验证数量是否会增加。
|
||||
完成上述要求后,你就可以在左边提交你的页面链接。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-socketio/">GitHub</a>克隆的。 <dfn>Emit</dfn>是您将使用的最常见的沟通方式。当您从服务器向'io'发送内容时,会将事件的名称和数据发送到所有连接的套接字。这个概念的一个很好的例子就是每次新用户连接时都会发出连接用户的当前数量! <hr>首先添加一个变量,以便在您当前正在侦听连接之前跟踪用户。 <code>var currentUsers = 0;</code>现在当有人连接时,你应该在发出计数之前递增计数,这样你就可以在连接监听器中添加增量器。 <code>++currentUsers;</code>最后,在递增计数后,您应该发出事件(仍在连接侦听器中)。该事件应命名为“用户计数”,数据应该只是'currentUsers'。 <code>io.emit('user count', currentUsers);</code> <hr>现在,您可以为客户实施一种方式来监听此事件!与在服务器上侦听连接类似,您将使用<em>on</em>关键字。 <pre> socket.on('user count',function(data){
|
||||
的console.log(数据);
|
||||
}); </pre>现在尝试加载您的应用并进行身份验证,您应该在客户端控制台中看到“1”代表当前用户数!尝试加载更多客户端并进行身份验证以查看数量是否上升。当您认为自己已经做对时,请提交您的页面。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -36,11 +20,11 @@ socket.on('user count', function(data){
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应定义 currentUsers
|
||||
- text: currentUsers已定义
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js').then(data => {assert.match(data, /currentUsers/gi, 'You should have variable currentUsers defined');}, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 服务器应在有新的连接时 emit 当前用户数量。
|
||||
- text: 服务器在每个新连接上发出当前用户计数
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /io.emit.*('|")user count('|").*currentUsers/gi, 'You should emit "user count" with data currentUsers'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: '客户端应监听 "user count" 事件。'
|
||||
- text: 您的客户正在侦听“用户计数”事件
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/public/client.js') .then(data => { assert.match(data, /socket.on.*('|")user count('|")/gi, 'Your client should be connection to server with the connection defined as socket'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -56,11 +40,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,42 +2,23 @@
|
||||
id: 5895f70df9fc0f352b528e6a
|
||||
title: Create New Middleware
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301551
|
||||
videoUrl: ''
|
||||
localeTitle: 创建新的中间件
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a> 上克隆。
|
||||
无论是否登录,或者哪怕用户试图访问其他页面,目前都会跳转到<code>/profile</code>。为了解决这个问题,我们需要在 profile 页面渲染之前进行用户验证,创建中间件就可以实现这个功能。
|
||||
这个挑战的目标是创建<code>ensureAuthenticated(req, res, next)</code>中间件方法,通过在 <em>request</em> 上调用 passports 的<code>isAuthenticated</code>方法,我们可以检查 <em>req.user</em> 是否定义,从而确定用户是否通过认证。如果用户已通过验证,就会调用 <em>next()</em>,否则我们应重定向到主页并让用户登录。该中间件的实现是:
|
||||
|
||||
```js
|
||||
function ensureAuthenticated(req, res, next) {
|
||||
if (req.isAuthenticated()) {
|
||||
return next();
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-advancednode/">GitHub</a>克隆的。同样,任何用户都可以通过输入网址来查看/配置他们是否通过身份验证。我们希望通过在呈现配置文件页面之前检查用户是否首先进行身份验证来防止这种情况。这是何时创建中间件的完美示例。这里的挑战是创建中间件功能<code>ensureAuthenticated(req, res, next)</code> ,它将检查用户是否通过调用护照进行身份验证isAuthenticated对<em>请求</em>进行检查,然后检查<em>req.user</em>是否定义。如果是,那么应该调用<em>next()</em> ,否则我们只需通过重定向到我们的主页来回复请求即可登录。该中间件的实现是: <pre> function ensureAuthenticated(req,res,next){
|
||||
if(req.isAuthenticated()){
|
||||
return next();
|
||||
}
|
||||
res.redirect('/');
|
||||
};
|
||||
```
|
||||
|
||||
然后,我们需要把 <em>ensureAuthenticated</em> 中间件添加到处理请求的回调之前:
|
||||
|
||||
```js
|
||||
app
|
||||
.route('/profile')
|
||||
.get(ensureAuthenticated, (req,res) => {
|
||||
res.render(process.cwd() + '/views/pug/profile');
|
||||
});
|
||||
```
|
||||
|
||||
完成上述要求后,你就可以在左边提交你的页面链接。
|
||||
</section>
|
||||
res.redirect( '/');
|
||||
}; </pre>现在,在包含呈现页面的函数的get请求的参数之前,将<em>ensureAuthenticated</em>作为中间件添加到配置文件页面的请求中。 <pre> app.route( '/简档')
|
||||
.get(ensureAuthenticated,(req,res)=> {
|
||||
res.render(process.cwd()+'/ views / pug / profile');
|
||||
}); </pre>当您认为自己已经做对时,请提交您的页面。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -45,9 +26,9 @@ app
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: <code>ensureAuthenticated</code>中间件应添加到<code>/profile</code>路由中。
|
||||
- text: 中间件确保应该在我们的/配置文件路由上实现
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /ensureAuthenticated[^]*req.isAuthenticated/gi, 'Your ensureAuthenticated middleware should be defined and utilize the req.isAuthenticated function'); assert.match(data, /profile[^]*get[^]*ensureAuthenticated/gi, 'Your ensureAuthenticated middleware should be attached to the /profile route'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 如果没有通过验证,对 /profile 的 GET 请求应重定向到 /
|
||||
- text: 正确的Get请求/配置文件重定向到/因为我们未经过身份验证
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/profile') .then(data => { assert.match(data, /Home page/gi, 'An attempt to go to the profile at this point should redirect to the homepage since we are not logged in'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -63,11 +44,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,24 +2,15 @@
|
||||
id: 589fc831f9fc0f352b528e76
|
||||
title: Handle a Disconnect
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301552
|
||||
localeTitle: 处理连接断开
|
||||
videoUrl: ''
|
||||
localeTitle: 处理断开连接
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>GitHub</a> 上克隆。
|
||||
你也许注意到,目前为止我们只处理用户数量的增加,没有处理减少。事实上,处理用户断开连接也很简单。区别在于,新连接的监听是发生在整个服务器上,但连接断开的监听是发生在每个 socket 上。
|
||||
<hr>为此,我们需要在目前的监听回调里面监听 socket 断开连接的事件。在断开连接的回调函数中,我们可以不传任何参数,但你可以在这里添加连接断开的测试代码:<code>socket.on('disconnect', () => { /* 在这里添加连接断开的测试代码 */ });</code>
|
||||
为确保客户端可以看到实时的用户数量,显然,我们应该在用户断开时让 currentUsers 减 1,然后发送 'user count' 事件,并使用修改后的用户数量。
|
||||
<strong>注意:</strong><br>和 'disconnect' 类似,所有 socket 可以发送到服务器的事件,我们都应该在有 'socket' 定义的连接监听器里处理。
|
||||
完成上述要求后,你就可以在左边提交你的页面链接。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-socketio/">GitHub</a>克隆的。您可能会注意到,到目前为止,您只增加了用户数。处理用户断开连接就像处理初始连接一样简单,除了区别在于你必须在每个套接字上监听它而不是在整个服务器上监听它。 <hr>为此,请在现有的连接侦听器中添加一个侦听器,该侦听器在没有数据传递的情况下侦听套接字上的“disconnect”。您只需登录用户已断开连接的控制台即可测试此功能。 <code>socket.on('disconnect', () => { /*anything you want to do on disconnect*/ });</code>要确保客户端持续获得当前用户的更新计数,您应该在断开连接时将currentUsers减少1,然后使用更新的计数发出'user count'事件! <strong>注意</strong> <br>就像'disconnect'一样,套接字可以向服务器发出的所有其他事件应该在我们定义了'socket'的连接监听器中处理。当您认为自己已经做对时,请提交您的页面。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -27,9 +18,9 @@ localeTitle: 处理连接断开
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 服务器应处理断开 socket 连接的事件。
|
||||
- text: 服务器处理与套接字的事件断开连接
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /socket.on.*('|")disconnect('|")/gi, ''); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: '客户端应监听 "user count" 事件。'
|
||||
- text: 您的客户正在侦听“用户计数”事件
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/public/client.js') .then(data => { assert.match(data, /socket.on.*('|")user count('|")/gi, 'Your client should be connection to server with the connection defined as socket'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -45,11 +36,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,24 +2,15 @@
|
||||
id: 58a25c98f9fc0f352b528e7f
|
||||
title: Hashing Your Passwords
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301553
|
||||
videoUrl: ''
|
||||
localeTitle: 哈希密码
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a> 上克隆。
|
||||
回过头来看信息安全,你也许记得在数据库中存储明文密码是<em>绝对</em>禁止的。现在,我们需要引入 BCrypt 来解决这个问题。
|
||||
<hr>添加 BCrypt 作为依赖,并通过<code>require</code>添加到服务器代码中。你需要在两个步骤中使用哈希运算:注册和保存新账户,以及登录时检查密码是否正确。
|
||||
目前处理注册的路由中,我们是这样把密码添加到数据库的:<code>password: req.body.password</code>。我们可以通过这段代码创建哈希值:<code>var hash = bcrypt.hashSync(req.body.password, 12);</code>,然后就可以把<code>passsword: req.body.password</code>替换为<code>password: hash</code>。
|
||||
最后,在验证逻辑中,我们已经有这样一段代码执行检查:<code>if (password !== user.password) { return done(null, false); }</code>。但我们现在存储的密码<code>user.password</code>已经是哈希值了。由于目前的检测机制是密码不匹配时就返回未认证,因此修改后,用于比对用户密码哈希值的代码应该是这样:<code>if (!bcrypt.compareSync(password, user.password)) { return done(null, false); }</code>。
|
||||
当你需要存储密码时,这样做可以有效地提升网站的安全性。完成上述要求后,你就可以在左边提交你的页面链接。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-advancednode/">GitHub</a>克隆的。回到信息安全部分,您可能还记得存储明文密码<em>永远不会</em>好。现在是时候实施BCrypt来解决这个问题了。 <hr>将BCrypt添加为依赖项并在服务器中将其需要。您需要在两个关键区域处理散列:您在哪里处理注册/保存新帐户以及在登录时检查密码是否正确。目前在我们的注册路线上,您将用户的密码插入数据库,如下所示: <code>password: req.body.password</code> 。实现保存哈希的一种简单方法是在数据库逻辑<code>var hash = bcrypt.hashSync(req.body.password, 12);</code>之前添加以下内容<code>var hash = bcrypt.hashSync(req.body.password, 12);</code>并使用<code>password: hash</code>替换数据库保存中的<code>req.body.password</code> 。最后在我们的身份验证策略中,我们在完成流程之前在代码中检查以下内容: <code>if (password !== user.password) { return done(null, false); }</code> 。完成之前的更改后,现在<code>user.password</code>是一个哈希。在更改现有代码之前,请注意语句如何检查密码是否不相等,然后返回未经过身份验证的密码。考虑到这一点,您的代码可能如下所示,以正确检查针对哈希输入的密码: <code>if (!bcrypt.compareSync(password, user.password)) { return done(null, false); }</code>这是所有需要实现的最重要的安全功能之一,当你有来存储密码!当您认为自己已经做对时,请提交您的页面。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -27,9 +18,9 @@ localeTitle: 哈希密码
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应存在 BCrypt 依赖。
|
||||
- text: BCrypt是一种依赖
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/package.json') .then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, 'bcrypt', 'Your project should list "bcrypt" as a dependency'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: BCrypt 应正确地引入和调用。
|
||||
- text: BCrypt正确需要并实施
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /require.*("|')bcrypt("|')/gi, 'You should have required bcrypt'); assert.match(data, /bcrypt.hashSync/gi, 'You should use hash the password in the registration'); assert.match(data, /bcrypt.compareSync/gi, 'You should compare the password to the hash in your strategy'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -45,11 +36,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,23 +2,15 @@
|
||||
id: 5895f70ef9fc0f352b528e6b
|
||||
title: How to Put a Profile Together
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301554
|
||||
localeTitle: 如何将 Profile 放在一起
|
||||
videoUrl: ''
|
||||
localeTitle: 如何将配置文件放在一起
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a> 上克隆。
|
||||
现在,只有通过验证的用户才能进入 <em>/profile</em> 页面,这样我们就可以在页面上使用 'req.user' 里的信息了。
|
||||
请在变量中包含 <em>username</em> 键,值为 'req.user.username',并通过 render 方法传给 profile 页面。然后在 'profile.pug' 页面,添加这行<code>h2.center#welcome Welcome, #{username}!</code>代码来创建类为<code>center</code>、id 为<code>welcome</code>且文本内容为 'Welcome, ' 后加用户名的 h2 元素。
|
||||
以及,请在 profile 里添加 <em>/logout</em> 链接,后续会用于处理用户退出登录的逻辑:<code>a(href='/logout') Logout</code>
|
||||
完成上述要求后,你就可以在左边提交你的页面链接。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-advancednode/">GitHub</a>克隆的。现在我们可以确保访问<em>/ profile</em>的用户已经过身份验证,我们可以使用页面上“req.user”中包含的信息!继续将包含变量<em>username</em> equaling'req.user.username'的对象传递到配置文件视图的render方法中。然后转到你的'profile.pug'视图并添加第<code>h2.center#welcome Welcome, #{username}!</code>行<code>h2.center#welcome Welcome, #{username}!</code>使用“center”类创建h2元素,并使用包含文本“Welcome”和用户名的id“welcome”创建!同样在配置文件中,添加指向<em>/ logout</em>的链接。该路由将托管用于取消身份验证用户的逻辑。 <code>a(href='/logout') Logout</code>当您认为自己正确时,请提交您的页面。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -26,7 +18,7 @@ localeTitle: 如何将 Profile 放在一起
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应在 Pug render 中给 /profile 传一个变量。
|
||||
- text: 正确地将Pug渲染变量添加到/ profile
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /\/views\/pug\/profile[^]*username:( |)req.user.username/gi, 'You should be passing the variable username with req.user.username into the render function of the profile page'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -42,11 +34,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,25 +2,15 @@
|
||||
id: 5895f70df9fc0f352b528e69
|
||||
title: How to Use Passport Strategies
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301555
|
||||
localeTitle: 如何使用 Passport 策略
|
||||
videoUrl: ''
|
||||
localeTitle: 如何使用Passport策略
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a> 上克隆。
|
||||
在提供的 index.pug 文件里有一个登录表单。因为这个表单中存在行内 JavaScript 代码<code>if showLogin</code>,因此它是隐藏的。因为 showLogin 未定义,所以表单不会渲染。如果在该页面的<code>res.render()</code>里添加一个包含<code>showLogin: true</code>的对象,你就可以在刷新页面后看到表单。当你点击 login 时,表单会向服务器的 <em>/login</code> 发送 POST 请求,此时服务器端就可以接受 POST 请求信息并进行用户验证。
|
||||
在这次挑战中,你需要为 POST 请求添加路由<code>/login</code>。为了用这个路由进行验证,你需要添加一个中间件,中间件应作为参数添加到用于处理请求的回调函数<code>function(req,res)</code>之前。对于 passport 的验证中间件,应这样调用:<code>passport.authenticate('local')</code>。
|
||||
<em>passport.authenticate</em> 也接收选项作为参数,这些选项用于设置验证,例如<code>{ failureRedirect: '/' }</code>就很有用,请记得添加到你的代码中。如果中间件验证通过,我们就应该提供相应的后续处理。在这个挑战中,我们需要让用户重定到 <em>/profile</em>,这样<code>profile.pug</code>页面就会渲染。
|
||||
如果验证通过,用户对象将会储存到 <em>req.user</em> 中。
|
||||
这时,如果你在表单里输入了用户名和密码,路由将会重定向到主页 <em>/</em>,在服务端将会打印 'User {USERNAME} attempted to log in.',由于现在我们还没有实现注册功能,因此所有登录尝试都会失败。
|
||||
完成上述要求后,你就可以在左边提交你的页面链接。如果运行出错,你可以在<a href='https://gist.github.com/JosephLivengood/8a335d1a68ed9170da02bb9d8f5b71d5'>这里</a>检查你的项目完成情况。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-advancednode/">GitHub</a>克隆的。在提供的index.pug文件中,实际上有一个登录表单。它之前已被隐藏,因为内联javascript <code>if showLogin</code>的形式缩进后。在showLogin作为变量从未定义之前,它从未呈现包含该表单的代码块。继续在该页面的res.render上向对象<code>showLogin: true</code>添加一个新变量。刷新页面时,您应该看到表单!此表单设置为<b>POST</b> on <em>/ login,</em>因此我们应该设置此接受POST并验证用户身份。对于此挑战,您应添加路由/登录以接受POST请求。要在此路由上进行身份验证,您需要添加中间件才能发送响应。这是通过在您的<code>function(req,res)</code>之前使用中间件传递另一个参数来完成的!要使用的中间件是<code>passport.authenticate('local')</code> 。 <em>passport.authenticate</em>也可以将一些选项作为参数,例如: <code>{ failureRedirect: '/' }</code>这非常有用,所以一定要添加它。作为使用中间件后的响应(只有在身份验证中间件通过时才会调用)应该是将用户重定向到<em>/ profile,</em>并且该路由应该呈现视图'profile.pug'。如果身份验证成功,则用户对象将保存在<em>req.user中</em> 。现在,如果您在表单中输入用户名和密码,它应该重定向到主页<em>/</em>并且在服务器的控制台中应该是'用户{USERNAME}尝试登录'。因为我们目前无法登录未注册的用户。当您认为自己已经做对时,请提交您的页面。如果您遇到错误,可以<a href="https://gist.github.com/JosephLivengood/8a335d1a68ed9170da02bb9d8f5b71d5">在这里查看</a>到目前为止完成的项目。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -28,9 +18,9 @@ localeTitle: 如何使用 Passport 策略
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: server.js 中应正确执行所有步骤。
|
||||
- text: 所有步骤都在server.js中正确实现
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /showLogin:( |)true/gi, 'You should be passing the variable "showLogin" as true to your render function for the homepage'); assert.match(data, /failureRedirect:( |)('|")\/('|")/gi, 'Your code should include a failureRedirect to the "/" route'); assert.match(data, /login[^]*post[^]*local/gi, 'You should have a route for login which accepts a POST and passport.authenticates local'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 到 /login 的 POST 请求应重定向到 /
|
||||
- text: 对/ login的POST请求正确重定向到/
|
||||
testString: getUserInput => $.post(getUserInput('url')+ '/login') .then(data => { assert.match(data, /Looks like this page is being rendered from Pug into HTML!/gi, 'A login attempt at this point should redirect to the homepage since we do not have any registered users'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -46,11 +36,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,37 +2,21 @@
|
||||
id: 5895f70cf9fc0f352b528e67
|
||||
title: Implement the Serialization of a Passport User
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301556
|
||||
localeTitle: 实现 Passport 用户的序列化
|
||||
videoUrl: ''
|
||||
localeTitle: 实现Passport用户的序列化
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a> 上克隆。
|
||||
截至目前,我们还没有配置完数据库,因此还无法加载用户数据。实现这个的方式很多,但对于我们的项目,一旦服务器启动,那么只要有 app 实例在运行,数据库就应一直处于连接状态。
|
||||
为此,我们要添加 MongoDB 作为依赖,并在 server 中<code>require</code>它,就像这样:<code>const mongo = require('mongodb').MongoClient;</code>。
|
||||
在连接数据库之后,我们才能让服务器开始监听请求,这样做可以保证服务器在数据库连接前或数据库发生错误时不接受任何请求。为此,我们需要这样写:
|
||||
|
||||
```js
|
||||
mongo.connect(process.env.DATABASE, (err, db) => {
|
||||
if(err) {
|
||||
console.log('Database error: ' + err);
|
||||
} else {
|
||||
console.log('Successful database connection');
|
||||
|
||||
//serialization and app.listen
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
现在,请把上一个挑战中我们注释掉的<code>deserializeUser</code>中的代码块添加回来,然后删掉<code>done(null, null)</code>。请确保你已经在 .env 文件里配置了<code>DATABASE</code>的数据库连接字段,例如:<code>DATABASE=mongodb://admin:pass@mlab.com:12345/my-project</code>。你可以在 <a href='https://mlab.com/welcome/'>mLab</a> 上创建一个免费的数据库。至此,序列化的创建完成。
|
||||
完成上述要求后,你就可以在左边提交你的页面链接。如果运行出错,你可以在<a href="https://gist.github.com/JosephLivengood/e192e809a1d27cb80dc2c6d3467b7477">这里</a>检查你的项目完成情况。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-advancednode/">GitHub</a>克隆的。现在我们没有加载实际的用户对象,因为我们还没有设置我们的数据库。这可以通过许多不同的方式完成,但是对于我们的项目,当我们启动服务器并在应用程序的整个生命周期中保持持久连接时,我们将连接到数据库。为此,请将MongoDB添加为依赖项,并在服务器中将其需要。 ( <code>const mongo = require('mongodb').MongoClient;</code> )现在我们想要连接到我们的数据库,然后开始侦听请求。这样做的目的是在连接数据库之前或者出现数据库错误时不允许请求。要实现此目的,您需要在以下内容中包含序列化和应用程序侦听器: <pre> mongo.connect(process.env.DATABASE,(err,db)=> {
|
||||
if(错误){
|
||||
console.log('数据库错误:'+错误);
|
||||
} else {
|
||||
console.log('成功的数据库连接');
|
||||
<pre> <code> //serialization and app.listen</code> </pre>
|
||||
<p> }}); </p></pre>您现在可以在deserializeUser中取消注释该块并删除您的<code>done(null, null)</code> 。确保将.env文件中的<em>DATABASE</em>设置为数据库的连接字符串(例如: <code>DATABASE=mongodb://admin:pass@mlab.com:12345/my-project</code> )。您可以在<a href="https://mlab.com/welcome/">mLab</a>上设置免费数据库。恭喜 - 您已完成序列化设置!当您认为自己已经做对时,请提交您的页面。如果您遇到错误,可以<a href="https://gist.github.com/JosephLivengood/e192e809a1d27cb80dc2c6d3467b7477">在这里查看</a>到目前为止完成的项目。 <p></p></section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -40,9 +24,9 @@ mongo.connect(process.env.DATABASE, (err, db) => {
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应存在数据库连接。
|
||||
- text: 存在数据库连接
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /mongo.connect/gi, 'You should have created a connection to your database'); assert.match(data, /mongo.connect[^]*app.listen[^]*}[^]*}/gi, 'You should have your app.listen nested at within your database connection at the bottom'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 反序列化应正确使用,且应移除<code>done(null, null)</code>。
|
||||
- text: 反序列化现在正确使用DB并且擦除了<code>done(null, null)</code>
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.notMatch(data, /null,( |)null/gi, 'The callback in deserializeUser of (null, null) should be completely removed for the db block uncommented out'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -58,11 +42,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,37 +2,24 @@
|
||||
id: 589a69f5f9fc0f352b528e71
|
||||
title: Implementation of Social Authentication II
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301557
|
||||
videoUrl: ''
|
||||
localeTitle: 社会认证的实施II
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socialauth/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>GitHub</a> 上克隆。
|
||||
设置 GitHub 验证的最后一步是创建策略本身。为此,你需要在项目中<code>require</code>'passport-github',且实例化为 GithubStrategy:<code>const GitHubStrategy = require('passport-github').Strategy;</code>。
|
||||
为了设置 GitHub 策略,我们需要在 <b>passport</b> 中使用实例化的 <b>GithubStrategy</b>,它可以接收两个参数:一个对象(包括 <em>clientID</em>, <em>clientSecret</em> 和 <em>callbackURL</em>),以及一个回调函数。在这个回调函数中,我们要处理验证成功时,判断用户是否已经在数据库中存在的逻辑,还有如果数据库中不存在,把用户数据添加到数据库的代码。这种处理方式适用于绝大部分第三方验证策略,但有些策略会需要我们提供更多的信息,详情请参考相关策略的 README。例如,Google 的验证策略会要求你提供一个 <em>scope</em>,用于标示用户成功登录后,你需要从返回的对象中获取那些信息。以及,这也需要经过用户同意,你才可以获取到。当前我们使用的验证策略,你也可以从它 GitHub 的页面上了解它的用法,不过我们也会在这里进行详细讲解。
|
||||
你的新策略应该这样去实现:
|
||||
|
||||
```js
|
||||
passport.use(new GitHubStrategy({
|
||||
clientID: process.env.GITHUB_CLIENT_ID,
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
||||
callbackURL: /*INSERT CALLBACK URL ENTERED INTO GITHUB HERE*/
|
||||
},
|
||||
function(accessToken, refreshToken, profile, cb) {
|
||||
console.log(profile);
|
||||
//Database logic here with callback containing our user object
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socialauth/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-socialauth/">GitHub</a>克隆的。设置Github身份验证的最后一部分是创建策略本身。为此,您需要将'passport-github'的依赖项添加到项目中,并将其作为GithubStrategy,例如<code>const GitHubStrategy = require('passport-github').Strategy;</code> 。要设置Github策略,你必须告诉<b>护照</b> <b>使用</b>实例化的<b>GithubStrategy</b> ,它接受2个参数:一个对象(包含<em>clientID</em> , <em>clientSecret</em>和<em>callbackURL</em> )和一个在成功验证用户时要调用的函数,我们将确定如果用户是新用户以及最初要保存在用户数据库对象中的字段。这在许多策略中很常见,但有些可能需要更多信息,如特定策略的github README所述;例如,Google也需要一个<em>范围</em>来确定您的请求所返回的信息类型,并要求用户批准此类访问。我们正在实施的当前策略<a>在此处</a>列出<a>了</a>它的用法,但我们将在freeCodeCamp上完成所有这些工作!以下是您的新战略应该如何看待这一点: <pre> passport.use(new GitHubStrategy({
|
||||
clientID:process.env.GITHUB_CLIENT_ID,
|
||||
clientSecret:process.env.GITHUB_CLIENT_SECRET,
|
||||
callbackURL:/ * INSERT CALLBACK URL在这里输入GITHUB * /
|
||||
},
|
||||
function(accessToken,refreshToken,profile,cb){
|
||||
的console.log(配置文件);
|
||||
//这里的数据库逻辑带有包含我们用户对象的回调
|
||||
}
|
||||
));
|
||||
```
|
||||
|
||||
目前,你的验证部分不会成功。由于没有数据库的逻辑和回调函数,你的代码目前还会报错。但如果你试一试,就可以在右边的控制台里看到输出了你的 GitHub 的个人信息。
|
||||
完成上述要求后,你就可以在左边提交你的页面链接。
|
||||
</section>
|
||||
)); </pre>您的身份验证还不会成功,并且实际上会抛出错误,没有数据库逻辑和回调,但如果您尝试它,它应该将您的Github配置文件记录到您的控制台!当您认为自己已经做对时,请提交您的页面。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -40,11 +27,11 @@ passport.use(new GitHubStrategy({
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应添加依赖。
|
||||
- text: 依赖性增加了
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/package.json') .then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, 'passport-github', 'Your project should list "passport-github" as a dependency'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 应引入依赖
|
||||
- text: 需要依赖性
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /require.*("|')passport-github("|')/gi, 'You should have required passport-github'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 到目前为止,Github 策略应正确设置。
|
||||
- text: 到目前为止,Github策略设置正确
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /passport.use.*new GitHubStrategy/gi, 'Passport should use a new GitHubStrategy'); assert.match(data, /callbackURL:( |)("|').*("|')/gi, 'You should have a callbackURL'); assert.match(data, /process.env.GITHUB_CLIENT_SECRET/g, 'You should use process.env.GITHUB_CLIENT_SECRET'); assert.match(data, /process.env.GITHUB_CLIENT_ID/g, 'You should use process.env.GITHUB_CLIENT_ID'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -60,11 +47,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,46 +2,34 @@
|
||||
id: 589a8eb3f9fc0f352b528e72
|
||||
title: Implementation of Social Authentication III
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301558
|
||||
localeTitle: 实现第三种社交登录
|
||||
videoUrl: ''
|
||||
localeTitle: 社会认证的实施III
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socialauth/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>GitHub</a> 上克隆。
|
||||
验证策略的最后一部分是处理从 GitHub 返回的个人信息。如果用户存在,我们就需要从数据库中读取用户数据并在 profile 页面加载;否则,我们需要把用户信息添加到数据库。GitHub 在用户信息中为我们提供了独一无二的 <em>id</em>,我们可以通过序列化的 id 在数据库中搜索用户(已实现)。以下是这个逻辑的实现示例,我们应该把它传到新策略的第二个参数,就是目前<code>console.log(profile);</code>的下方:
|
||||
|
||||
```js
|
||||
db.collection('socialusers').findAndModify(
|
||||
{id: profile.id},
|
||||
{},
|
||||
{$setOnInsert:{
|
||||
id: profile.id,
|
||||
name: profile.displayName || 'John Doe',
|
||||
photo: profile.photos[0].value || '',
|
||||
email: profile.emails[0].value || 'No public email',
|
||||
created_on: new Date(),
|
||||
provider: profile.provider || ''
|
||||
},$set:{
|
||||
last_login: new Date()
|
||||
},$inc:{
|
||||
login_count: 1
|
||||
}},
|
||||
{upsert:true, new: true},
|
||||
(err, doc) => {
|
||||
return cb(null, doc.value);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
<code>findAndModify</code>的作用是在数据库中查询对象并更新,如果对象不存在,我们也可以<code>upsert</code>(注,upsert 可以理解为 update + insert)它,然后我们可以在回调方法里获取到插入数据后的新对象。在这个例子中,我们会把 last_login 设置成为 now,而且总会为 login_count 加 1。只有在插入一个新对象(新用户)时,我们才会初始化这些字段。另外,还需要注意默认值的使用。有时返回的用户信息可能不全,可能是因为用户没有填写,也可能是因为用户选择不公开一部分信息。在这种情况下,我们需要进行相应的处理,以防我们的 app 报错。
|
||||
你现在应该可以登录你的应用了,试试吧。完成上述要求后,你就可以在左边提交你的页面链接。如果运行出错,你可以在 <a href='https://glitch.com/#!/project/guttural-birch'>here</a> 这里检查这个迷你项目的完成代码。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socialauth/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-socialauth/">GitHub</a>克隆的。策略的最后一部分是处理从Github返回的配置文件。我们需要加载用户数据库对象(如果存在)或创建一个(如果不存在)并填充配置文件中的字段,然后返回用户的对象。 Github在每个配置文件中为我们提供了一个唯一的<em>ID</em> ,我们可以使用它来搜索(已经实现)用户序列化。下面是您可以在项目中使用的示例实现 - 它位于作为新策略的第二个参数的函数内,就在<code>console.log(profile);</code>目前是: <pre> db.collection( 'socialusers')。findAndModify(
|
||||
{id:profile.id},
|
||||
{},
|
||||
{$ setOnInsert:{
|
||||
id:profile.id,
|
||||
name:profile.displayName || 'John Doe',
|
||||
照片:profile.photos [0] .value || ”
|
||||
电子邮件:profile.emails [0] .value || '没有公开电子邮件',
|
||||
created_on:new Date(),
|
||||
provider:profile.provider || “
|
||||
} $设置:{
|
||||
last_login:新日期()
|
||||
} $ INC {
|
||||
login_count:1
|
||||
}},
|
||||
{upsert:true,new:true},
|
||||
(错误,doc)=> {
|
||||
return cb(null,doc.value);
|
||||
}
|
||||
); </pre>使用findAndModify,它允许您搜索对象并对其进行更新,如果对象不存在则将其置换,并在每次回调函数中接收新对象。在这个例子中,我们总是将last_login设置为now,我们总是将login_count增加1,并且只有当我们插入一个新对象(新用户)时,我们才会填充大部分字段。需要注意的是使用默认值。有时,返回的个人资料不会填写所有信息,或者用户会选择保留私密信息;所以在这种情况下我们必须处理它以防止错误。你现在应该可以登录你的应用了 - 试试吧!当您认为自己已经做对时,请提交您的页面。如果你正在运行到错误,您可以检查出的这个小项目的完成代码的例子<a href="https://glitch.com/#!/project/guttural-birch">在这里</a> 。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -49,7 +37,7 @@ db.collection('socialusers').findAndModify(
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: GitHub 策略应配置完成。
|
||||
- text: Github策略设置完成
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /GitHubStrategy[^]*db.collection/gi, 'Strategy should use now use the database to search for the user'); assert.match(data, /GitHubStrategy[^]*socialusers/gi, 'Strategy should use "socialusers" as db collection'); assert.match(data, /GitHubStrategy[^]*return cb/gi, 'Strategy should return the callback function "cb"'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -65,11 +53,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,33 +2,18 @@
|
||||
id: 589a69f5f9fc0f352b528e70
|
||||
title: Implementation of Social Authentication
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301559
|
||||
localeTitle: 实现第一种社交登录
|
||||
videoUrl: ''
|
||||
localeTitle: 社会认证的实施
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socialauth/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>GitHub</a> 上克隆。
|
||||
第三方用户验证的实现逻辑如下:<ol><li>在用户点击按钮或者链接后,进入验证页面,通过第三方平台(如 GitHub)来进行用户验证。</li><li>需要在路由中调用<code>passport.authenticate('github')</code>,跳转至 GitHub 验证页面。</li><li>页面跳转到 GitHub 上,如果用户未登录 GitHub,就需要在这里进行登录。登录成功后,会出现向用户申请访问权限的确认页。</li><li>如果用户同意访问,则用户会回到我们提供的回调地址,带着 GitHub 那边提供的用户信息回到我们的 app 中。</li><li>验证已完成。在我们的应用中,我们需要查询这个用户是否已经存在。如果是新用户,那我们需要把他的用户信息存储到数据库。</li></ol>
|
||||
在 OAuth 验证策略中,我们至少需要提供 <em>Client ID</em> 和 <em>Client Secret</em>,这样第三方平台就会获悉验证请求的来源,以及这个来源是否有效。为此,需要去我们使用的第三方验证平台(比如 GitHub)获取这两个字段的值。注意,我们获取到的这个值是唯一的,且仅对我们的当前 app 有效——<b>因此,千万不要分享给别人</b>,更不要上传到公共仓库或者直接写在代码里。通常,我们会在 <em>.env</em> 文件里配置,并在 Node.js 里通过:<code>process.env.GITHUB_CLIENT_ID</code>获取。对于这次挑战,我们将会使用 GitHub 作为验证平台。
|
||||
首先,你需要进入账户设置里的 <a href='https://github.com/settings/developers'>Developer settings</a>板块,然后在 OAuth Apps 获取 <em>Client ID</em> 和 <em>Client Secret</em>。点击 'Register a new application',设置你的应用名称,然后把你的 glitch 主页地址(<b>注意,不是项目代码的地址</b>)粘贴到 Homepage URL。然后,回调 url 需要设置成上面 Homepage URL 里你粘贴的地址,但后面要加上 '/auth/github/callback'。这样在用户通过 Github 验证后才能跳转到我们指定的页面。别忘了,我们还需要在 .env 文件里配置好 'GITHUB_CLIENT_ID' 和 'GITHUB_CLIENT_SECRET'。
|
||||
然后,请在你现在的项目里,为 /auth/github 和 /auth/github/callback 创建两个接收 GET 请求的路由。第一个只需要通过 passport 来调用 'github' 验证,第二个需要调用 passport 来验证 'github',但需要在失败时跳转回主页 '/',成功是跳转到用户页面 '/profile'。跳转的逻辑与上一个项目中的逻辑一样。
|
||||
例如 '/auth/github/callback' 应该像我们处理在上一个项目中一般的登录一样:
|
||||
|
||||
```js
|
||||
app.route('/login')
|
||||
.post(passport.authenticate('local', { failureRedirect: '/' }), (req,res) => {
|
||||
res.redirect('/profile');
|
||||
});
|
||||
```
|
||||
|
||||
完成上述要求后,你就可以在左边提交你的页面链接。如果运行出错,你可以在<a href='https://gist.github.com/JosephLivengood/28ea2cae7e1dc6a53d7f0c42d987313b'>这里</a>检查你的项目完成情况。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socialauth/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-socialauth/">GitHub</a>克隆的。此类身份验证在您的应用中遵循的基本路径是: <ol><li>用户单击按钮或链接将它们发送到我们使用特定策略进行身份验证的路由(EG.GitHub) </li><li>您的路由呼叫<code>passport.authenticate('github')</code> ,将其重定向到Github。 </li><li>用户登陆的页面,在Github上,允许他们登录,如果他们还没有。然后它要求他们批准从我们的应用程序访问他们的个人资料。 </li><li>然后,如果用户获得批准,则会使用他们的个人资料将该用户返回到我们的应用。 </li><li>它们现在已经过身份验证,您的应用应检查它是否为返回的配置文件,如果不是,则将其保存在数据库中。 </li></ol> OAuth策略要求您至少拥有<em>客户端ID</em>和<em>客户端密钥</em> ,以便他们验证身份验证请求的来源以及是否有效。这些是从您尝试使用Github实现身份验证的站点获得的,并且对您的应用程序是唯一的 - 它们不会被<b>共享</b> ,不应该上传到公共存储库或直接在您的代码中编写。通常的做法是将它们放在<em>.env</em>文件中并引用它们: <code>process.env.GITHUB_CLIENT_ID</code> 。对于这个挑战,我们将使用Github策略。 <em><em>从Github</em></em>获取您的<em>客户ID和密码<em>是在“开发者设置”下的帐户配置文件设置中完成的,然后是“ <a href="https://github.com/settings/developers">OAuth应用程序</a> ”。点击“注册一个新的应用程序”,为您的应用命名,将网址粘贴到您的故障主页( <b>不是项目代码的网址</b> ),最后为回调网址,粘贴到与主页相同的网址,但使用'/ auth / github / callback'已添加。这是用户将被重定向到我们在Github上进行身份验证后处理的地方。将返回的信息保存为.env文件中的“GITHUB_CLIENT_ID”和“GITHUB_CLIENT_SECRET”。在重新混合的项目中,创建2条接受GET请求的路由:/ auth / github和/ auth / github / callback。第一个应该只调用护照来验证'github',第二个应该调用护照来验证'github',失败重定向到'/'然后如果成功重定向到'/ profile'(类似于我们的上一个项目)。 '/ auth / github / callback'应该如何看待的示例与我们在上一个项目中处理正常登录的方式类似:</em></em> <pre> <em><em>app.route( '/登录')
|
||||
.post(passport.authenticate('local',{failureRedirect:'/'}),(req,res)=> {
|
||||
res.redirect( '/简档');
|
||||
});</em></em> </pre> <em><em>当您认为自己已经做对时,请提交您的页面。如果您遇到错误,可以<a href="https://gist.github.com/JosephLivengood/28ea2cae7e1dc6a53d7f0c42d987313b">在此处</a>查看项目。</em></em> </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -36,9 +21,9 @@ app.route('/login')
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 路由 /auth/github 应正确配置。
|
||||
- text: Route / auth / github正确
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /('|")\/auth\/github('|")[^]*get.*passport.authenticate.*github/gi, 'Route auth/github should only call passport.authenticate with github'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 路由 /auth/github/callback 应正确配置。
|
||||
- text: Route / auth / github /回调正确
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /('|")\/auth\/github\/callback('|")[^]*get.*passport.authenticate.*github.*failureRedirect:( |)("|')\/("|')/gi, 'Route auth/github/callback should accept a get request and call passport.authenticate for github with a failure redirect to home'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -54,11 +39,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,41 +2,23 @@
|
||||
id: 58965611f9fc0f352b528e6c
|
||||
title: Logging a User Out
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301560
|
||||
localeTitle: 用户退出
|
||||
videoUrl: ''
|
||||
localeTitle: 记录用户
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a> 上克隆。
|
||||
创建退出登录的逻辑是比较简单的。只要用户尝试退出登录,路由就应重定向到主页,而不应该显示任何其他页面。
|
||||
在 passport 里,只需要在重定向前调用<code>req.logout();</code>即可完成用户登出。
|
||||
|
||||
```js
|
||||
app.route('/logout')
|
||||
.get((req, res) => {
|
||||
req.logout();
|
||||
res.redirect('/');
|
||||
});
|
||||
```
|
||||
|
||||
你可能注意到我们还没有处理 404 错误,这个错误码代表页面无法找到。在 Node.js 中我们通常会用如下的中间件来处理,请在所有路由之后添加这段代码:
|
||||
|
||||
```js
|
||||
app.use((req, res, next) => {
|
||||
res.status(404)
|
||||
.type('text')
|
||||
.send('Not Found');
|
||||
});
|
||||
```
|
||||
|
||||
完成上述要求后,你就可以在左边提交你的页面链接。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-advancednode/">GitHub</a>克隆的。创建注销逻辑很容易。路径应该只是取消认证用户并重定向到主页而不是渲染任何视图。在护照中, <code>req.logout();</code>认证用户就像调用<code>req.logout();</code>一样简单<code>req.logout();</code>在重定向之前。 <pre> app.route( '/注销')
|
||||
.get((req,res)=> {
|
||||
req.logout();
|
||||
res.redirect( '/');
|
||||
}); </pre>您可能已经注意到我们也没有处理丢失的页面(404),在Node中处理此问题的常用方法是使用以下中间件。继续在所有其他路线之后添加: <pre> app.use((req,res,next)=> {
|
||||
res.status(404)
|
||||
.TYPE( '文本')
|
||||
.send('未找到');
|
||||
}); </pre>当您认为自己已经做对时,请提交您的页面。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -44,9 +26,9 @@ app.use((req, res, next) => {
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应存在退出登录的路由。
|
||||
- text: 退出路线
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /req.logout/gi, 'You should be calling req.logout() in your /logout route'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 退出登录后应重定向到主页 /
|
||||
- text: 注销应该重定向到主页/
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/logout') .then(data => { assert.match(data, /Home page/gi, 'When a user logs out they should be redirected to the homepage'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -62,11 +44,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,55 +2,40 @@
|
||||
id: 58966a17f9fc0f352b528e6d
|
||||
title: Registration of New Users
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301561
|
||||
localeTitle: 注册新用户
|
||||
videoUrl: ''
|
||||
localeTitle: 新用户注册
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a> 上克隆。
|
||||
现在我们需要为新用户添加注册帐号的功能,首先我们需要在主页的 res.render 接收的变量对象中添加<code>showRegistration: true</code>。此时刷新页面,你会看到页面上已经显示了我们在 index.pug 文件中定义的注册表单。这个表单设置了请求路径 <em>/register</em>,并将请求方法设置成 <b>POST</b>,所以我们需要在服务器接受 <b>POST</b> 请求,且在数据库中创建用户对象。
|
||||
用户注册的逻辑如下:注册新用户 > 认证新用户 > 重定向到 /profile。
|
||||
对于步骤一的注册新用户,详细逻辑如下:用 findOne 命令查询数据库 > 如果返回了用户对象,则表示用户存在,然后返回主页;如果用户未定义且没有报错,则会将包含用户名和密码的用户对象通过<code>insertOne</code>添加到数据库,只要没有报错则会继续下一步:认证新用户——我们已经在 /login 路由的 POST 请求中写好了这部分逻辑。
|
||||
|
||||
```js
|
||||
app.route('/register')
|
||||
.post((req, res, next) => {
|
||||
db.collection('users').findOne({ username: req.body.username }, function(err, user) {
|
||||
if (err) {
|
||||
next(err);
|
||||
} else if (user) {
|
||||
res.redirect('/');
|
||||
} else {
|
||||
db.collection('users').insertOne({
|
||||
username: req.body.username,
|
||||
password: req.body.password
|
||||
},
|
||||
(err, doc) => {
|
||||
if (err) {
|
||||
res.redirect('/');
|
||||
} else {
|
||||
next(null, user);
|
||||
}
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-advancednode/">GitHub</a>克隆的。现在我们需要允许我们网站上的新用户注册一个帐户。在主页的res.render上,向传递的对象添加一个新变量 - <code>showRegistration: true</code> 。刷新页面时,您应该会看到已在index.pug文件中创建的注册表单!此表单设置为<b>POST</b> on <em>/ register,</em>因此我们应该设置此接受POST并在数据库中创建用户对象。注册路由的逻辑应如下所示:注册新用户>验证新用户>重定向到/配置文件步骤1的逻辑,注册新用户,应如下所示:使用findOne命令查询数据库>如果用户返回然后它存在并重定向回到主页<em>或者</em>如果用户未定义且没有发生错误,则使用用户名和密码将“insertOne”输入数据库,只要没有错误发生,然后调用<em>next</em>转到步骤2,验证新的user,我们已经在POST / login路由中编写了逻辑。 <pre> app.route( '/寄存器')
|
||||
.post((req,res,next)=> {
|
||||
db.collection('users')。findOne({username:req.body.username},function(err,user){
|
||||
if(错误){
|
||||
下一个(ERR);
|
||||
} else if(user){
|
||||
res.redirect( '/');
|
||||
} else {
|
||||
db.collection( '用户')。insertOne(
|
||||
{username:req.body.username,
|
||||
密码:req.body.password},
|
||||
(错误,doc)=> {
|
||||
if(错误){
|
||||
res.redirect( '/');
|
||||
} else {
|
||||
next(null,user);
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
passport.authenticate('local', { failureRedirect: '/' }),
|
||||
(req, res, next) => {
|
||||
res.redirect('/profile');
|
||||
})},
|
||||
passport.authenticate('local',{failureRedirect:'/'}),
|
||||
(req,res,next)=> {
|
||||
res.redirect( '/简档');
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
完成上述要求后,你就可以在左边提交你的页面链接。如果运行出错,你可以在<a href='https://gist.github.com/JosephLivengood/6c47bee7df34df9f11820803608071ed'>这里</a>检查你的项目完成情况。
|
||||
</section>
|
||||
); </pre>当您认为自己已经做对时,请提交您的页面。如果您遇到错误,可以<a href="https://gist.github.com/JosephLivengood/6c47bee7df34df9f11820803608071ed">在这里查看</a>到目前为止完成的项目。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -58,15 +43,15 @@ app.route('/register')
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 注册路由和显示主页。
|
||||
- text: 注册路线并在家中显示
|
||||
testString: 'getUserInput => $.get(getUserInput(''url'')+ ''/_api/server.js'') .then(data => { assert.match(data, /showRegistration:( |)true/gi, ''You should be passing the variable "showRegistration" as true to your render function for the homepage''); assert.match(data, /register[^]*post[^]*findOne[^]*username:( |)req.body.username/gi, ''You should have a route accepted a post request on register that querys the db with findone and the query being "username: req.body.username"''); }, xhr => { throw new Error(xhr.statusText); })'
|
||||
- text: 注册功能应可以正常运行。
|
||||
- text: 注册应该工作
|
||||
testString: 'getUserInput => $.ajax({url: getUserInput(''url'')+ ''/register'',data: {username: ''freeCodeCampTester'', password: ''freeCodeCampTester''},crossDomain: true, type: ''POST'', xhrFields: { withCredentials: true }}) .then(data => { assert.match(data, /Profile/gi, ''I should be able to register and it direct me to my profile. CLEAR YOUR DATABASE if this test fails (each time until its right!)''); }, xhr => { throw new Error(xhr.statusText); })'
|
||||
- text: 登录功能应可以正常运行。
|
||||
- text: 登录应该工作
|
||||
testString: 'getUserInput => $.ajax({url: getUserInput(''url'')+ ''/login'',data: {username: ''freeCodeCampTester'', password: ''freeCodeCampTester''}, type: ''POST'', xhrFields: { withCredentials: true }}) .then(data => { assert.match(data, /Profile/gi, ''Login should work if previous test was done successfully and redirect successfully to the profile. Check your work and clear your DB''); assert.match(data, /freeCodeCampTester/gi, ''The profile should properly display the welcome to the user logged in''); }, xhr => { throw new Error(xhr.statusText); })'
|
||||
- text: 退出登录功能应可以正常运行。
|
||||
- text: 注销应该有效
|
||||
testString: 'getUserInput => $.ajax({url: getUserInput(''url'')+ ''/logout'', type: ''GET'', xhrFields: { withCredentials: true }}) .then(data => { assert.match(data, /Home/gi, ''Logout should redirect to home''); }, xhr => { throw new Error(xhr.statusText); })'
|
||||
- text: 退出登录后,profile 页面应无法访问。
|
||||
- text: 注销后配置文件不再有效
|
||||
testString: 'getUserInput => $.ajax({url: getUserInput(''url'')+ ''/profile'', type: ''GET'', crossDomain: true, xhrFields: { withCredentials: true }}) .then(data => { assert.match(data, /Home/gi, ''Profile should redirect to home when we are logged out now again''); }, xhr => { throw new Error(xhr.statusText); })'
|
||||
|
||||
```
|
||||
@@ -82,11 +67,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,24 +2,15 @@
|
||||
id: 589fc832f9fc0f352b528e79
|
||||
title: Send and Display Chat Messages
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301562
|
||||
videoUrl: ''
|
||||
localeTitle: 发送和显示聊天消息
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>GitHub</a> 上克隆。
|
||||
现在,我们可以开始实现聊天室功能了。整体逻辑很简单,只需要获取用户发给服务端的消息,再通过服务端给所有客户端发送信息就可以了。在 client.js 文件里,你应该已经注意到了这段提交表单的代码:<code>$('form').submit(function(){ /*logic*/ });</code>
|
||||
<hr>现在我们需要处理事件的 emit,它应该发生在定义 'messageToSend' 之后,以及清除<code>#m</code>中的文本之前。我们称这个事件叫 'chat message',需发送的数据叫 'messageToSend':<code>socket.emit('chat message', messageToSend);</code>
|
||||
在服务端,我们需要监听包含 'message' 的 'chat message' 事件。一旦事件发生,我们就通过<code>io.emit</code> 把包含 'name' 和 'message' 的 'chat message' 事件发送给所有已连接的 socket。
|
||||
回到客户端,我们需要监听 'chat message' 事件。只要接收到这个事件,就把包含名字和消息的内容(注意:需要在名字后添加冒号)通过<code><li></code>添加到<code>#messages</code>。
|
||||
至此,我们已经完成发送信息到所有客户端的功能。完成上述要求后,你就可以在左边提交你的页面链接。如果运行出错,你可以在 <a href='https://gist.github.com/JosephLivengood/3e4b7750f6cd42feaa2768458d682136'>这里对应服务端</a> 和 <a href='https://gist.github.com/JosephLivengood/41ba76348df3013b7870dc64861de744'>这里对应客户端</a> 来检查完成代码。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-socketio/">GitHub</a>克隆的。是时候开始允许客户端向服务器发送聊天消息以向所有客户端发送消息!已经在您的client.js文件中,您应该看到在提交messgae表单时已经存在一段代码处理! ( <code>$('form').submit(function(){ /*logic*/ });</code> ) <hr>在您处理表单提交的代码中,您应该在定义“messageToSend”之后但在清除文本框<code>#m</code>之前发出事件。该事件应命名为“聊天消息”,数据应为“messageToSend”。 <code>socket.emit('chat message', messageToSend);</code>现在,在您的服务器上,您应该收听事件“聊天消息”的套接字,并将数据命名为“message”。一旦接收到事件,应该然后发射所述事件“聊天消息”到所有插座<code>io.emit</code>与数据为含“名称”和“报文”的对象。现在再次在您的客户端上,您现在应该监听事件“聊天消息”,并在收到时,将一个列表项追加到<code>#messages</code> ,其名称为冒号和消息!此时聊天功能齐全,并在所有客户端发送消息!当您认为自己已经做对时,请提交您的页面。如果您遇到错误,可以<a href="https://gist.github.com/JosephLivengood/3e4b7750f6cd42feaa2768458d682136">在此处</a>检查项目到此时<a href="https://gist.github.com/JosephLivengood/3e4b7750f6cd42feaa2768458d682136">为服务器</a>和<a href="https://gist.github.com/JosephLivengood/41ba76348df3013b7870dc64861de744">客户端</a> 。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -27,9 +18,9 @@ localeTitle: 发送和显示聊天消息
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: '服务端应监听 "chat message",且应在监听到后 emit。'
|
||||
- text: 服务器侦听“聊天消息”,然后正确发出
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /socket.on.*('|")chat message('|")[^]*io.emit.*('|")chat message('|").*name.*message/gi, 'Your server should listen to the socket for "chat message" then emit to all users "chat message" with name and message in the data object'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: '客户端应正确处理和展示从 "chat message" 事件发来的新数据。'
|
||||
- text: 客户端正确处理和显示事件“聊天消息”中的新数据
|
||||
testString: "getUserInput => $.get(getUserInput('url')+ '/public/client.js') .then(data => { assert.match(data, /socket.on.*('|\")chat message('|\")[^]*messages.*li/gi, 'You should append a list item to #messages on your client within the \"chat message\" event listener to display the new message'); }, xhr => { throw new Error(xhr.statusText); })"
|
||||
|
||||
```
|
||||
@@ -45,11 +36,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,39 +2,24 @@
|
||||
id: 5895f70cf9fc0f352b528e66
|
||||
title: Serialization of a User Object
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301563
|
||||
videoUrl: ''
|
||||
localeTitle: 用户对象的序列化
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a> 上克隆。
|
||||
序列化和反序列化在身份认证中是很重要的概念。序列化一个对象就是将其内容转换成一个体积很小的 <em>key</em>,后续可以通过这个 <em>key</em> 反序列化为原始对象。这样,服务器就可以在用户未登录时识别用户,或者说给这个用户一个唯一标识,用户也不需要在每次访问不同页面时都给服务器发送用户名和密码。
|
||||
我们需要用到序列化和反序列化的方法来进行配置。passport 为我们提供了<code>passport.serializeUser( OURFUNCTION )</code>和<code>passport.deserializeUser( OURFUNCTION )</code>两个方法。
|
||||
<code>serializeUser</code>方法接收两个参数,分别是表示用户的对象和一个回调函数。其中,回调函数的返回值应为这个用户的唯一标识符:最简单的写法就是让它返回用户的<code>_id</code>,这个<code>_id</code>属性是 MongoDB 为用户创建的唯一字段。类似地,反序列化也接收两个参数,分别是在序列化时生成的标识符以及一个回调函数。在回调函数里,我们需要根据根据传入的标识符(比如 _id)返回表示用户的对象。为了在 MongoDB 中通过 query(查询语句)获取 _id 字段,首先我们需要引入 MongoDB 的<code>ObjectID</code>方法:<code>const ObjectID = require('mongodb').ObjectID;</code>;然后调用它:<code>new ObjectID(THE_ID)</code>。当然,这一切的前提都是先引入 MongoDB 作为依赖。你可以在下面的例子中看到:
|
||||
|
||||
```js
|
||||
passport.serializeUser((user, done) => {
|
||||
done(null, user._id);
|
||||
});
|
||||
passport.deserializeUser((id, done) => {
|
||||
db.collection('users').findOne(
|
||||
{_id: new ObjectID(id)},
|
||||
(err, doc) => {
|
||||
done(null, doc);
|
||||
}
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
注意:在完全配置好 MongoDB 前,<code>deserializeUser</code>会抛出错误。因此,现在请先注释掉上面的代码,在回调函数中仅仅调用<code>done(null, null)</code>即可。
|
||||
完成上述要求后,你就可以在左边提交你的页面链接。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-advancednode/">GitHub</a>克隆的。序列化和反序列化是身份验证方面的重要概念。序列化对象意味着将其内容转换为一个小<em>键,</em>基本上可以将其反序列化为原始对象。这使我们能够知道与服务器通信的人,而无需在每次请求新页面时发送用户名和密码等身份验证数据。要正确设置它,我们需要一个序列化函数和一个反序列化函数。在护照中,我们使用<code>passport.serializeUser( OURFUNCTION )</code>和<code>passport.deserializeUser( OURFUNCTION )</code>创建它们。使用2个参数调用serializeUser,完整的用户对象和护照使用的回调。在回调中返回应该是唯一的键来标识该用户 - 最容易使用的用户是对象中的用户_id,因为它应该是MongoDb生成的唯一用户。类似地,使用该密钥和护照的回调函数调用deserializeUser,但这次我们必须获取该密钥并将用户完整对象返回到回调。要进行查询搜索Mongo _id,您必须创建<code>const ObjectID = require('mongodb').ObjectID;</code> ,然后使用它调用<code>new ObjectID(THE_ID)</code> 。一定要将MongoDB添加为依赖项。您可以在以下示例中看到: <pre> passport.serializeUser((user,done)=> {
|
||||
done(null,user._id);
|
||||
}); </pre><br><pre> passport.deserializeUser((id,done)=> {
|
||||
db.collection( '用户')。findOne(
|
||||
{_id:new ObjectID(id)},
|
||||
(错误,doc)=> {
|
||||
完成(null,doc);
|
||||
}
|
||||
);
|
||||
}); </pre>注意:这个deserializeUser将抛出一个错误,直到我们在下一步中设置数据库,因此注释掉整个块并在函数deserializeUser中调用<code>done(null, null)</code> 。当您认为自己已经做对时,请提交您的页面。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -42,13 +27,13 @@ passport.deserializeUser((id, done) => {
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应存在正确的<code>serializeUser</code>方法。
|
||||
- text: 序列化用户功能正确
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /passport.serializeUser/gi, 'You should have created your passport.serializeUser function'); assert.match(data, /null, user._id/gi, 'There should be a callback in your serializeUser with (null, user._id)'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 应存在正确的<code>deserializeUser</code>方法。
|
||||
- text: 反序列化用户功能正确
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /passport.deserializeUser/gi, 'You should have created your passport.deserializeUser function'); assert.match(data, /null,( |)null/gi, 'There should be a callback in your deserializeUser with (null, null) for now'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: MongoDB 应作为项目的依赖。
|
||||
- text: MongoDB是一个依赖项
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/package.json') .then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, 'mongodb', 'Your project should list "mongodb" as a dependency'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 注释掉的代码中应包含<code>ObjectId</code>。
|
||||
- text: Mongodb正确要求包括ObjectId
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /require.*("|')mongodb("|')/gi, 'You should have required mongodb'); assert.match(data, /new ObjectID.*id/gi, 'Even though the block is commented out, you should use new ObjectID(id) for when we add the database'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -64,11 +49,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,68 +2,42 @@
|
||||
id: 5895f700f9fc0f352b528e63
|
||||
title: Set up a Template Engine
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301564
|
||||
videoUrl: ''
|
||||
localeTitle: 设置模板引擎
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a> 上克隆。
|
||||
你可以在应用的模版引擎中使用静态模板文件(如那些写在<em>Pug</em>里的)。在运行时,模版引擎会用服务端的真实数据替换掉模版文件中的变量,然后将模版转译成发送给客户端的 HTML 静态文件。这样可以轻松地构造 HTML 页面,允许在页面直接显示变量内容而不需要发送 API 请求。
|
||||
为了在项目中使用 <em>Pug</em>,你需要在 package.json 中添加依赖<code>"pug": "^0.1.0"</code>
|
||||
|
||||
为了在 Node/Express 中使用 pug 作为模版引擎,你需要在 express 中将 <b>app</b> 的 “view-engine” 设置为 “pug”,就像这样:<code>app.set('view engine', 'pug')</code>。
|
||||
|
||||
如果没有正确的 <em>render</em> <em>'views/pug'</em> 路径下的 index 文件,页面将不会被加载。
|
||||
|
||||
最后, 你需要使用<code>res.render</code>方法渲染 <em>views/pug/index.pug</em> 页面来作为路由请求的返回。
|
||||
|
||||
如果一切顺利,刷新一下应用的主页就可以看到 Pug 成功加载的提示,这时你就可以提交你的页面了。
|
||||
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-advancednode/">GitHub</a>克隆的。模板引擎使您可以在应用程序中使用静态模板文件(例如用<em>Pug</em>编写的文件)。在运行时,模板引擎将模板文件中的变量替换为可由服务器提供的实际值,并将模板转换为静态HTML文件,然后将其发送到客户端。这种方法可以更轻松地设计HTML页面,并允许在页面上显示变量,而无需从客户端进行API调用。要设置<em>Pug</em>以便在项目中使用,您需要先在package.json中将其作为依赖项添加。 <code>"pug": "^0.1.0"</code>现在告诉Node / Express使用模板引擎,你必须告诉你的快递<b>应用程序</b> <b>将</b> 'pug' <b>设置</b>为'view-engine'。 <code>app.set('view engine', 'pug')</code>最后,你应该改变请求您响应该指数路线<code>res.render</code>与路径视图<em>意见/哈巴狗/ index.pug。</em>如果一切按计划进行,您应该刷新应用程序主页并看到一条小消息,说您已成功从我们的Pug文件中删除Pug!当您认为自己已经做对时,请提交您的页面。 </section>
|
||||
|
||||
## Instructions
|
||||
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: "项目中应使用 'pug' 作为依赖。"
|
||||
- text: 帕格是一个依赖
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/package.json') .then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, 'pug', 'Your project should list "pug" as a dependency'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 项目中应使用 Pug 作为模版引擎。
|
||||
- text: 查看引擎是帕格
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /('|")view engine('|"),( |)('|")pug('|")/gi, 'Your project should set Pug as a view engine'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 在 Response 里使用正确的 ExpressJS 方法渲染主页面。
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /res(\s*)?\.(r\w{5})/gi, 'You successfully rendered the Pug template!'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: Pug 应该生效。
|
||||
- text: 帕格正在工作
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/') .then(data => { assert.match(data, /pug-success-message/gi, 'Your projects home page should now be rendered by pug with the projects .pug file unaltered'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,35 +2,19 @@
|
||||
id: 5895f70cf9fc0f352b528e65
|
||||
title: Set up Passport
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301565
|
||||
localeTitle: 设置 Passport
|
||||
videoUrl: ''
|
||||
localeTitle: 设置护照
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a> 上克隆。
|
||||
现在我们来创建 <em>Passport</em>,最终我们需要用它来实现用户注册和登录。除了 Passport,我们会用 express-session 来处理 session(会话)。在客户端,我们可以用这个中间件把会话 id 储存到 cookie,并可以通过服务器上的 id 访问会话数据。通过这种方式,我们可以无需把用户信息存到 cookie 来完成用户的验证。
|
||||
为了在你的项目中使用 Passport,首先你需要在 package.json 文件中添加依赖:<code>"passport": "^0.3.2"</code>
|
||||
此外,还需要添加 express-session 作为依赖,就像这样:<code>"express-session": "^1.15.0"</code>。express-session 有许多高级特性,但我们暂时只需要了解其基础特性。
|
||||
现在,我们需要配置 session 并初始化 Passport。请先创建变量<code>session</code>和<code>passport</code>引入 express-session 和 passport。
|
||||
为了让 express 应用可以使用 session,我们需要添加一些基础选项。请在 .env 文件中添加字段<code>SESSION_SECRET</code>,并给它赋一个随机值,便于加密 cookie、计算哈希。
|
||||
|
||||
```js
|
||||
app.use(session({
|
||||
secret: process.env.SESSION_SECRET,
|
||||
resave: true,
|
||||
saveUninitialized: true,
|
||||
}));
|
||||
```
|
||||
|
||||
还有,我们需要让 express 使用<code>passport.initialize()</code>和<code>passport.session()</code>。为此,你需要这样写:<code>app.use(passport.initialize());</code>。
|
||||
完成之后就可以提交你的页面了。如果运行出错,你可以在<a href='https://gist.github.com/JosephLivengood/338a9c5a326923c3826a666d430e65c3'>这里</a>检查项目的完成情况。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-advancednode/">GitHub</a>克隆的。是时候设置<em>Passport,</em>这样我们终于可以开始允许用户注册或登录帐户了!除了Passport,我们还将使用Express-session来处理会话。使用此中间件将会话ID保存为客户端中的cookie,并允许我们使用服务器上的该ID访问会话数据。这样,我们将个人帐户信息保留在客户端使用的cookie之外,以验证我们的服务器是否经过身份验证,并保留<em>密钥</em>以访问存储在服务器上的数据。要设置Passport以便在项目中使用,您需要先在package.json中将其作为依赖项添加。 <code>"passport": "^0.3.2"</code>此外,现在还要将Express-session添加为依赖项。 Express-session拥有大量可以使用的高级功能,但现在我们只是要使用基础知识! <code>"express-session": "^1.15.0"</code>您需要立即设置会话设置并初始化Passport。一定要先创建变量'session'和'passport',分别要求'express-session'和'passport'。要设置您要使用的快速应用程序使用会话,我们将仅定义几个基本选项。请务必将“SESSION_SECRET”添加到.env文件中,并为其提供随机值。这用于计算用于加密cookie的哈希值! <pre> app.use(会话({
|
||||
secret:process.env.SESSION_SECRET,
|
||||
resave:是的,
|
||||
saveUninitialized:true,
|
||||
})); </pre>您也可以继续告诉您的快递应用程序<b>使用</b> 'passport.initialize()'和'passport.session()'。 (例如, <code>app.use(passport.initialize());</code> )当您认为自己正确时,请提交您的页面。如果您遇到错误,可以<a href="https://gist.github.com/JosephLivengood/338a9c5a326923c3826a666d430e65c3">在这里查看</a>到目前为止完成的项目。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -38,13 +22,13 @@ app.use(session({
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应添加 Passort 和 express-session 作为依赖。
|
||||
- text: Passort和Express-session是依赖项
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/package.json') .then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, 'passport', 'Your project should list "passport" as a dependency'); assert.property(packJson.dependencies, 'express-session', 'Your project should list "express-session" as a dependency'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 依赖应正确引入。
|
||||
- text: 正确要求依赖性
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /require.*("|')passport("|')/gi, 'You should have required passport'); assert.match(data, /require.*("|')express-session("|')/gi, 'You should have required express-session'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: express 应调用 passport 的方法。
|
||||
- text: Express应用程序使用新的依赖项
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /passport.initialize/gi, 'Your express app should use "passport.initialize()"'); assert.match(data, /passport.session/gi, 'Your express app should use "passport.session()"'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 应正确设置 session 和 session secret。
|
||||
- text: 正确设置会话和会话密钥
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /secret:( |)process.env.SESSION_SECRET/gi, 'Your express app should have express-session set up with your secret as process.env.SESSION_SECRET'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -60,11 +44,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,40 +2,18 @@
|
||||
id: 589fc830f9fc0f352b528e74
|
||||
title: Set up the Environment
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301566
|
||||
videoUrl: ''
|
||||
localeTitle: 设置环境
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-socialauth/'>GitHub</a> 上克隆。
|
||||
现在,我们需要添加 Socket.IO 作为依赖,在你的代码中引入,给它传入参数 http 并实例化,将其命名为<code>io</code>,就像这样:<code>const io = require('socket.io')(http);</code>
|
||||
我们需要处理的第一件事是监听从客户端发出的连接事件,我们可以调用 <em>on</em> 方法来监听具体的事件。它接收两个参数:一个是发出的事件的标题字符串,另一个是后续用来传递数据的回调函数。在这个回调函数中,我们用 <em>socket</em> 来代表它所包含的数据。简单来说,socket 就是指已连接到服务器的客户端。
|
||||
在我们服务器代码中,只需要在注释间添加以下内容即可:
|
||||
|
||||
```js
|
||||
io.on('connection', socket => {
|
||||
console.log('A user has connected');
|
||||
});
|
||||
```
|
||||
|
||||
对于发出连接事件的客户端,只需要在 client.js 中添加以下内容:
|
||||
|
||||
```js
|
||||
/*global io*/
|
||||
var socket = io();
|
||||
```
|
||||
|
||||
注意,这个 client.js 文件是在用户通过验证后加载到客户端的。在这个文件中,我们没有定义 io 变量,但第一行的注释会阻止运行时产生的报错。然后,我们在 chat.pug 的页面上已经为你添加好了 Socket.IO 库的 CDN。
|
||||
现在你可以尝试启动你的 app 并登录,你会看到服务器的控制台中打印了 'A user has connected'
|
||||
<strong>注意:</strong><br>只有在连接到处于同一个 url/server 上的 socket 时,<code>io()</code>才可以正常执行。如果需要连接到外部的 socket,就需要这样调用:<code>io.connect('URL');</code>。
|
||||
完成上述要求后,你就可以在左边提交你的页面链接。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-socketio/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-socketio/">GitHub</a>克隆的。将Socket.IO添加为依赖项,并在服务器中要求/实例化它,定义为'io',并将http服务器作为参数。 <code>const io = require('socket.io')(http);</code>需要处理的第一件事是从客户端侦听新连接。 <dfn>on</dfn>关键字就是这样 - 监听特定事件。它需要2个参数:一个包含所发出事件标题的字符串,以及一个用于传递数据的函数。在我们的连接侦听器的情况下,我们使用<em>socket</em>来定义第二个参数中的数据。套接字是连接的个人客户端。要在我们的服务器上侦听连接,请在项目中的注释之间添加以下内容: <pre> io.on('connection',socket => {
|
||||
console.log('用户已连接');
|
||||
}); </pre>现在,对于客户端进行连接,您只需要将以下内容添加到client.js中,该客户端经过身份验证后由页面加载: <pre> / * global io * /
|
||||
var socket = io(); </pre>注释会抑制您通常会看到的错误,因为文件中未定义“io”。我们已经在chat.pug页面上的Socket.IO库中添加了一个可靠的CDN。现在尝试加载您的应用并进行身份验证,您应该在服务器控制台中看到“用户已连接”! <strong>注意</strong> <br> <code>io()</code>仅在连接到同一URL /服务器上托管的套接字时起作用。要连接到其他地方托管的外部套接字,您可以使用<code>io.connect('URL');</code> 。当您认为自己已经做对时,请提交您的页面。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -43,13 +21,13 @@ var socket = io();
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应添加 Socket.IO 作为依赖。
|
||||
- text: Socket.IO是一个依赖项
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/package.json') .then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, 'socket.io', 'Your project should list "socket.io" as a dependency'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: Socket.IO 应正确地引入和实例化。
|
||||
- text: Socket.IO已得到适当的要求和实例化
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js').then(data => {assert.match(data, /io.*=.*require.*('|")socket.io('|").*http/gi, 'You should correctly require and instantiate socket.io as io.');}, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: Socket.IO 应监听连接。
|
||||
- text: Socket.IO应该正在监听连接
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /io.on.*('|")connection('|").*socket/gi, 'io should listen for "connection" and socket should be the 2nd arguments variable'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 客户端应连接到服务器。
|
||||
- text: 您的客户端应该连接到您的服务器
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/public/client.js') .then(data => { assert.match(data, /socket.*=.*io/gi, 'Your client should be connection to server with the connection defined as socket'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -65,11 +43,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,26 +2,15 @@
|
||||
id: 5895f70bf9fc0f352b528e64
|
||||
title: Use a Template Engine's Powers
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301567
|
||||
localeTitle: 使用模板引擎
|
||||
videoUrl: ''
|
||||
localeTitle: 使用模板引擎的权力
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在<a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/'>这个 Glitch 项目</a>的基础上进行开发,你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-advancednode/'>GitHub</a> 上克隆。
|
||||
模版引擎最大的特点之一就是在 HTML 页面展示之前,可以从服务端传变量到模版文件。
|
||||
在 Pug 文件中,你可以用变量名来调用变量,比如写成<code>#{variable_name}</code>来实现行内调用,或像<code>p= variable_name</code>把元素与变量直接写在一起,这表示 p 元素的内容等价于这个变量。
|
||||
建议大家在 <a href='https://github.com/pugjs/pug'>Pug 的 README</a> 里看看它的语法和用法,这样你写出的代码会相对简练。另外要注意,Pug 使用缩进来表示嵌套的代码块。
|
||||
在 pug 的 'index.pug' 文件中,我们使用了 <em>title</em> 和 <em>message</em> 两个变量。
|
||||
为了从服务器传递这些信息,你需要给 <em>res.render</em> 的第二个参数传入一个对象,其中包含变量对应的值。比如,如果你想传递对象<code>{title: 'Hello', message: 'Please login'</code>到你的主页,那么应该这样写:
|
||||
<code>res.render(process.cwd() + '/views/pug/index', {title: 'Hello', message: 'Please login'});</code>
|
||||
刷新页面,如果页面中数据显示正确,你就可以提交你的页面了。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-advancednode/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-advancednode/">GitHub</a>克隆的。使用模板引擎的最大特点之一是能够将变量从服务器传递到模板文件,然后再将其呈现为HTML。在您的Pug文件中,您将通过引用变量名称作为<code>#{variable_name}</code>与元素上的其他文本内联,或者在元素上使用相等的一侧而不使用空格(例如<code>p= variable_name</code>来设置该<code>p= variable_name</code> 。 p元素文本等于变量。我们强烈建议在他们的Githubs自述文件中<a href="https://github.com/pugjs/pug">查看</a> Pug的语法和结构。 Pug就是使用空格和制表符来显示嵌套元素,并减少制作漂亮网站所需的代码量。查看项目中包含的我们的pug文件'index.pug',我们使用变量<em>title</em>和<em>message</em>要从我们的服务器单独传递它们,您需要将一个对象作为第二个参数添加到<em>res.render中</em> ,并带有变量和他们的价值。例如,传递此对象以设置索引视图的变量: <code>{title: 'Hello', message: 'Please login'</code>它应该看起来像: <code>res.render(process.cwd() + '/views/pug/index', {title: 'Hello', message: 'Please login'});</code>现在刷新页面,您应该在视图中呈现的那些值在index.pug文件中列出的正确位置!当您认为自己已经做对时,请提交您的页面。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -29,7 +18,7 @@ localeTitle: 使用模板引擎
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: Pug 应正确地展示变量。
|
||||
- text: 帕格渲染变量正确
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/') .then(data => { assert.match(data, /pug-variable("|')>Please login/gi, 'Your projects home page should now be rendered by pug with the projects .pug file unaltered'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -45,11 +34,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,21 +2,15 @@
|
||||
id: 587d8248367417b2b2512c3c
|
||||
title: Ask Browsers to Access Your Site via HTTPS Only with helmet.hsts()
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301573
|
||||
localeTitle: 使用 helmet.hsts() 要求浏览器只通过HTTPS访问你的网站
|
||||
videoUrl: ''
|
||||
localeTitle: 要求浏览器通过HTTPS访问您的站点仅限于使用helmet.hsts()
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
温馨提醒,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
HTTP严格安全传输 (HSTS) 是一个能帮助我们防护网站被协议降低攻击和 Cookie 挟持的 WEB 安全协议。如果你的网站支持 HTTPS 的话你可以让你的用户停止使用不安全的 HTTP。通过设置头部 ``Strict-Transport-Security``, 你告诉浏览器使用在未来的某段指定时间内使用 HTTPS 来请求网站内容。首次请求之后所以的后续请求都将使用 HTTPS
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。 HTTP严格传输安全(HSTS)是一种Web安全策略,可帮助保护网站免受协议降级攻击和cookie劫持。如果您的网站可以通过HTTPS访问,您可以要求用户的浏览器避免使用不安全的HTTP。通过设置标头Strict-Transport-Security,您可以告诉浏览器在指定的时间内对将来的请求使用HTTPS。这将适用于初始请求之后的请求。配置helmet.hsts()以在接下来的90天内使用HTTPS。传递配置对象{maxAge:timeInSeconds,force:true}。 Glitch已经启用了hsts。要覆盖其设置,您需要在配置对象中将字段“force”设置为true。在检查Glitch标头进行测试后,我们将拦截并恢复Glitch标头。注意:在自定义网站上配置HTTPS需要获取域和SSL / TSL证书。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
配置 helmet.hsts() 在未来的90天内使用 HTTPS。传递配置对象 {maxAge: timeInMilliseconds, force: true}. Glitch 默认已经开启 ``hsts``. 但你仍然可以通过 "force" 来覆盖它的配置。为了测试,我们会审查 Glitch 请求头部,然后拦截并恢复。
|
||||
注意: 配置 HTTPS 需要域名以及 SSL/TSL 证书。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -24,9 +18,9 @@ HTTP严格安全传输 (HSTS) 是一个能帮助我们防护网站被协议降
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: helmet.hsts() 中间件应该被正确加载
|
||||
- text: helmet.hsts()中间件应正确安装
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'hsts'); assert.property(data.headers, 'strict-transport-security'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: maxAge 应该等于 7776000 ms (90天)
|
||||
- text: maxAge应该等于7776000毫秒(90天)
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.match(data.headers['strict-transport-security'], /^max-age=7776000;?/); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -42,11 +36,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,20 +2,15 @@
|
||||
id: 587d8248367417b2b2512c3a
|
||||
title: Avoid Inferring the Response MIME Type with helmet.noSniff()
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301574
|
||||
localeTitle: 使用 helment.noSniff() 避免推断响应的 MIME 类型
|
||||
videoUrl: ''
|
||||
localeTitle: 避免使用helmet.noSniff()推断响应MIME类型
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
温馨提醒,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
浏览器可以通过探查 ``content`` 或者 ``MIME`` 头部来判断不同的响应内容。这两个的优先级比 ``Content-Type`` 还高,浏览器可以通过这两个头部来猜测并处理响应。这个在某些情况下非常实用,但也会造成一定的潜在风险。我们可以通过中间件来设置 ``X-Content-Type-Options`` 头部为 ``nosniff``。 这样,浏览器就不会绕过 ``Content-Type`` 这个头了。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。浏览器可以使用内容或MIME嗅探来适应来自响应的不同数据类型。它们覆盖Content-Type标头以猜测和处理数据。虽然这在某些情况下可能很方便,但它也可能导致一些危险的攻击。此中间件将X-Content-Type-Options标头设置为nosniff。这指示浏览器不绕过提供的Content-Type。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -23,7 +18,7 @@ localeTitle: 使用 helment.noSniff() 避免推断响应的 MIME 类型
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: helmet.noSniff() 中间件应该被正确加载
|
||||
- text: helmet.noSniff()中间件应该正确安装
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'nosniff'); assert.equal(data.headers['x-content-type-options'], 'nosniff'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -39,11 +34,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,38 +2,15 @@
|
||||
id: 587d8249367417b2b2512c40
|
||||
title: Configure Helmet Using the ‘parent’ helmet() Middleware
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301575
|
||||
localeTitle: 使用 helmet() 中间件来配置 Helmet
|
||||
videoUrl: ''
|
||||
localeTitle: 配置头盔使用“父”头盔()中间件
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
温馨提醒,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
app.use(helmet()) 会自动加载除 noCache() 和 contentSecurityPolicy() 外上面所有提到的中间件,但有需要的话这两个中间件也被启用。你也可以使用配置对象来一个个地禁用或者配置中间件。
|
||||
<h3>例子:</h3>
|
||||
|
||||
```js
|
||||
app.use(helmet({
|
||||
frameguard: { // configure
|
||||
action: 'deny'
|
||||
},
|
||||
contentSecurityPolicy: { // enable and configure
|
||||
directives: {
|
||||
defaultSrc: ["self"],
|
||||
styleSrc: ['style.com'],
|
||||
}
|
||||
},
|
||||
dnsPrefetchControl: false // disable
|
||||
}))
|
||||
```
|
||||
|
||||
为了教学和方便测试我们是一个个地讲解中间件,但通过使用 ‘parent’ helmet() 是最清晰和简洁的,特别是在真实的项目中。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。 app.use(helmet())将自动包含上面介绍的所有中间件,noCache()和contentSecurityPolicy()除外,但如果需要,可以启用这些中间件。您还可以使用配置对象单独禁用或配置任何其他中间件。 //示例<code>app.use(helmet({</code> <code>frameguard: { // configure</code> <code>action: 'deny'</code> <code>},</code> <code>contentSecurityPolicy: { // enable and configure</code> <code>directives: {</code> <code>defaultSrc: ["self"],</code> <code>styleSrc: ['style.com'],</code> <code>}</code> <code>},</code> <code>dnsPrefetchControl: false // disable</code> <code>}))</code>我们分别为教学目的引入了每个中间件,并且易于测试。使用“父”头盔()中间件对于真实项目来说是最简单,更清洁的。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -41,7 +18,7 @@ app.use(helmet({
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 没有测试 - 这是一个介绍关卡
|
||||
- text: 没有测试 - 这是一个描述性的挑战
|
||||
testString: assert(true)
|
||||
|
||||
```
|
||||
@@ -57,11 +34,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,20 +2,15 @@
|
||||
id: 587d8249367417b2b2512c3e
|
||||
title: Disable Client-Side Caching with helmet.noCache()
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301576
|
||||
localeTitle: 使用 helment.noCache() 禁用客户端缓存
|
||||
videoUrl: ''
|
||||
localeTitle: 使用helmet.noCache()禁用客户端缓存
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
温馨提醒,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
如果你为你的网站发布了一个更新,你想要用户永远使用最新的版本。你可以通过禁用浏览器的缓存来实现。这个功能在开发环境中也非常有用。当然你会失去缓存在性能上的优势,所以你应该在真正有必要的时候才用这个功能
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。如果您要为您的网站发布更新,并且您希望用户始终下载较新的版本,您可以(尝试)在客户端的浏览器上禁用缓存。它在开发中也很有用。缓存具有性能优势,您将失去这些优势,因此只有在真正需要时才使用此选项。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -23,7 +18,7 @@ localeTitle: 使用 helment.noCache() 禁用客户端缓存
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: helmet.noCache() 中间件应该被正确加载
|
||||
- text: 应该正确安装helmet.noCache()中间件
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'nocache'); assert.equal(data.headers['cache-control'], 'no-store, no-cache, must-revalidate, proxy-revalidate'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -39,11 +34,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,20 +2,15 @@
|
||||
id: 587d8248367417b2b2512c3d
|
||||
title: Disable DNS Prefetching with helmet.dnsPrefetchControl()
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301577
|
||||
localeTitle: 使用 helmet.dnsPrefetchControl() 禁用 DNS 预取
|
||||
videoUrl: ''
|
||||
localeTitle: 使用helmet.dnsPrefetchControl()禁用DNS预取
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
温馨提醒,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
为了提高性能,大部分浏览器都会为页面上的链接预先加载 DNS 记录。这样当用户点击一个链接的时候浏览器已经提前知道 IP 地址了。但这也会造成 DNS 服务的过度使用(如果你是一个大型网站,有这千万级的用户的情况下),隐私问题 (窃听者可以推测出你在哪个页面上),和页面数据准确性(有些没访问过的链接会被标记成已访问)。如果你对安全性要求比较高,你应该禁用 DNZ 预加载。当然,这样做性能上会有一点点损失.
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。为了提高性能,大多数浏览器都会为页面中的链接预取DNS记录。以这种方式,当用户点击链接时,目标IP已知。这可能导致过度使用DNS服务(如果您拥有一个大网站,数百万人访问......),隐私问题(一个窃听者可能会推断您在某个页面上),或者页面统计信息更改(某些链接可能会即使他们不是,也会出现。)如果您有高安全性需求,则可以以性能损失为代价禁用DNS预取。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -23,7 +18,7 @@ localeTitle: 使用 helmet.dnsPrefetchControl() 禁用 DNS 预取
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: helmet.dnsPrefetchControl() 中间件应该被正确加载
|
||||
- text: 应该正确安装helmet.dnsPrefetchControl()中间件
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'dnsPrefetchControl'); assert.equal(data.headers['x-dns-prefetch-control'], 'off'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -39,11 +34,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,48 +2,20 @@
|
||||
id: 58a25bcff9fc0f352b528e7d
|
||||
title: Hash and Compare Passwords Asynchronously
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
localeTitle: 异步哈希和比较密码
|
||||
videoUrl: ''
|
||||
localeTitle: 哈希和异步比较密码
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
温馨提醒,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-bcrypt/'> GitHub </a>上克隆。
|
||||
哈希会占用很大计算机资源并且会耗费比较多时间,比较推荐的做法是用异步的方法用调用哈希算法这样就不会阻挡其他的访问了。异步调用哈希方法非常简单,只需要 <code>bcrypt.hash(myPlaintextPassword, saltRounds, (err, hash) => { /*Store hash in your db*/ });</code>
|
||||
|
||||
```js
|
||||
bcrypt.hash(myPlaintextPassword, saltRounds, (err, hash) => {
|
||||
/*Store hash in your db*/
|
||||
});
|
||||
```
|
||||
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-bcrypt/">GitHub</a>克隆的。由于散列设计为计算密集型,因此建议在服务器上异步执行此操作,以避免在散列时阻止传入连接。所有你需要做的就是异步散列密码是调用<code>bcrypt.hash(myPlaintextPassword, saltRounds, (err, hash) => { /*Store hash in your db*/ });</code> <hr>将此散列函数添加到服务器(我们已经定义了函数中使用的变量供您使用)并将其记录到控制台以供您查看!此时,您通常会将哈希值保存到数据库中。现在,当您需要确定新输入是否与散列相同时,您只需使用比较函数<code>bcrypt.compare(myPlaintextPassword, hash, (err, res) => { /*res == true or false*/ });</code> 。在记录完成的哈希并将“res”记录到比较中的控制台之后,将其添加到现有哈希函数中(因为您需要在调用比较函数之前等待哈希完成)。您应该在控制台中看到一个哈希值,然后打印出“true”!如果您将compare函数中的'myPlaintextPassword'更改为'someOtherPlaintextPassword',那么它应该为false。 <pre> bcrypt.hash('passw0rd!',13,(错误,哈希)=> {
|
||||
的console.log(散列); //$2a$12$Y.PHPE15wR25qrrtgGkiYe2sXo98cjuMCG1YwSI5rJW1DSJp0gEYS
|
||||
bcrypt.compare('passw0rd!',hash,(err,res)=> {
|
||||
的console.log(RES); //真正
|
||||
});
|
||||
}); </pre>当您认为自己已经做对时,请提交您的页面。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
把这段哈希方法添加到你的服务器(我们已经定义好这个方法的变量给你直接使用了) 然后输入到控制台! 完成之后你通常需要把哈希过的值保存到数据库。
|
||||
当你需要对比用户输入的值是否和之前哈希过的值一样的时候,你只需要调用对比函数 <code>bcrypt.compare(myPlaintextPassword, hash, (err, res) => { /*res == true or false*/ });</code>. 把这个添加到你已有的哈希方法(你需要等哈希完成之后才能调用对比方法) 在你输入哈希过的值之后在对比方法里面输入 'res' 到控制台. 你会看到一个哈希值和 ``true`` 被打印出来。如果你在对比方法里面把 'myPlaintextPassword' 改变成 'someOtherPlaintextPassword' 那么应该会输出 ``false``
|
||||
|
||||
```js
|
||||
bcrypt.compare(myPlaintextPassword, hash, (err, res) => {
|
||||
/*res == true or false*/
|
||||
});
|
||||
```
|
||||
|
||||
当控制台打印哈希完成并且在对比的 console 里打印 ‘res’ 后,将其添加到现有的哈希函数中(因为要等待哈希完成才能调用 compare 函数)。 控制台中应该打印一个哈希,然后打印 “true”! 如果将比较函数中的 “myPlaintextPassword” 更改为 “someOtherPlaintextPassword”,则它应该显示为false。
|
||||
|
||||
```js
|
||||
bcrypt.hash('passw0rd!', 13, (err, hash) => {
|
||||
console.log(hash);
|
||||
//$2a$12$Y.PHPE15wR25qrrtgGkiYe2sXo98cjuMCG1YwSI5rJW1DSJp0gEYS
|
||||
bcrypt.compare('passw0rd!', hash, (err, res) => {
|
||||
console.log(res); //true
|
||||
});
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
提交页面当你觉得已经完成的时候。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -51,7 +23,7 @@ bcrypt.hash('passw0rd!', 13, (err, hash) => {
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 异步生成哈希并正确地对比
|
||||
- text: 异步哈希生成并正确比较
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /START_ASYNC[^]*bcrypt.hash.*myPlaintextPassword( |),( |)saltRounds( |),( |).*err( |),( |)hash[^]*END_ASYNC/gi, 'You should call bcrypt.hash on myPlaintextPassword and saltRounds and handle err and hash as a result in the callback'); assert.match(data, /START_ASYNC[^]*bcrypt.hash[^]*bcrypt.compare.*myPlaintextPassword( |),( |)hash( |),( |).*err( |),( |)res[^]*}[^]*}[^]*END_ASYNC/gi, 'Nested within the hash function should be the compare function comparing myPlaintextPassword to hash'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -67,11 +39,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,34 +2,15 @@
|
||||
id: 58a25bcff9fc0f352b528e7e
|
||||
title: Hash and Compare Passwords Synchronously
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301579
|
||||
localeTitle: 同步哈希和比较密码
|
||||
videoUrl: ''
|
||||
localeTitle: 哈希并同步比较密码
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
温馨提醒,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-bcrypt/'> GitHub </a>上克隆。
|
||||
同步哈希也是非常简单的,但是会造成延迟特别是在哈希计算量大并且次数多的情况下。同步进行哈希是通过调用
|
||||
|
||||
```js
|
||||
var hash = bcrypt.hashSync(myPlaintextPassword, saltRounds);
|
||||
```
|
||||
|
||||
<hr>把同步哈希添加到你的代码并输入到控制台,和之前一样,需要用到的变量已经提前定义好,你不需要做任何改动。你可能已经注意到,即使你是用异步的方式哈希同一个密码,结果也是不一样的。这是因为每次哈希的盐都是随机生成的,你可以通过第三个哈希字符串的头22的字符来验证。
|
||||
当你需要对比用户输入的值, 你只需要使用 compareSync 方法:
|
||||
|
||||
```js
|
||||
var result = bcrypt.compareSync(myPlaintextPassword, hash);
|
||||
```
|
||||
|
||||
返回的结果为 ``true`` 或者 ``false``。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-bcrypt/">GitHub</a>克隆的。同步散列也很容易,但如果使用服务器端成本高或经常进行散列会导致延迟。使用此方法进行散列与调用<code>var hash = bcrypt.hashSync(myPlaintextPassword, saltRounds);</code> <hr>将此散列方法添加到代码中,然后将结果记录到控制台。同样,使用的变量已在服务器中定义,因此您无需调整它们。您可能会注意到即使您使用与异步函数相同的密码进行哈希处理,控制台中的结果也不同 - 这是由于每次哈希值随机生成,如第三个哈希字符串中的前22个字符所示。现在将密码输入与新的同步散列进行比较,您将使用compareSync方法: <code>var result = bcrypt.compareSync(myPlaintextPassword, hash);</code>结果是布尔值true或false。添加此功能并登录控制台结果以查看其是否正常工作。当您认为自己已经做对时,请提交您的页面。如果您在这些挑战期间遇到错误,可以在<a href="https://gist.github.com/JosephLivengood/9a2698fb63e42d9d8b4b84235c08b4c4">此处</a>查看示例完成的代码。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
添加这个方法并输出把控制台来验证同步哈希已经成功了。
|
||||
提交页面当你觉得已经完成的时候。如果你遇到任何错误,你可以参考这个<a href='https://gist.github.com/JosephLivengood/9a2698fb63e42d9d8b4b84235c08b4c4'>链接</a>中已经完成的代码。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -37,7 +18,7 @@ var result = bcrypt.compareSync(myPlaintextPassword, hash);
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 同步生成哈希并正确地对比
|
||||
- text: 同步哈希生成并正确比较
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /START_SYNC[^]*hash.*=.*bcrypt.hashSync.*myPlaintextPassword( |),( |)saltRounds[^]*END_SYNC/gi, 'You should call bcrypt.hashSync on myPlaintextPassword with saltRounds'); assert.match(data, /START_SYNC[^]*result.*=.*bcrypt.compareSync.*myPlaintextPassword( |),( |)hash[^]*END_SYNC/gi, 'You should call bcrypt.compareSync on myPlaintextPassword with the hash generated in the last line'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -53,11 +34,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,20 +2,15 @@
|
||||
id: 587d8247367417b2b2512c37
|
||||
title: Hide Potentially Dangerous Information Using helmet.hidePoweredBy()
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301580
|
||||
localeTitle: 使用 helmet.hidePoweredBy() 隐藏潜在的危险信息
|
||||
videoUrl: ''
|
||||
localeTitle: 使用helmet.hidePoweredBy()隐藏潜在的危险信息
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
温馨提醒,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
如果黑客发现你的网站是用 Express 搭建的,那么他们就可以利用 Express 或 Node 现存的漏洞来攻击你的网站。<code>X-Powered-By: Express</code> 默认情况下会被添加到所有响应的头部。不过 helmet.hidePoweredBy() 中间件可以帮你移除 X-Powered-By 头。你甚至可以把头设置成其它的值。 如 <code>app.use(helmet.hidePoweredBy({ setTo: 'PHP 4.2.0' }))</code>
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。如果黑客看到您的网站由Express提供支持,他们可以利用Express / Node中的已知漏洞。 X-Powered-By:Express默认发送来自Express的每个请求。 helmet.hidePoweredBy()中间件将删除X-Powered-By标头。您还可以将标头显式设置为其他内容,以便让人们离开。例如app.use(helmet.hidePoweredBy({setTo:'PHP 4.2.0'})) </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -23,7 +18,7 @@ localeTitle: 使用 helmet.hidePoweredBy() 隐藏潜在的危险信息
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: helmet.hidePoweredBy() 中间件应该被正确加载
|
||||
- text: helmet.hidePoweredBy()中间件应正确安装
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'hidePoweredBy'); assert.notEqual(data.headers['x-powered-by'], 'Express')}, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -39,11 +34,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,20 +2,15 @@
|
||||
id: 587d8247367417b2b2512c36
|
||||
title: Install and Require Helmet
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301581
|
||||
localeTitle: 安装和引入 Helmet
|
||||
videoUrl: ''
|
||||
localeTitle: 安装并需要头盔
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
Helmet 通过配置不同的 HTTP 头部信息来使你的 Express 应用更加安全。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。 Helmet通过设置各种HTTP标头来帮助您保护Express应用程序。安装包,然后需要它。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
安装,并引入 Helmet 这个包。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -23,7 +18,7 @@ Helmet 通过配置不同的 HTTP 头部信息来使你的 Express 应用更加
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: "package.json 文件应该有 'helmet' 这个依赖包"
|
||||
- text: “helmet”依赖应该在package.json中
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/package.json').then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, 'helmet'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -39,11 +34,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,21 +2,15 @@
|
||||
id: 587d8247367417b2b2512c38
|
||||
title: Mitigate the Risk of Clickjacking with helmet.frameguard()
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301582
|
||||
localeTitle: 使用 helmet.frameguard() 降低点击劫持的风险
|
||||
videoUrl: ''
|
||||
localeTitle: 使用helmet.frameguard()降低点击劫持的风险
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
温馨提醒,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
黑客可能会不经过你的允许,把你的页面嵌套在 <code><frame></code> 或者 <code><iframe></code> 标签里,用以实现“点击劫持”。点击劫持是一种视觉上的欺骗手段,让用户误以为自己在与所看到的网页交互。通过 iframe,黑客可以在你的页面上添加一个透明的“层”,然后把自己的恶意代码放在一个用户看不到的按钮中。这样一来,你网站的执行环境就被黑客设置成了他想要的效果。helmet 中间件可以设置 X-Frame-Options 这个头部。这样就能限制谁可以通过 iframe 引入你的页面了。 有三个模式可供配置: DENY, SAMEORIGIN, 和 ALLOW-FROM.
|
||||
我们的应用不需要被 iframe 引用。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。未经您的同意,您的页面可以放在<code><frame></code>或<code><iframe></code> 。除其他外,这可能导致点击劫持攻击。点击劫持是一种欺骗用户与不同于用户认为的页面进行交互的技术。这可以通过iframing在恶意上下文中执行您的页面获得。在这种情况下,黑客可以在页面上放置隐藏层。隐藏按钮可用于运行错误的脚本。此中间件设置X-Frame-Options标头。它限制了谁可以将您的网站放在框架中。它有三种模式:DENY,SAMEORIGIN和ALLOW-FROM。我们不需要我们的应用程序框架。您应该使用配置对象<code>{action: 'deny'}</code>传递的<code>helmet.frameguard()</code> 。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
你可以调用 <code>helmet.frameguard()</code> 这个方法,然后传入配置对象 <code>{action: 'deny'}</code> 就可以了。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -24,9 +18,9 @@ localeTitle: 使用 helmet.frameguard() 降低点击劫持的风险
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: helmet.frameguard() 中间件应该被正确加载
|
||||
- text: helmet.frameguard()中间件应正确安装
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'frameguard', 'helmet.frameguard() middleware is not mounted correctly'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '<code>helmet.frameguard()</code> 中的 <code>action</code> 属性的值应该为 "DENY"'
|
||||
- text: helmet.frameguard()'action'应该设置为'DENY'
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.property(data.headers, 'x-frame-options'); assert.equal(data.headers['x-frame-options'], 'DENY');}, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -42,11 +36,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,24 +2,15 @@
|
||||
id: 587d8247367417b2b2512c39
|
||||
title: Mitigate the Risk of Cross Site Scripting (XSS) Attacks with helmet.xssFilter()
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301583
|
||||
localeTitle: 使用 helmet.xssFilter() 降低跨站点脚本(XSS)攻击的风险
|
||||
videoUrl: ''
|
||||
localeTitle: 使用helmet.xssFilter()降低跨站点脚本(XSS)攻击的风险
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
温馨提醒,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
跨站脚本攻击 (XSS) 是一种比较常见的攻击手段,通过给页面注入恶意脚本来获取用户的 session, 密码等信息。
|
||||
防止这类型的攻击也非常简单:“永远不要相信用户的输入”。 作为一名开发人员,你一定要对所有外部的输入进行审查,其中包括来自表单,GET 请求,POST 请求主体的内容。审核就是说你需要查找并给有潜在风险的字符进行编码,例如:<, >.
|
||||
现代浏览器已经有非常好的软件策略来应对着类型问题。通常是通过配置 http 头部来实现.
|
||||
X-XSS-Protection HTTP 头部是一个基本的防护。浏览器通过启发式过滤法则来检测有潜在风险的注入脚本,这是有如果这个头部被启用了,浏览器就能改成脚本里面的代码从而使恶意代码不再生效。
|
||||
但是浏览器对这个功能的支持有限。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。跨站点脚本(XSS)是一种常见的攻击类型,其中恶意脚本被注入易受攻击的页面,目的是窃取会话cookie或密码等敏感数据。降低XSS攻击风险的基本规则很简单:“绝不信任用户的输入”。作为开发人员,您应该始终清理来自外部的所有输入。这包括来自表单,GET查询URL,甚至来自POST主体的数据。消毒意味着您应该找到并编码可能有危险的字符,例如<,>。现代浏览器可以通过采用更好的软件策略来帮助降低风险。通常这些都可以通过http标头进行配置。 X-XSS-Protection HTTP标头是一种基本保护。浏览器使用启发式过滤器检测潜在的注入脚本。如果启用了标头,则浏览器会更改脚本代码,从而中和它。它仍然有限的支持。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -27,7 +18,7 @@ X-XSS-Protection HTTP 头部是一个基本的防护。浏览器通过启发式
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: helmet.xssFilter() 应该被正确地加载
|
||||
- text: helmet.xssFilter()中间件应该正确安装
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'xXssProtection'); assert.property(data.headers, 'x-xss-protection'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -43,11 +34,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,20 +2,15 @@
|
||||
id: 587d8248367417b2b2512c3b
|
||||
title: Prevent IE from Opening Untrusted HTML with helmet.ieNoOpen()
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301584
|
||||
localeTitle: 使用 helment.ieNoOpen() 防止 IE 打开不受信任的 HTML
|
||||
videoUrl: ''
|
||||
localeTitle: 使用helmet.ieNoOpen()阻止IE打开不受信任的HTML
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
温馨提醒,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
有些网站会下载不安全的 HTML 文件,某些版本的 IE 默认情况下还会在你网站的作用域下打开这些 HTML 文件。换句话说,这些不安全的 HTML 页面可以在你的网站做恶意行为。我们可以通过中间件来设置 ``X-Download-Options`` 头部为 ``noopen``。这样就可以防治 IE 在不信任的网站下执行下载的文件。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。某些Web应用程序将提供不受信任的HTML以供下载。默认情况下,某些版本的Internet Explorer会在您的站点上下文中打开这些HTML文件。这意味着不受信任的HTML页面可能会在您的页面上下文中开始做坏事。此中间件将X-Download-Options标头设置为noopen。这将阻止IE用户在受信任站点的上下文中执行下载。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -23,7 +18,7 @@ localeTitle: 使用 helment.ieNoOpen() 防止 IE 打开不受信任的 HTML
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: helmet.ieNoOpen() 中间件应该被正确加载
|
||||
- text: helmet.ieNoOpen()中间件应正确安装
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'ienoopen'); assert.equal(data.headers['x-download-options'], 'noopen'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -39,11 +34,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,22 +2,17 @@
|
||||
id: 587d8249367417b2b2512c3f
|
||||
title: Set a Content Security Policy with helmet.contentSecurityPolicy()
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301585
|
||||
localeTitle: 使用 helment.contentSecurityPolicy() 设置内容安全策略
|
||||
videoUrl: ''
|
||||
localeTitle: 使用helmet.contentSecurityPolicy()设置内容安全策略
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
温馨提醒,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
这个挑战重点讨论一种现代浏览器中有效并且能大幅度减轻安全风险和很多种类型常见攻击的安全防护。通过配置内容安全策略你可以防止很多类型的脚本恶意注入。这会让你的应用远离 XSS 漏洞、恶意追踪、恶意 frames 和很多很多其他攻击。CSP 通过配置资源白名单来避免这些问题。 你可以给任何一种类型的页面资源 (脚本、样式文件、字体、frames、媒体文件等等等)做这个配置,它支持很多指令,所以网站管理员可以做细致的控制。更多详情请参考 HTML 5 Rocks,KeyCDN。不幸的是。一些旧的浏览器不支持 CSP。
|
||||
默认的指令很容易受到攻击, 所以设置 defaultSrc 指令作为降级方案很重要。Helmet 同时支持 defaultSrc 和 default-src 命名规范。降级方案可以被应用在大部分指令上。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。这一挑战突出了一个有希望的新防御,它可以显着降低现代浏览器中许多类型攻击的风险和影响。通过设置和配置内容安全策略,您可以防止在页面中无意中注入任何内容。这样可以保护您的应用免受XSS漏洞,不受欢迎的跟踪,恶意帧等攻击。 CSP通过定义受信任的内容源的白名单来工作。您可以为网页可能需要的每种资源(脚本,样式表,字体,框架,媒体等等)配置它们。有多个指令可用,因此网站所有者可以拥有精细控制。有关详细信息,请参阅HTML 5 Rocks,KeyCDN。不幸的是旧版浏览器不支持CSP。默认情况下,指令是全开的,因此将defaultSrc指令设置为回退非常重要。 Helmet支持defaultSrc和default-src命名样式。回退适用于大多数未指定的指令。在本练习中,使用helmet.contentSecurityPolicy(),并将其配置为将defaultSrc指令设置为["self"](允许的源列表必须在数组中),以便默认只信任您的网站地址。同时设置scriptSrc指令,以便允许从您的网站和域"trusted-cdn.com"下载脚本。提示:在"'self'"关键字中,单引号是关键字本身的一部分,因此需要用双引号括起来才能生效。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
在这个练习中,我们使用 helmet.contentSecurityPolicy(),并配置 defaultSrc 指令为 ["self"] (允许的资源列表必须在一个数组当中), 这样做表示只信任自己的网站域名。另外配置 scriptSrc 指令可以限制脚本只能本网站域名上或者信任的域名‘trusted-cdn.com’上下载
|
||||
提示: 在 "'self'" 关键词, 单引号也是关键词的一部分, 所以你应该用双引号来包起它才能正常工作。
|
||||
<section id="instructions">
|
||||
在本练习中,使用<code>helmet.contentSecurityPolicy()</code>,并将其配置为将<code>defaultSrc指令</code>设置为<code>["self"]</code>(允许的列表) 来源必须位于一个数组中,以便默认情况下仅信任您的网站地址。 还设置<code>scriptSrc</code>指令,以便您允许从您的网站以及"trusted-cdn.com"域下载脚本。
|
||||
提示:在<code>self</code>关键字中,单引号是关键字本身的一部分,因此需要使用双引号将其括起来才能起作用。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -25,9 +20,9 @@ localeTitle: 使用 helment.contentSecurityPolicy() 设置内容安全策略
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: helmet.csp() 中间件应该被正确加载
|
||||
- text: helmet.csp()中间件应该正确安装。
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'csp'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '你的 csp 配置不正确. defaultSrc 应该是 [""self""] 并且scriptSrc 应该是 [""self"", "trusted-cdn.com"]'
|
||||
- text: 你的csp配置不正确。 defaultSrc应该是["self"]而scriptSrc应该是["self","trusted-cdn.com"]
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { var cspHeader = Object.keys(data.headers).filter(function(k){ return k === 'content-security-policy' || k === 'x-webkit-csp' || k === 'x-content-security-policy' })[0]; assert.equal(data.headers[cspHeader], "default-src 'self'; script-src 'self' trusted-cdn.com"); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -43,11 +38,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,22 +2,15 @@
|
||||
id: 58a25bcef9fc0f352b528e7c
|
||||
title: Understand BCrypt Hashes
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301586
|
||||
localeTitle: 了解加密哈希
|
||||
videoUrl: ''
|
||||
localeTitle: 了解BCrypt Hashes
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
温馨提醒,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-bcrypt/'> GitHub </a>上克隆。
|
||||
BCrypt 哈希非常安全. 哈希其实就是原数据的加密形式,非常安全,这通过把原始数据放进哈希算法然后返回固定长度的字符串实现。为了让这个过程更复杂跟安全,你还可以加你的哈希添加 <em>盐</em>。加盐其实就是给在哈希算法工作前给源数据添加随机的字符串数据,这会让破解哈希更加复杂
|
||||
BCrypt 看起来像这样子 <code>$2a$13$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm</code>,它遵循固定的结构. 前面一小节 <code>$2a</code> 说明了该哈希采用什么算法。下一部分 <code>$13</code> 定义了 <em>代价</em>. 代价就是用多少计算资源来生成哈希。 它的对数刻度是 2^cost 它决定了数据被放入哈希算法多少次。举个例子,如果代价为10也就是说你能够在一个普通的计算机上每秒钟哈希10个密码。然而,如果代价为15,那每个哈希都要3秒钟,再举例子,如果代价是31at a cost of 31,那每次哈希能耗费好几天。通常代价为12就已经足够安全。哈希的最后一部分 <code>$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm</code>, 看起来像随机字符、句号、字母组成的随机字符串。但实际上它有两部分内容。前面22个字符就是纯文本的盐,剩下的就是加密过的密码!
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-bcrypt/">GitHub</a>克隆的。 BCrypt哈希非常安全。哈希基本上是原始数据的指纹 - 始终是唯一的。这是通过将原始数据馈送到算法中并返回固定长度的结果来实现的。为了进一步使这个过程复杂化并使其更安全,您还可以<em>加入哈希</em>值。对哈希进行盐析涉及在哈希处理之前将随机数据添加到原始数据,这使得更难破解哈希。 BCrypt哈希总是看起来像<code>$2a$13$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm</code>确实有一个结构。第一小部分数据<code>$2a</code>定义了使用何种哈希算法。下一部分<code>$13</code>定义了<em>成本</em> 。成本是计算哈希值所需的功率。它具有2 ^成本的对数标度,并确定通过散列算法放置数据的次数。例如,以10为代价,您可以在普通计算机上每秒散列10个密码,但是每个散列需要花费3秒才能进行散乱...并且需要花费更多时间,成本为31需要多天才能完成哈希。目前,12的成本被认为是非常安全的。哈希<code>$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm</code>的最后一部分看起来像<code>$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm</code>数字,句号和字母,但它实际上是两条独立的信息。前22个字符是纯文本的盐,其余的是哈希密码! <hr>要开始使用BCrypt,请将其作为项目中的依赖项添加,并在服务器中将其命名为“bcrypt”。当您认为自己已经做对时,请提交您的页面。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
要开始使用 BCrypt, 只需添加到你的依赖列表,然后在你的服务器引入 'bcrypt'
|
||||
在你觉得已经完成的时候提交页面。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -25,9 +18,9 @@ BCrypt 看起来像这样子 <code>$2a$13$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUU
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: BCyrpt 已被添加到依赖列表
|
||||
- text: BCrypt是一个依赖
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/package.json') .then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, 'bcrypt', 'Your project should list "bcrypt" as a dependency'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: BCrypt 已经被正确引入
|
||||
- text: 已经适当地要求BCrypt
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js').then(data => {assert.match(data, /bcrypt.*=.*require.*('|")bcrypt('|")/gi, 'You should correctly require and instantiate socket.io as io.');}, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
@@ -43,11 +36,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,23 +2,15 @@
|
||||
id: 587d824c367417b2b2512c4c
|
||||
title: Assert Deep Equality with .deepEqual and .notDeepEqual
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301587
|
||||
localeTitle: 用 Assert.deepEqual() 和 Assert.notDeepEqual() 断言深度相等
|
||||
videoUrl: ''
|
||||
localeTitle: 使用.deepEqual和.notDeepEqual断言深度等式
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
.deepEqual(),.notDeepEqual()
|
||||
<code>deepEqual()</code> 断言两个对象是否 deep equal
|
||||
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。 .deepEqual(),. noDeepEqual()。deepEqual()断言两个对象是深度相等的</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
用 <code>assert.deepEqual()</code> 或者 <code>assert.notDeepEqual()</code> 通过测试
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -26,11 +18,11 @@ localeTitle: 用 Assert.deepEqual() 和 Assert.notDeepEqual() 断言深度相等
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=6').then(data => {assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--deepEqual 或 notDeepEqual
|
||||
- text: 选择正确的断言 - deepEqual vs. notDeepEqual
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=6').then(data => { assert.equal(data.assertions[0].method, 'deepEqual', 'The order of the keys does not matter'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--deepEqual 或 notDeepEqual
|
||||
- text: 选择正确的断言 - deepEqual vs. notDeepEqual
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=6').then(data => { assert.equal(data.assertions[1].method, 'notDeepEqual', 'The position of elements within an array does matter'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -46,11 +38,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,20 +2,15 @@
|
||||
id: 587d824c367417b2b2512c4d
|
||||
title: Compare the Properties of Two Elements
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301588
|
||||
videoUrl: ''
|
||||
localeTitle: 比较两个元素的属性
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。 .isAbove()=> a> b,.isAtMost()=> a <= b </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
使用 <code>assert.isAbove()</code> (如 greater) 或者 <code>assert.isAtMost()</code> (如 less than 或者 equal) 通过测试。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -23,15 +18,15 @@ localeTitle: 比较两个元素的属性
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=7').then(data => {assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isAbove 或 isAtMost
|
||||
- text: 选择正确的断言 - isAbove vs. isAtMost
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=7').then(data => { assert.equal(data.assertions[0].method, 'isAtMost', '5 is at most (<=) 5'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isAbove 或 isAtMost
|
||||
- text: 选择正确的断言 - isAbove vs. isAtMost
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=7').then(data => { assert.equal(data.assertions[1].method, 'isAbove', '1 is greater than 0'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isAbove 或 isAtMost
|
||||
- text: 选择正确的断言 - isAbove vs. isAtMost
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=7').then(data => { assert.equal(data.assertions[2].method, 'isAbove', 'Math.PI = 3.14159265 is greater than 3'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isAbove 或 isAtMost
|
||||
- text: 选择正确的断言 - isAbove vs. isAtMost
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=7').then(data => { assert.equal(data.assertions[3].method, 'isAtMost', '1 - Math.random() is > 0 and <= 1. It is atMost 1 !'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -47,11 +42,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,20 +2,15 @@
|
||||
id: 587d824a367417b2b2512c46
|
||||
title: Learn How JavaScript Assertions Work
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301589
|
||||
localeTitle: 了解 JavaScript 断言是如何工作的
|
||||
videoUrl: ''
|
||||
localeTitle: 了解JavaScript断言的工作原理
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。使用assert.isNull()或assert.isNotNull()来使测试通过。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 assert.isNull() 或 assert.isNotNull() 通过测试。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -23,11 +18,11 @@ localeTitle: 了解 JavaScript 断言是如何工作的
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=0').then(data => {assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isNull 或 isNotNull
|
||||
- text: 选择正确的断言 - isNull vs. isNotNull
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=0').then(data => { assert.equal(data.assertions[0].method, 'isNull', 'Null is null'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isNull 或 isNotNull
|
||||
- text: 选择正确的断言 - isNull vs. isNotNull
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=0').then(data => { assert.equal(data.assertions[1].method, 'isNotNull', '1 is not null'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -43,11 +38,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,49 +2,15 @@
|
||||
id: 587d824f367417b2b2512c5a
|
||||
title: Run Functional Tests on an API Response using Chai-HTTP III - PUT method
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301590
|
||||
localeTitle: 使用 Chai-HTTP III 的 PUT 方法运行功能测试
|
||||
videoUrl: ''
|
||||
localeTitle: 使用Chai-HTTP III-PUT方法在API响应上运行功能测试
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
下一个例子我们将了解如何使用 request payload(body)发送数据。
|
||||
我们准备测试发送到 '/travellers' 端点的 PUT 请求
|
||||
JSON 对象结构如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"surname": [last name of a traveller of the past]
|
||||
}
|
||||
```
|
||||
|
||||
route 相应如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": [first name], "surname": [last name], "dates": [birth - death years]
|
||||
}
|
||||
```
|
||||
|
||||
更多细节请查看服务器代码。
|
||||
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。在下一个示例中,我们将看到如何在请求有效负载(正文)中发送数据。我们将测试PUT请求。 '/ travelers'端点接受一个采用结构的JSON对象:{surname:[过去的旅行者的姓氏]},路由响应:{name:[first name],surname:[last name],dates :[birth - death years]}查看服务器代码以获取更多详细信息。发送{姓:'科伦坡'}。替换assert.fail()并使测试通过。检查1)状态,2)类型,3)body.name,4)body.surname遵循上面的断言顺序,我们依赖它。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
发送 <br>
|
||||
|
||||
```json
|
||||
{
|
||||
"surname": "Colombo"
|
||||
}
|
||||
```
|
||||
|
||||
替换 assert.fail(),使测试通过。
|
||||
测试 1) status, 2) type, 3) body.name, 4) body.surname
|
||||
请按照以上顺序书写断言,顺序错误会影响系统判定。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -52,15 +18,15 @@ route 相应如下:
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=2').then(data => { assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '你需要测试 "res.status" 是否为 200'
|
||||
- text: 您应该测试'res.status'为200
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=2').then(data => { assert.equal(data.assertions[0].method, 'equal'); assert.equal(data.assertions[0].args[0], 'res.status'); assert.equal(data.assertions[0].args[1], '200');}, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '你需要测试 "res.type" 是否为 "application/json"'
|
||||
- text: 你应该测试'res.type'是'application / json'
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=2').then(data => { assert.equal(data.assertions[1].method, 'equal'); assert.equal(data.assertions[1].args[0], 'res.type'); assert.equal(data.assertions[1].args[1], '\'application/json\'');}, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '你需要测试 "res.body.name" 是否为 "Cristoforo"'
|
||||
- text: 您应该测试'res.body.name'为'Cristoforo'
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=2').then(data => { assert.equal(data.assertions[2].method, 'equal'); assert.equal(data.assertions[2].args[0], 'res.body.name'); assert.equal(data.assertions[2].args[1], '\'Cristoforo\'');}, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '你需要测试 "res.body.surname" 是否为 "Colombo"'
|
||||
- text: 你应该测试'res.body.surname'是'科伦坡'
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=2').then(data => { assert.equal(data.assertions[3].method, 'equal'); assert.equal(data.assertions[3].args[0], 'res.body.surname'); assert.equal(data.assertions[3].args[1], '\'Colombo\'');}, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -76,11 +42,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,31 +2,15 @@
|
||||
id: 587d824f367417b2b2512c5b
|
||||
title: Run Functional Tests on an API Response using Chai-HTTP IV - PUT method
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301591
|
||||
localeTitle: 使用 Chai-HTTP IV 的 PUT 方法运行功能测试
|
||||
videoUrl: ''
|
||||
localeTitle: 使用Chai-HTTP IV - PUT方法在API响应上运行功能测试
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
这个练习与上一个类似,我们详细看看。
|
||||
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。此练习与前面的练习类似。看看它的细节。发送{姓:'da Verrazzano'}。替换assert.fail()并使测试通过。检查1)状态,2)类型,3)body.name,4)body.surname遵循上面的断言顺序,我们依赖它。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
发送
|
||||
|
||||
```json
|
||||
{
|
||||
"surname": "da Verrazzano"
|
||||
}
|
||||
```
|
||||
|
||||
替换 assert.fail(),使测试通过。
|
||||
测试 for 1) status, 2) type, 3) body.name, 4) body.surname
|
||||
请按照以上顺序书写断言,顺序错误会影响系统判定。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -34,15 +18,15 @@ localeTitle: 使用 Chai-HTTP IV 的 PUT 方法运行功能测试
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=3').then(data => { assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '你需要测试 "res.status" 是否为 200'
|
||||
- text: 您应该测试'res.status'为200
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=3').then(data => { assert.equal(data.assertions[0].method, 'equal'); assert.equal(data.assertions[0].args[0], 'res.status'); assert.equal(data.assertions[0].args[1], '200');}, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '你需要测试 "res.type" 是否为 "application/json"'
|
||||
- text: 你应该测试'res.type'是'application / json'
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=3').then(data => { assert.equal(data.assertions[1].method, 'equal'); assert.equal(data.assertions[1].args[0], 'res.type'); assert.equal(data.assertions[1].args[1], '\'application/json\'');}, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '你需要测试 "res.body.name" 是否为 "Giovanni"'
|
||||
- text: 您应该测试'res.body.name'为'Giovanni'
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=3').then(data => { assert.equal(data.assertions[2].method, 'equal'); assert.equal(data.assertions[2].args[0], 'res.body.name'); assert.equal(data.assertions[2].args[1], '\'Giovanni\'');}, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '你需要测试 "res.body.surname" 是否为 "da Verrazzano"'
|
||||
- text: 你应该测试'res.body.surname'是'da Verrazzano'
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=3').then(data => { assert.equal(data.assertions[3].method, 'equal'); assert.equal(data.assertions[3].args[0], 'res.body.surname'); assert.equal(data.assertions[3].args[1], '\'da Verrazzano\'');}, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -58,11 +42,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,21 +2,15 @@
|
||||
id: 587d824f367417b2b2512c59
|
||||
title: Run Functional Tests on API Endpoints using Chai-HTTP II
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301592
|
||||
localeTitle: 使用 Chai-HTTP II 在 API 端上运行功能测试
|
||||
videoUrl: ''
|
||||
localeTitle: 使用Chai-HTTP II在API端点上运行功能测试
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。替换assert.fail()。测试状态和text.response。让测试通过。在追加的查询中发送您的姓名吗?name = <your_name> ,端点以'你好'回应<your_name> ”。 </your_name></your_name></section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
替换 assert.fail()。测试 status 和 text.response,使测试通过。
|
||||
在 query 中传入你的名字 ?name=<your_name>,端点会返回 'hello <your_name>'。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -24,11 +18,11 @@ localeTitle: 使用 Chai-HTTP II 在 API 端上运行功能测试
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=1').then(data => { assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '你需要测试 "res.status" == 200'
|
||||
- text: 你应该测试'res.status'== 200
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=1').then(data => { assert.equal(data.assertions[0].method, 'equal'); assert.equal(data.assertions[0].args[0], 'res.status'); assert.equal(data.assertions[0].args[1], '200');}, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '你需要测试 "res.text" == "hello Guest"'
|
||||
- text: 你应该测试'res.text'=='你好客人'
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=1').then(data => { assert.equal(data.assertions[1].method, 'equal'); assert.equal(data.assertions[1].args[0], 'res.text'); assert.match(data.assertions[1].args[1], /hello [\w\d_-]/);}, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -44,11 +38,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,21 +2,15 @@
|
||||
id: 587d824e367417b2b2512c58
|
||||
title: Run Functional Tests on API Endpoints using Chai-HTTP
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301593
|
||||
localeTitle: 使用 Chai-HTTP 在 API 端上运行功能测试
|
||||
videoUrl: ''
|
||||
localeTitle: 使用Chai-HTTP在API端点上运行功能测试
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。替换assert.fail()。测试状态和text.response。让测试通过。不要在查询中发送名称,端点会以“hello Guest”响应。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
替换 assert.fail()。测试 status 和 text.response,使测试通过。
|
||||
不要在 query 中传入 name,端点将会返回 'hello Guest'.
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -24,11 +18,11 @@ localeTitle: 使用 Chai-HTTP 在 API 端上运行功能测试
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=0').then(data => { assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '你需要测试 "res.status" == 200'
|
||||
- text: 你应该测试'res.status'== 200
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=0').then(data => { assert.equal(data.assertions[0].method, 'equal'); assert.equal(data.assertions[0].args[0], 'res.status'); assert.equal(data.assertions[0].args[1], '200');}, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '你需要测试 "res.text" == "hello Guest"'
|
||||
- text: 你应该测试'res.text'=='你好客人'
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=0').then(data => { assert.equal(data.assertions[1].method, 'equal'); assert.equal(data.assertions[1].args[0], 'res.text'); assert.equal(data.assertions[1].args[1], '\'hello Guest\'');}, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -44,11 +38,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,21 +2,15 @@
|
||||
id: 587d8250367417b2b2512c5d
|
||||
title: Run Functional Tests using a Headless Browser II
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301594
|
||||
localeTitle: 使用 无头浏览器 II 运行功能测试
|
||||
videoUrl: ''
|
||||
localeTitle: 使用无头浏览器II运行功能测试
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。此练习与前面的练习类似。查看方向代码。按照断言顺序,我们依靠它。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
这个练习与上一个类似。
|
||||
看看此挑战的给出的代码指引。请按顺序书写断言,顺序错误会影响系统判定。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -24,15 +18,15 @@ localeTitle: 使用 无头浏览器 II 运行功能测试
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=5').then(data => { assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 断言无头浏览器请求成功
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=5').then(data => { assert.equal(data.assertions[0].method, 'browser.success'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '断言 "span#name" 元素里的文字为 "Amerigo"'
|
||||
- text: 断言元素“span#name”中的文本是“Amerigo”
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=5').then(data => { assert.equal(data.assertions[1].method, 'browser.text'); assert.equal(data.assertions[1].args[0], '\'span#name\''); assert.equal(data.assertions[1].args[1], '\'Amerigo\'');}, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '断言 "span#surname" 元素里的文字为 "Vespucci"'
|
||||
- text: 断言元素“span#surname”中的文本是“Vespucci”
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=5').then(data => { assert.equal(data.assertions[2].method, 'browser.text'); assert.equal(data.assertions[2].args[0], '\'span#surname\''); assert.equal(data.assertions[2].args[1], '\'Vespucci\'');}, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '断言 "span#dates" 的存在,并且计数为 1'
|
||||
- text: '断言元素“span #dalendar”存在且其计数为1'
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=5').then(data => { assert.equal(data.assertions[3].method, 'browser.element'); assert.equal(data.assertions[3].args[0], '\'span#dates\''); assert.equal(data.assertions[3].args[1], 1);}, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -48,11 +42,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,23 +2,15 @@
|
||||
id: 587d824f367417b2b2512c5c
|
||||
title: Run Functional Tests using a Headless Browser
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301595
|
||||
localeTitle: 使用 无头浏览器 运行功能测试
|
||||
videoUrl: ''
|
||||
localeTitle: 使用无头浏览器运行功能测试
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
下一项挑战,我们将会使用无头浏览器模拟人机交互。
|
||||
无头浏览器是一种没有图形用户界面的浏览器。这类工具对网页调试特别有效,因为它们可以跟普通浏览器一样理解和渲染 HTML,CSS 和 JavaScript。
|
||||
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。在接下来的挑战中,我们将使用名为“Headless Browser”的设备模拟人与页面的交互。无头浏览器是没有图形用户界面的Web浏览器。这些工具对于测试网页特别有用,因为它们能够以与浏览器相同的方式呈现和理解HTML,CSS和JavaScript。对于这些挑战,我们使用的是Zombie.JS。它是一个完全基于JS的轻量级浏览器,不依赖于安装其他二进制文件。此功能使其可用于Glitch等环境。还有许多其他(更强大的)选项。 <br>查看练习方向代码中的示例按照断言顺序,我们依赖它。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
这次挑战我们使用 Zombie.JS。它是一款完全基于 JS 的轻量级浏览器,不需要安装其他二进制文件。这个特性使它可以轻松在如 Glitch 的环境下使用。还有很多(更强大的)选项。<br>
|
||||
看看此挑战的给出的代码指引。请按顺序书写断言,顺序错误会影响系统判定。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -26,15 +18,15 @@ localeTitle: 使用 无头浏览器 运行功能测试
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=4').then(data => { assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 断言无头浏览器请求成功
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=4').then(data => { assert.equal(data.assertions[0].method, 'browser.success'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '断言 "span#name" 元素里的文字为 "Cristoforo"'
|
||||
- text: 断言元素“span#name”中的文本是“Cristoforo”
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=4').then(data => { assert.equal(data.assertions[1].method, 'browser.text'); assert.equal(data.assertions[1].args[0], '\'span#name\''); assert.equal(data.assertions[1].args[1], '\'Cristoforo\'');}, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '断言 "span#surname" 元素里的文字为 "Colombo"'
|
||||
- text: 断言元素“span#surname”中的文本是“Colombo”
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=4').then(data => { assert.equal(data.assertions[2].method, 'browser.text'); assert.equal(data.assertions[2].args[0], '\'span#surname\''); assert.equal(data.assertions[2].args[1], '\'Colombo\'');}, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: '断言 "span#dates" 的存在,并且计数为 1'
|
||||
- text: '断言元素“span #dalendar”存在且其计数为1'
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=functional&n=4').then(data => { assert.equal(data.assertions[3].method, 'browser.element'); assert.equal(data.assertions[3].args[0], '\'span#dates\''); assert.equal(data.assertions[3].args[1], 1);}, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -50,11 +42,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,29 +2,15 @@
|
||||
id: 587d824b367417b2b2512c49
|
||||
title: Test for Truthiness
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301596
|
||||
localeTitle: 真假测试
|
||||
videoUrl: ''
|
||||
localeTitle: 测试真实性
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
|
||||
.isTrue(true) 和 .isNotTrue(everything else) 可以通过测试。
|
||||
|
||||
```js
|
||||
assert.isTrue(true, 'this will pass with the boolean value true');
|
||||
assert.isTrue('true', 'this will NOT pass with the string value 'true');
|
||||
assert.isTrue(1, 'this will NOT pass with the number value 1');
|
||||
```
|
||||
|
||||
.isFalse() 和 .isNotFalse() 断言同样存在。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。使用assert.isTrue()或assert.isNotTrue()来使测试通过。 .isTrue(true)和.isNotTrue(其他所有内容)都将通过。 .isFalse()和.isNotFalse()也存在。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 assert.isTrue() 或 assert.isNotTrue() 通过测试。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -32,13 +18,13 @@ assert.isTrue(1, 'this will NOT pass with the number value 1');
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=3').then(data => {assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isTrue 或 isNotTrue
|
||||
- text: 选择正确的断言 - isTrue vs. isNotTrue
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=3').then(data => { assert.equal(data.assertions[0].method, 'isTrue', 'True is true'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isTrue 或 isNotTrue
|
||||
- text: 选择正确的断言 - isTrue vs. isNotTrue
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=3').then(data => { assert.equal(data.assertions[1].method, 'isTrue', 'Double negation of a truthy value is true'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isTrue 或 isNotTrue
|
||||
- text: 选择正确的断言 - isTrue vs. isNotTrue
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=3').then(data => { assert.equal(data.assertions[2].method, 'isNotTrue', 'A truthy object is not true - neither is a false one'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -54,11 +40,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,24 +2,15 @@
|
||||
id: 587d824d367417b2b2512c53
|
||||
title: Test if a String Contains a Substring
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301597
|
||||
localeTitle: 测试字符串是否包含子字符串
|
||||
videoUrl: ''
|
||||
localeTitle: 测试String是否包含子串
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
|
||||
#include(相对于 #notInclude)也可用于字符串!
|
||||
它断言字符串包含某个子串
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。 #include(在#notInclude上)也适用于字符串!!它断言实际的字符串包含预期的子字符串</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 <code>assert.include()</code> 或者 <code>assert.notInclude()</code> 通过测试。
|
||||
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -27,11 +18,11 @@ localeTitle: 测试字符串是否包含子字符串
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=13').then(data => { assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--include 或 notInclude
|
||||
- text: 选择正确的断言 - include与notInclude
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=13').then(data => { assert.equal(data.assertions[0].method, 'include', '\'Arrow\' contains \'row\'...'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--include 或 notInclude
|
||||
- text: 选择正确的断言 - include与notInclude
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=13').then(data => { assert.equal(data.assertions[1].method, 'notInclude', '... a \'dart\' doesn\'t contain a \'queue\''); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -47,11 +38,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,27 +2,15 @@
|
||||
id: 587d824c367417b2b2512c4f
|
||||
title: Test if a Value Falls within a Specific Range
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301598
|
||||
localeTitle: 测试某个值是否在特定范围内
|
||||
videoUrl: ''
|
||||
localeTitle: 测试值是否在特定范围内
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
|
||||
<code>.approximately(actual, expected, delta, [message])</code>
|
||||
断言在 +/- <code>delta</code> 范围内实际和 <code>expected</code> 相等。
|
||||
|
||||
actual = expected +/- range
|
||||
填写最小的 range(第三个参数)使测试总是通过
|
||||
该值应小于 1
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。 。近似。(实际,预期,范围,[消息])实际=预期+/-范围选择最小范围(第3个参数)使测试始终通过它应小于1 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 <code>assert.approximately()</code> 来通过测试。
|
||||
选择最小范围(第三个参数)来通过所有测试。它应该小于 1。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -30,11 +18,11 @@ actual = expected +/- range
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=9').then(data => {assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: Use approximately(actual, expected, range) - Chose the correct range
|
||||
- text: 使用大约(实际,预期,范围) - 选择正确的范围
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=9').then(data => { assert.equal(data.assertions[0].method, 'approximately'); assert.equal(data.assertions[0].args[2], 0.5, 'weirdNumbers(0.5) is in the range (0.5, 1.5]. It\'s within 1 +/- 0.5'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: Use approximately(actual, expected, range) - Chose the correct range
|
||||
- text: 使用大约(实际,预期,范围) - 选择正确的范围
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=9').then(data => { assert.equal(data.assertions[1].method, 'approximately'); assert.equal(data.assertions[1].args[2], 0.8, 'weirdNumbers(0.2) is in the range (0.2, 1.2]. It\'s within 1 +/- 0.8'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -50,11 +38,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,20 +2,15 @@
|
||||
id: 587d824d367417b2b2512c52
|
||||
title: Test if a Value is a String
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301599
|
||||
localeTitle: 测试某个值是否为字符串
|
||||
videoUrl: ''
|
||||
localeTitle: 测试值是否为字符串
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
#isString 断言一个值为字符串。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。 #isString断言实际值是一个字符串。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 <code>assert.isString()</code> 或者 <code>assert.isNotString()</code> 通过所有测试。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -23,13 +18,13 @@ localeTitle: 测试某个值是否为字符串
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=12').then(data => { assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isString 或 isNotString
|
||||
- text: 选择正确的断言 - isString vs. isNotString
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=12').then(data => { assert.equal(data.assertions[0].method, 'isNotString', 'A float number is not a string'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isString 或 isNotString
|
||||
- text: 选择正确的断言 - isString vs. isNotString
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=12').then(data => { assert.equal(data.assertions[1].method, 'isString', 'environment vars are strings (or undefined)'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isString 或 isNotString
|
||||
- text: 选择正确的断言 - isString vs. isNotString
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=12').then(data => { assert.equal(data.assertions[2].method, 'isString', 'A JSON is a string'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -45,11 +40,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,19 +2,15 @@
|
||||
id: 587d824d367417b2b2512c50
|
||||
title: Test if a Value is an Array
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301600
|
||||
localeTitle: 测试某个值是否为数组
|
||||
videoUrl: ''
|
||||
localeTitle: 测试值是否为数组
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 <code>assert.isArray()</code> 或者 <code>assert.isNotArray()</code> 通过所有测试。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -22,11 +18,11 @@ localeTitle: 测试某个值是否为数组
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=10').then(data => {assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isArray 或 isNotArray
|
||||
- text: 选择正确的断言 - isArray vs. isNotArray
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=10').then(data => { assert.equal(data.assertions[0].method, 'isArray', 'String.prototype.split() returns an Array'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isArray 或 isNotArray
|
||||
- text: 选择正确的断言 - isArray vs. isNotArray
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=10').then(data => { assert.equal(data.assertions[1].method, 'isNotArray', 'Array.prototype.indexOf() returns a number'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -42,11 +38,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,20 +2,15 @@
|
||||
id: 587d824e367417b2b2512c56
|
||||
title: Test if a Value is of a Specific Data Structure Type
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301601
|
||||
localeTitle: 测试某个值是否是特定的数据类型
|
||||
videoUrl: ''
|
||||
localeTitle: 测试值是否为特定数据结构类型
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
#typeOf 断言一个值的类型是给定字符串,这个字符串由 Object.prototype.toString 决定。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。 #typeOf断言值的类型是给定的字符串,由Object.prototype.toString确定。在适当的地方使用#typeOf或#notTypeOf </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 <code>assert.typeOf()</code> 或者 <code>assert.notTypeOf()</code> 来通过所有测试。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -23,17 +18,17 @@ localeTitle: 测试某个值是否是特定的数据类型
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(data => { assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--typeOf 或 notTypeOf
|
||||
- text: 选择正确的断言 - typeOf vs. notTypeOf
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(data => { assert.equal(data.assertions[0].method, 'typeOf', 'myCar is typeOf Object'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--typeOf 或 notTypeOf
|
||||
- text: 选择正确的断言 - typeOf vs. notTypeOf
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(data => { assert.equal(data.assertions[1].method, 'typeOf', 'Car.model is a String'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--typeOf 或 notTypeOf
|
||||
- text: 选择正确的断言 - typeOf vs. notTypeOf
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(data => { assert.equal(data.assertions[2].method, 'notTypeOf', 'Plane.wings is a Number (not a String)'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--typeOf 或 notTypeOf
|
||||
- text: 选择正确的断言 - typeOf vs. notTypeOf
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(data => { assert.equal(data.assertions[3].method, 'typeOf', 'Plane.engines is an Array'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--typeOf 或 notTypeOf
|
||||
- text: 选择正确的断言 - typeOf vs. notTypeOf
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=16').then(data => { assert.equal(data.assertions[4].method, 'typeOf', 'Car.wheels is a Number'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -49,11 +44,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,20 +2,15 @@
|
||||
id: 587d824b367417b2b2512c47
|
||||
title: Test if a Variable or Function is Defined
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301602
|
||||
localeTitle: 测试变量或函数是否已定义
|
||||
videoUrl: ''
|
||||
localeTitle: 测试变量或函数是否定义
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。使用assert.isDefined()或assert.isUndefined()来使测试通过</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 assert.isDefined() 或 assert.isUndefined() 通过测试。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -23,13 +18,13 @@ localeTitle: 测试变量或函数是否已定义
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=1').then(data => {assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isDefined 或 isUndefined
|
||||
- text: 选择正确的断言 - isDefined与isUndefined
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=1').then(data => { assert.equal(data.assertions[0].method, 'isDefined', 'Null is not undefined'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isDefined 或 isUndefined
|
||||
- text: 选择正确的断言 - isDefined与isUndefined
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=1').then(data => { assert.equal(data.assertions[1].method, 'isUndefined', 'Undefined is undefined'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isDefined 或 isUndefined
|
||||
- text: 选择正确的断言 - isDefined与isUndefined
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=1').then(data => { assert.equal(data.assertions[2].method, 'isDefined', 'A string is not undefined'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -45,11 +40,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,19 +2,15 @@
|
||||
id: 587d824d367417b2b2512c51
|
||||
title: Test if an Array Contains an Item
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301603
|
||||
localeTitle: 测试数组是否包含某个项目
|
||||
videoUrl: ''
|
||||
localeTitle: 测试数组是否包含项目
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 <code>assert.include()</code> 或者 <code>assert.notInclude()</code> 来通过所有测试。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -22,11 +18,11 @@ localeTitle: 测试数组是否包含某个项目
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=11').then(data => { assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--include 或 notInclude
|
||||
- text: 选择正确的断言 - include与notInclude
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=11').then(data => { assert.equal(data.assertions[0].method, 'notInclude', 'It\'s summer in july...'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--include 或 notInclude
|
||||
- text: 选择正确的断言 - include与notInclude
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=11').then(data => { assert.equal(data.assertions[1].method, 'include', 'JavaScript is a backend language !!'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -42,11 +38,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,21 +2,15 @@
|
||||
id: 587d824e367417b2b2512c55
|
||||
title: Test if an Object has a Property
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301604
|
||||
localeTitle: 测试对象是否具有某个属性
|
||||
videoUrl: ''
|
||||
localeTitle: 测试对象是否具有属性
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
#property 断言一个对象含有给定属性。
|
||||
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。 #property断言实际对象具有给定属性。在适当的地方使用#property或#notProperty </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 <code>assert.property()</code> 或者 <code>assert.notProperty()</code> 来通过所有测试。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -24,13 +18,13 @@ localeTitle: 测试对象是否具有某个属性
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=15').then(data => { assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--property 或 notProperty
|
||||
- text: 选择正确的断言 - property vs. notProperty
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=15').then(data => { assert.equal(data.assertions[0].method, 'notProperty', 'A car has not wings'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--property 或 notProperty
|
||||
- text: 选择正确的断言 - property vs. notProperty
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=15').then(data => { assert.equal(data.assertions[1].method, 'property', 'planes have engines'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--property 或 notProperty
|
||||
- text: 选择正确的断言 - property vs. notProperty
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=15').then(data => { assert.equal(data.assertions[2].method, 'property', 'Cars have wheels'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -46,11 +40,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,21 +2,15 @@
|
||||
id: 587d824e367417b2b2512c57
|
||||
title: Test if an Object is an Instance of a Constructor
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301605
|
||||
videoUrl: ''
|
||||
localeTitle: 测试对象是否是构造函数的实例
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
#instanceOf 断言一个对象是一个构造器的实例
|
||||
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。 #instanceOf声明对象是构造函数的实例。在适当的地方使用#instanceOf或#notInstanceOf </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
在适当的地方使用 #instanceOf 或 #notInstanceOf
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -24,15 +18,15 @@ localeTitle: 测试对象是否是构造函数的实例
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=17').then(data => { assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--instanceOf 或 notInstanceOf
|
||||
- text: 选择正确的断言 - instanceOf与notInstanceOf
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=17').then(data => { assert.equal(data.assertions[0].method, 'notInstanceOf', 'myCar is not an instance of Plane'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--instanceOf 或 notInstanceOf
|
||||
- text: 选择正确的断言 - instanceOf与notInstanceOf
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=17').then(data => { assert.equal(data.assertions[1].method, 'instanceOf', 'airlinePlane is an instance of Plane'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--instanceOf 或 notInstanceOf
|
||||
- text: 选择正确的断言 - instanceOf与notInstanceOf
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=17').then(data => { assert.equal(data.assertions[2].method, 'instanceOf', 'everything is an Object in JavaScript...'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--instanceOf 或 notInstanceOf
|
||||
- text: 选择正确的断言 - instanceOf与notInstanceOf
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=17').then(data => { assert.equal(data.assertions[3].method, 'notInstanceOf', 'myCar.wheels is not an instance of String'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -48,11 +42,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,20 +2,15 @@
|
||||
id: 587d824c367417b2b2512c4e
|
||||
title: Test if One Value is Below or At Least as Large as Another
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301606
|
||||
localeTitle: 测试一个值是否小于或等于另一个值
|
||||
videoUrl: ''
|
||||
localeTitle: 测试一个值是否低于或低至另一个值
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。 .isBelow()=> a <b,.isAtLeast => a> = b </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 <code>assert.isBelow()</code> (如 less than) 或者 <code>assert.isAtLeast()</code> (如 greater than or equal) 来通过所有测试。
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -23,15 +18,15 @@ localeTitle: 测试一个值是否小于或等于另一个值
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=8').then(data => {assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isBelow 或 isAtLeast
|
||||
- text: 选择正确的断言 - isBelow vs. isAtLeast
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=8').then(data => { assert.equal(data.assertions[0].method, 'isAtLeast', '5 is at least (>=) 5'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isBelow 或 isAtLeast
|
||||
- text: 选择正确的断言 - isBelow vs. isAtLeast
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=8').then(data => { assert.equal(data.assertions[1].method, 'isAtLeast', '2 * Math.random() is at least 0'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isBelow 或 isAtLeast
|
||||
- text: 选择正确的断言 - isBelow vs. isAtLeast
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=8').then(data => { assert.equal(data.assertions[2].method, 'isBelow', '1 is smaller than 2'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isBelow 或 isAtLeast
|
||||
- text: 选择正确的断言 - isBelow vs. isAtLeast
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=8').then(data => { assert.equal(data.assertions[3].method, 'isBelow', '2/3 (0.6666) is smaller than 1'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -47,11 +42,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,25 +2,15 @@
|
||||
id: 587d824b367417b2b2512c48
|
||||
title: Use Assert.isOK and Assert.isNotOK
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301607
|
||||
localeTitle: 使用 Assert.isok() 和 Assert.isNotOK()
|
||||
videoUrl: ''
|
||||
localeTitle: 使用Assert.isOK和Assert.isNotOK
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
|
||||
使用 assert.isOk() 或 assert.isNotOk() 通过测试。
|
||||
[Truthy 参考](https://developer.mozilla.org/en-US/docs/Glossary/Truthy)
|
||||
[Falsy 参考](https://developer.mozilla.org/en-US/docs/Glossary/Falsy)
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。使用assert.isOk()或assert.isNotOk()来使测试通过。 .isOk(truthy)和.isNotOk(falsey)将通过。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
|
||||
.isOk(truthy) 或 .isNotOk(falsey) 可以通过测试。
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -28,13 +18,13 @@ localeTitle: 使用 Assert.isok() 和 Assert.isNotOK()
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=2').then(data => {assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isOk 或 isNotOk
|
||||
- text: 选择正确的断言 - isOk vs. isNotOk
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=2').then(data => { assert.equal(data.assertions[0].method, 'isNotOk', 'Null is falsy'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isOk 或 isNotOk
|
||||
- text: 选择正确的断言 - isOk vs. isNotOk
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=2').then(data => { assert.equal(data.assertions[1].method, 'isOk','A string is truthy'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--isOk 或 isNotOk
|
||||
- text: 选择正确的断言 - isOk vs. isNotOk
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=2').then(data => { assert.equal(data.assertions[2].method, 'isOk', 'true is truthy'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -50,11 +40,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,21 +2,15 @@
|
||||
id: 587d824d367417b2b2512c54
|
||||
title: Use Regular Expressions to Test a String
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301608
|
||||
videoUrl: ''
|
||||
localeTitle: 使用正则表达式测试字符串
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
#match 断言一个值匹配一个正则表达式(第二个参数)
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。 #match断言实际值与第二个参数正则表达式匹配。 </section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 <code>assert.match()</code> 来通过所有测试。
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -24,11 +18,11 @@ localeTitle: 使用正则表达式测试字符串
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=14').then(data => { assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--match 或 notMatch
|
||||
- text: 选择正确的断言 - 匹配与非匹配
|
||||
testString: 'getUserInput => $.get(getUserInput(''url'') + ''/_api/get-tests?type=unit&n=14'').then(data => { assert.equal(data.assertions[0].method, ''match'', ''\''# name: John Doe, age: 35\'' matches the regex''); }, xhr => { throw new Error(xhr.responseText); })'
|
||||
- text: 请选择正确的断言--match 或 notMatch
|
||||
- text: 选择正确的断言 - 匹配与非匹配
|
||||
testString: 'getUserInput => $.get(getUserInput(''url'') + ''/_api/get-tests?type=unit&n=14'').then(data => { assert.equal(data.assertions[1].method, ''notMatch'', ''\''# name: Paul Smith III, age: twenty-four\'' does not match the regex (the age must be numeric)''); }, xhr => { throw new Error(xhr.responseText); })'
|
||||
|
||||
```
|
||||
@@ -44,11 +38,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,21 +2,15 @@
|
||||
id: 587d824b367417b2b2512c4a
|
||||
title: Use the Double Equals to Assert Equality
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301609
|
||||
localeTitle: 用两个等号断言相等
|
||||
videoUrl: ''
|
||||
localeTitle: 使用Double Equals来断言平等
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
.equal() 使用 '==' 比较对象
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。 .equal(),. notEqual().equal()使用'=='比较对象</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 <code>assert.equal()</code> 或者 <code>assert.notEqual()</code> 来通过所有测试。
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -24,15 +18,15 @@ localeTitle: 用两个等号断言相等
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=4').then(data => {assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--equal 或 notEqual
|
||||
- text: 选择正确的断言 - 相等与不等
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=4').then(data => { assert.equal(data.assertions[0].method, 'equal', 'Numbers are coerced into strings with == '); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--equal 或 notEqual
|
||||
- text: 选择正确的断言 - 相等与不等
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=4').then(data => { assert.equal(data.assertions[1].method, 'notEqual', ' == compares object references'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--equal 或 notEqual
|
||||
- text: 选择正确的断言 - 相等与不等
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=4').then(data => { assert.equal(data.assertions[2].method, 'equal', '6 * \'2\' is 12 ! It should be equal to \'12\''); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--equal 或 notEqual
|
||||
- text: 选择正确的断言 - 相等与不等
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=4').then(data => { assert.equal(data.assertions[3].method, 'notEqual', '6 + \'2\' is \'62\'...'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -48,11 +42,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@@ -2,21 +2,15 @@
|
||||
id: 587d824b367417b2b2512c4b
|
||||
title: Use the Triple Equals to Assert Strict Equality
|
||||
challengeType: 2
|
||||
isHidden: false
|
||||
forumTopicId: 301610
|
||||
localeTitle: 用三个等号断言严格相等
|
||||
videoUrl: ''
|
||||
localeTitle: 使用Triple Equals断言严格平等
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
注意,本项目在 <a href='https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/'>这个 Glitch 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
.strictEqual() 使用 '===' 比较对象
|
||||
</section>
|
||||
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-mochachai/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-mochachai/">GitHub</a>克隆的。 .strictEqual(),. notStrictEqual()。sqleEqual()使用'==='比较对象</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
使用 <code>assert.strictEqual()</code> 或者 <code>assert.notStrictEqual()</code> 来通过所有测试。
|
||||
|
||||
<section id="instructions">
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
@@ -24,15 +18,15 @@ localeTitle: 用三个等号断言严格相等
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 需通过全部测试
|
||||
- text: 所有测试都应该通过
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=5').then(data => {assert.equal(data.state,'passed'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--strictEqual 或 notStrictEqual
|
||||
- text: 选择正确的断言 - strictEqual vs. notStrictEqual
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=5').then(data => { assert.equal(data.assertions[0].method, 'notStrictEqual', 'with strictEqual the type must match'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--strictEqual 或 notStrictEqual
|
||||
- text: 选择正确的断言 - strictEqual vs. notStrictEqual
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=5').then(data => { assert.equal(data.assertions[1].method, 'strictEqual', '3*2 = 6...'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--strictEqual 或 notStrictEqual
|
||||
- text: 选择正确的断言 - strictEqual vs. notStrictEqual
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=5').then(data => { assert.equal(data.assertions[2].method, 'strictEqual', '6 * \'2\' is 12. Types match !'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 请选择正确的断言--strictEqual 或 notStrictEqual
|
||||
- text: 选择正确的断言 - strictEqual vs. notStrictEqual
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/get-tests?type=unit&n=5').then(data => { assert.equal(data.assertions[3].method, 'notStrictEqual', 'Even if they have the same elements, the Arrays are notStrictEqual'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
@@ -48,11 +42,6 @@ tests:
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
// solution required
|
||||
```
|
||||
|
||||
</section>
|
||||
|
Reference in New Issue
Block a user