feat: add 'back/front end' in curriculum (#42596)
* chore: rename APIs and Microservices to include "Backend" (#42515) * fix typo * fix typo * undo change * Corrected grammar mistake Corrected a grammar mistake by removing a comma. * change APIs and Microservices cert title * update title * Change APIs and Microservices certi title * Update translations.json * update title * feat(curriculum): rename apis and microservices cert * rename folder structure * rename certificate * rename learn Markdown * apis-and-microservices -> back-end-development-and-apis * update backend meta * update i18n langs and cypress test Co-authored-by: Shaun Hamilton <shauhami020@gmail.com> * fix: add development to front-end libraries (#42512) * fix: added-the-word-Development-to-front-end-libraries * fix/added-the-word-Development-to-front-end-libraries * fix/added-word-development-to-front-end-libraries-in-other-related-files * fix/added-the-word-Development-to-front-end-and-all-related-files * fix/removed-typos-from-last-commit-in-index.md * fix/reverted-changes-that-i-made-to-dependecies * fix/removed xvfg * fix/reverted changes that i made to package.json * remove unwanted changes * front-end-development-libraries changes * rename backend certSlug and README * update i18n folder names and keys * test: add legacy path redirect tests This uses serve.json from the client-config repo, since we currently use that in production * fix: create public dir before moving serve.json * fix: add missing script * refactor: collect redirect tests * test: convert to cy.location for stricter tests * rename certificate folder to 00-certificates * change crowdin config to recognise new certificates location * allow translations to be used Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com> * add forwards slashes to path redirects * fix cypress path tests again * plese cypress * fix: test different challenge Okay so I literally have no idea why this one particular challenge fails in Cypress Firefox ONLY. Tom and I paired and spun a full build instance and confirmed in Firefox the page loads and redirects as expected. Changing to another bootstrap challenge passes Cypress firefox locally. Absolutely boggled by this. AAAAAAAAAAAAAAA * fix: separate the test Okay apparently the test does not work unless we separate it into a different `it` statement. >:( >:( >:( >:( Co-authored-by: Sujal Gupta <55016909+heysujal@users.noreply.github.com> Co-authored-by: Noor Fakhry <65724923+NoorFakhry@users.noreply.github.com> Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com>
This commit is contained in:
@ -0,0 +1,79 @@
|
||||
---
|
||||
id: 587d7fb1367417b2b2512bf4
|
||||
title: 通过链式调用中间件来创建时间服务
|
||||
challengeType: 2
|
||||
forumTopicId: 301510
|
||||
dashedName: chain-middleware-to-create-a-time-server
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
使用 `app.METHOD(path, middlewareFunction)` 可以在指定的路由挂载中间件, 也可以在路由定义中链式调用中间件。
|
||||
|
||||
请看以下示例:
|
||||
|
||||
```js
|
||||
app.get('/user', function(req, res, next) {
|
||||
req.user = getTheUserSync(); // Hypothetical synchronous operation
|
||||
next();
|
||||
}, function(req, res) {
|
||||
res.send(req.user);
|
||||
});
|
||||
```
|
||||
|
||||
此方法可用于将服务操作拆分为较小的单元, 这可以让应用拥有更好的结构,也便于在不同的位置上复用代码; 此方法还可用于对数据执行某些验证。 可以在每一个中间件堆栈中,阻止当前链的执行,并将控制权传递给专门设计用于处理错误的函数; 或者可以将控制权传递给下一个匹配的路由,以处理特殊情况, 我们将在高级 Express 章节中看到这些内容。
|
||||
|
||||
# --instructions--
|
||||
|
||||
在路由 `app.get('/now', ...)` 中链式调用中间件函数,并在最后处理。 在中间件函数中给请求对象中的 `req.time` 添加到当前时间, 可以使用 `new Date().toString()`, 在处理函数中,使用 `{time: req.time}` 结构的 JSON 对象来响应请求。
|
||||
|
||||
**注意:** 如果不链式调用中间件,测试将不能通过。 如果将中间件函数挂载在其他地方,即使输出结果正确,测试也会失败。
|
||||
|
||||
# --hints--
|
||||
|
||||
/now 接口应该已经挂载了中间件
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/chain-middleware-time').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.stackLength,
|
||||
2,
|
||||
'"/now" route has no mounted middleware'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
/now 接口应该返回一个现在时间 +/-20 秒的时间
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/chain-middleware-time').then(
|
||||
(data) => {
|
||||
var now = new Date();
|
||||
assert.isAtMost(
|
||||
Math.abs(new Date(data.time) - now),
|
||||
20000,
|
||||
'the returned time is not between +- 20 secs from now'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@ -0,0 +1,78 @@
|
||||
---
|
||||
id: 587d7fb2367417b2b2512bf8
|
||||
title: 从 POST 请求中获取数据
|
||||
challengeType: 2
|
||||
forumTopicId: 301511
|
||||
dashedName: get-data-from-post-requests
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
在路径 `/name` 挂载一个 POST 处理方法, 和前面一样, 我们已经在 html 首页准备了一份表单, 它将提交与练习 10 相同的数据(查询字符串), 如果 body-parser 正确配置好了,那么就可以在 `req.body` 对象中找到请求的参数。 来看看一个常规的例子:
|
||||
|
||||
<blockquote>路由:POST '/library'<br>URL 编码的请求正文:userId=546&bookId=6754<br>req.body:{userId: '546', bookId: '6754'}</blockquote>
|
||||
|
||||
响应和前面一样的 JSON 对象 `{name: 'firstname lastname'}`。 你可以使用首页应用提供的 html 表单,来测试你的 API 是否正常工作。
|
||||
|
||||
提示:除了 GET 和 POST,还有其他几种 http 方法。 按照惯例,http 动词和在服务端执行的某种操作之间有对应关系, 这种对应关系通常如下:
|
||||
|
||||
POST(有时候是 PUT)- 使用请求发送信息,以创建新资源;
|
||||
|
||||
GET- 读取不用修改的已存在的资源;
|
||||
|
||||
PUT 或者 PATCH(有时候是 POST)- 发送数据,以更新资源;
|
||||
|
||||
DELETE=> 删除一个资源。
|
||||
|
||||
还有其他两种方法常用于与服务进行交互。 除了 GET 之外,上面列出的所有方法都可以负载数据(即数据都能放到消息正文中), 这些方法也可以使用 body-parser 中间件。
|
||||
|
||||
# --hints--
|
||||
|
||||
测试 1:你的 API 接口应该使用正确的名字来响应
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.post(getUserInput('url') + '/name', { first: 'Mick', last: 'Jagger' }).then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.name,
|
||||
'Mick Jagger',
|
||||
'Test 1: "POST /name" route does not behave as expected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
测试 2:你的 API 接口应该使用正确的名字来响应
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.post(getUserInput('url') + '/name', {
|
||||
first: 'Keith',
|
||||
last: 'Richards'
|
||||
}).then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.name,
|
||||
'Keith Richards',
|
||||
'Test 2: "POST /name" route does not behave as expected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@ -0,0 +1,67 @@
|
||||
---
|
||||
id: 587d7fb2367417b2b2512bf6
|
||||
title: 从客户端获取输入的查询参数
|
||||
challengeType: 2
|
||||
forumTopicId: 301512
|
||||
dashedName: get-query-parameter-input-from-the-client
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
从客户端获取输入的另一种常见方式是使用查询字符串对路由路径中的数据进行编码, 查询字符串使用标记(?)分隔,并且包含键值对 field=value, 每对键值使用连字号(&)分隔。 Express 能够从查询字符串中解析这些数据,并且把它放到 `req.query` 对象中。 有些字符(如百分号(%))不能在出现在 URL 中,它们在发送前必须以不同的格式进行编码。 如果使用 JavaScript 的 API,可以用特定的方法来编码/解码这些字符。
|
||||
|
||||
<blockquote>路由地址:'/library'<br> 实际请求 URL:'/library?userId=546&bookId=6754'<br>req.query:{userId: '546', bookId: '6754'}</blockquote>
|
||||
|
||||
# --instructions--
|
||||
|
||||
构建一个 API 接口,使用路由挂载在 `GET /name` 上, 使用一个 JSON 文件来响应,它的结构是这样的:`{ name: 'firstname lastname'}`, 名字(first name)和姓氏(last name)参数应该编码在查询参数中,例如:`?first=firstname&last=lastname`。
|
||||
|
||||
**注意:** 在后面的练习中,我们将向相同的路由路径 `/name` 发送 POST 请求来接收数据。 如果愿意,可以使用`app.route(path).get(handler).post(handler)`这中写法, 这种语法允许在同一路径路由上链式调用不同的请求方法, 可以节省一点打字时间,也可以让代码看起来更清晰。
|
||||
|
||||
# --hints--
|
||||
|
||||
测试 1:你的 API 应该用正确的名字来响应
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/name?first=Mick&last=Jagger').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.name,
|
||||
'Mick Jagger',
|
||||
'Test 1: "GET /name" route does not behave as expected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
测试 2:你的 API 应该用正确的名字来响应
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/name?last=Richards&first=Keith').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.name,
|
||||
'Keith Richards',
|
||||
'Test 2: "GET /name" route does not behave as expected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@ -0,0 +1,65 @@
|
||||
---
|
||||
id: 587d7fb2367417b2b2512bf5
|
||||
title: 从客户端获取输入的路由参数
|
||||
challengeType: 2
|
||||
forumTopicId: 301513
|
||||
dashedName: get-route-parameter-input-from-the-client
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
在构建 API 时,要让用户告诉我们他们想从服务中获取什么。 举个例子,如果客户请求数据库中存储的用户信息,他们需要一种方法让我们知道他们对哪个用户感兴趣, 使用路由参数可以实现这个需求。 路由参数是由斜杠(/)分隔的 URL 命名段, 每一小段能捕获与其位置匹配的 URL 部分的值, 捕获的值能够在 `req.params` 对象中找到。
|
||||
|
||||
<blockquote>路由地址:'/user/:userId/book/:bookId'<br> 实际请求 URL:'/user/546/book/6754'<br> req.params:{userId: '546', bookId: '6754'}</blockquote>
|
||||
|
||||
# --instructions--
|
||||
|
||||
在路由 `GET /:word/echo` 中构建一个响应服务, 响应一个采用 `{echo: word}` 结构的 JSON 对象。 可以在 `req.params.word` 中找到要重复的单词, 可以在浏览器的地址栏测试你的路由,访问一些匹配的路由,比如:`your-app-rootpath/freecodecamp/echo`。
|
||||
|
||||
# --hints--
|
||||
|
||||
测试 1:你的 echo 服务应该正确地重复单词
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/eChOtEsT/echo').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.echo,
|
||||
'eChOtEsT',
|
||||
'Test 1: the echo server is not working as expected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
测试 2:你的 echo 服务应该正确地重复单词
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/ech0-t3st/echo').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.echo,
|
||||
'ech0-t3st',
|
||||
'Test 2: the echo server is not working as expected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@ -0,0 +1,57 @@
|
||||
---
|
||||
id: 587d7fb1367417b2b2512bf3
|
||||
title: 实现一个根级的请求记录中间件
|
||||
challengeType: 2
|
||||
forumTopicId: 301514
|
||||
dashedName: implement-a-root-level-request-logger-middleware
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
前面我们介绍了 `express.static()` 中间件函数, 现在是时候更详细地了解什么是中间件了。 中间件函数是一个接收 3 个参数的函数,这 3 个参数分别是:请求对象、响应对象和在应用的请求-响应循环中的下一个函数。 中间件函数执行一些可能对应用程序产生一些效果的代码,通常还会在请求对象或者响应对象里添加一些信息, 它们也可以在满足某些条件时通过发送响应来结束循环, 如果在它们完成时没有发送响应,那么就会开始执行堆栈中的下一个函数, `next()` 将触发调用第 3 个参数。
|
||||
|
||||
看看下面的例子:
|
||||
|
||||
```js
|
||||
function(req, res, next) {
|
||||
console.log("I'm a middleware...");
|
||||
next();
|
||||
}
|
||||
```
|
||||
|
||||
假设在某个路由上安装了这个中间件函数, 当一个请求与路由匹配时,它会显示字符串“I’m a middleware…”,然后它执行堆栈中的下一个函数。 在这个练习中,我们将构建根级中间件。 正如我们在挑战 4 中看到的,要在根层级安装中间件函数,我们可以使用 `app.use(<mware-function>)` 方法。 在这种情况下,该函数将对所有请求执行,但也可以设置更具体的条件来执行, 例如,如果你希望某个函数只针对 POST 请求执行,可以使用 `app.post(<mware-function>)` 方法。 所有的 HTTP 动词(GET、DELETE、PUT……)都存在类似的方法。
|
||||
|
||||
# --instructions--
|
||||
|
||||
构建一个简单的日志记录器。 对于每个请求,它应该在控制台中打印一个采用以下格式的字符串:`method path - ip`, 例如:`GET /json - ::ffff:127.0.0.1`。 注意 `method` 和 `path` 之间有一个空格,并且 `path` 和 `ip` 中间的破折号的两边都有空格。 可以使用 `req.method`、`req.path` 和 `req.ip` 从请求对象中分别获取请求方法(http 动词)、路由相对路径和请求者的 ip 信息。 当你完成时,记得要调用 `next()`,否则服务器将一直处于挂起状态。 请确保“Logs”是打开的,观察一下当一些请求到达时会发生什么事情。
|
||||
|
||||
**注意:**Express 按照函数在代码中出现的顺序来执行, 中间件也是如此。 如果你想让中间件函数适用于所有路由,那么应该在路由之前配置好中间件。
|
||||
|
||||
# --hints--
|
||||
|
||||
应该激活根级记录器中间件
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/root-middleware-logger').then(
|
||||
(data) => {
|
||||
assert.isTrue(
|
||||
data.passed,
|
||||
'root-level logger is not working as expected'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@ -0,0 +1,53 @@
|
||||
---
|
||||
id: 587d7fb0367417b2b2512bed
|
||||
title: 认识 Node 的控制台
|
||||
challengeType: 2
|
||||
forumTopicId: 301515
|
||||
dashedName: meet-the-node-console
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
可以采用下面的任意一种方式完成这些挑战:
|
||||
|
||||
- 克隆 [这个 GitHub 仓库](https://github.com/freeCodeCamp/boilerplate-express/) 并在本地完成项目。
|
||||
- 使用[我们的 Repl.it 上的初始化项目](https://replit.com/github/freeCodeCamp/boilerplate-express)来完成项目。
|
||||
- 使用你选择的网站生成器来完成项目, 并确保包含了我们 GitHub 仓库的所有文件。
|
||||
|
||||
当完成本项目,请确认有一个正常运行的 demo 可以公开访问。 然后将 URL 提交到 `Solution Link` 中。
|
||||
|
||||
在开发过程中,能够随时看到代码的运行结果是非常重要的。
|
||||
|
||||
Node 只是一个 JavaScript 环境。 与客户端 JavaScript 一样,你可以使用控制台显示有用的调试信息。 在本地计算机上,你可以在终端中输出调试信息。 在 Replit 上,右侧边栏会默认打开一个终端。
|
||||
|
||||
我们建议在做这些挑战题时保持终端打开的状态。 通过这些终端的输出,你可能会发现这些错误的本质原因。
|
||||
|
||||
# --instructions--
|
||||
|
||||
修改 `myApp.js` 文件,在控制台打印出 “Hello World”。
|
||||
|
||||
# --hints--
|
||||
|
||||
控制台应该输出 `"Hello World"`
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/hello-console').then(
|
||||
(data) => {
|
||||
assert.isTrue(data.passed, '"Hello World" is not in the server console');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@ -0,0 +1,51 @@
|
||||
---
|
||||
id: 587d7fb0367417b2b2512bef
|
||||
title: 提供 HTML 文件服务
|
||||
challengeType: 2
|
||||
forumTopicId: 301516
|
||||
dashedName: serve-an-html-file
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
通过 `res.sendFile(path)` 方法给请求响应一个文件, 可以把它放到路由处理 `app.get('/', ...)` 中。 在后台,这个方法会根据你想发送的文件的类型,设置适当的消息头信息来告诉浏览器如何处理它, 然后读取并发送文件, 此方法需要文件的绝对路径。 建议使用 Node. js 的全局变量 `__dirname` 来计算出这个文件的绝对路径:
|
||||
|
||||
```js
|
||||
absolutePath = __dirname + relativePath/file.ext
|
||||
```
|
||||
|
||||
# --instructions--
|
||||
|
||||
发送文件 `/views/index.html` 作为 `/` 的 GET 请求的响应。 如果实时查看应用,你会看到一个大的 HTML 标题(以及我们稍后将使用的表单……),目前它们还没有任何样式。
|
||||
|
||||
**注意:** 你可以编辑上一个挑战的解题代码,或者创建一个新的代码片段。 如果你创建一个新的代码片段,请记住 Express 会从上到下匹配路由,并执行第一个匹配的处理程序, 你必须注释掉前面的代码,否则服务器还是响应之前的字符串。
|
||||
|
||||
# --hints--
|
||||
|
||||
应用应该响应 views/index.html 文件
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url')).then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/<h1>.*<\/h1>/,
|
||||
'Your app does not serve the expected HTML'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@ -0,0 +1,47 @@
|
||||
---
|
||||
id: 587d7fb1367417b2b2512bf1
|
||||
title: 在指定路由上提供 JSON 服务
|
||||
challengeType: 2
|
||||
forumTopicId: 301517
|
||||
dashedName: serve-json-on-a-specific-route
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
HTML 服务器提供 HTML 服务,而 API 提供数据服务。 <dfn>REST</dfn>(REpresentational State Transfer)API 允许以简单的方式进行数据交换,对于客户端不必要知道服务器的细节。 客户只需要知道资源在哪里(URL),以及想执行的动作(动词)。 GET 动词常被用来获取无需修改的信息。 如今,网络上的移动数据首选格式是 JSON, 简而言之,JSON 是一种可以方便地用字符串表示 JavaScript 对象的方式,因此它很容易传输。
|
||||
|
||||
我们来创建一个简单的 API,创建一个路径为 `/json` 且返回数据是 JSON 格式的路由, 可以像之前那样用 `app.get()` 方法来做。 然后在路由处理部分使用 `res.json()` 方法,并传入一个对象作为参数, 这个方法会结束请求响应循环(request-response loop),然后返回数据。 原来,一个有效的 JavaScript 对象会转化为字符串,然后会设置适当的消息头来告诉浏览器:“这是一个 JSON 数据”,最后将数据返回给客户端。 一个有效的对象通常是这种结构:`{key: data}`, `data` 可以是数字、字符串、嵌套对象或数组, `data` 也可以是变量或者函数返回值,在这种情况下,它们先求值再转成字符串。
|
||||
|
||||
# --instructions--
|
||||
|
||||
当向路由 `/json` 发送 GET 请求,将对象 `{"message": "Hello json"}` 以 JSON 格式返回给客户端, 浏览器访问 `your-app-url/json` 时,应该在屏幕上看到这个消息。
|
||||
|
||||
# --hints--
|
||||
|
||||
访问端口 `/json` 应该返回一个 json 对象 `{"message": "Hello json"}`
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/json').then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data.message,
|
||||
'Hello json',
|
||||
"The '/json' endpoint does not serve the right data"
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@ -0,0 +1,51 @@
|
||||
---
|
||||
id: 587d7fb0367417b2b2512bf0
|
||||
title: 提供静态资源服务
|
||||
challengeType: 2
|
||||
forumTopicId: 301518
|
||||
dashedName: serve-static-assets
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
HTML 服务器通常有一个或多个用户可以访问的目录。 你可以将应用程序所需的静态资源 (样式表、脚本、图片) 放在那里。
|
||||
|
||||
在 Express 中可以使用中间件 `express.static(path)` 来设置此功能,它的参数 `path` 就是包含静态资源文件的绝对路径。
|
||||
|
||||
如果你不知道什么是中间件……别担心,我们将在后面详细讨论。 其实,中间件就是一个拦截路由处理方法并在里面添加一些信息的函数。 使用 `app.use(path, middlewareFunction)` 方法来加载一个中间件, 它的第一个参数 `path` 是可选的, 如果没设置第一个参数,那么所有的请求都会经过这个中间件处理。
|
||||
|
||||
# --instructions--
|
||||
|
||||
使用 `app.use()` 为路径 `/public` 的请求安装 `express.static()` 中间件, 静态资源的绝对路径是 `__dirname + /public`。
|
||||
|
||||
现在应用应该能提供 CSS 样式表, 请注意, `/public/style.css` 文件被项目模板的 `/views/index.html` 引用, 首页应该更好看了。
|
||||
|
||||
# --hints--
|
||||
|
||||
应用应该将资源文件从 `/public` 目录发送到 `/public` 路径
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/public/style.css').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/body\s*\{[^\}]*\}/,
|
||||
'Your app does not serve static assets'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@ -0,0 +1,57 @@
|
||||
---
|
||||
id: 587d7fb0367417b2b2512bee
|
||||
title: 启动一个 Express 服务
|
||||
challengeType: 2
|
||||
forumTopicId: 301519
|
||||
dashedName: start-a-working-express-server
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
在 `myApp.js` 文件的前两行中,你可以看到创建一个 Express 应用对象很简单。 这个对象有几种方法,在后面的挑战中将学习到其中的许多部分。 一个基础的方法是 `app.listen(port)`。 它处于运行状态时告诉服务器监听指定的端口。 出于测试的原因,需要应用在后台运行,所以在 `server.js` 中已经添加了这个方法。
|
||||
|
||||
让我们在服务端输出第一个字符串! 在 Express 中,路由采用这种结构:`app.METHOD(PATH, HANDLER)`, METHOD 是 http 请求方法的小写形式, PATH 是服务器上的相对路径(它可以是一个字符串,甚至可以是正则表达式), HANDLER 是匹配路由时 Express 调用的函数, 处理函数采用这种形式:`function(req, res) {...}`,其中 req 是请求对象,res 是响应对象, 例如:
|
||||
|
||||
```js
|
||||
function(req, res) {
|
||||
res.send('Response String');
|
||||
}
|
||||
```
|
||||
|
||||
将会响应一个字符串“Response String”。
|
||||
|
||||
# --instructions--
|
||||
|
||||
当 GET 请求 `/`(根路由 )时,使用 `app.get()` 方法响应一个“Hello Express”字符串。 通过查看日志确保代码正常运行,如果使用 Replit 可以在预览中查看结果。
|
||||
|
||||
**注意:** 这些课程的所有代码应该放在开始给出的几行代码之间。
|
||||
|
||||
# --hints--
|
||||
|
||||
应用应该返回字符串“Hello Express”
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url')).then(
|
||||
(data) => {
|
||||
assert.equal(
|
||||
data,
|
||||
'Hello Express',
|
||||
'Your app does not serve the text "Hello Express"'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@ -0,0 +1,63 @@
|
||||
---
|
||||
id: 587d7fb2367417b2b2512bf7
|
||||
title: 使用 body-parser 来解析 POST 请求
|
||||
challengeType: 2
|
||||
forumTopicId: 301520
|
||||
dashedName: use-body-parser-to-parse-post-requests
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
除了 GET 还有另一个常见的 HTTP 动词,即 POST。 POST 是使用 HTML 表单发送客户端数据的默认方法。 在 REST 规范中,POST 常用于发送数据以在数据库中创建新项目(新用户或新博客文章)。 在这个项目中没有使用数据库,但下面将学习如何处理 POST 请求。
|
||||
|
||||
在这些类型的请求中,数据不会出现在 URL 中,而是隐藏在请求正文中。 请求正文也是 HTML 请求的一部分,被称为负载。 即使数据在 URL 中是不可见的,也不意味着它是私有的。 要了解原因,请观察 HTTP POST 请求的原始内容:
|
||||
|
||||
```http
|
||||
POST /path/subpath HTTP/1.0
|
||||
From: john@example.com
|
||||
User-Agent: someBrowser/1.0
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Content-Length: 20
|
||||
|
||||
name=John+Doe&age=25
|
||||
```
|
||||
|
||||
正如你所看到的,正文被编码成类似查询字符串的形式, 这是 HTML 表单使用的默认格式。 我们还可以通过 Ajax 使用 JSON 来处理具有更复杂结构的数据。 还有另一种类型的编码:multipart/form-data, 它被用来上传二进制文件。 在本练习中,我们将使用 URL 编码请求正文。 要解析来自 POST 请求的数据,你必须安装 `body-parser` 包, 这个包包含一套可以解码不同格式数据的中间件。
|
||||
|
||||
# --instructions--
|
||||
|
||||
在 `package.json` 中安装 `body-parser` 模块, 然后在文件顶部 `require` 进来, 用变量 `bodyParser` 保存它。 通过中间件的 `bodyParser.urlencoded({extended: false})` 方法处理 URL 编码数据, 将通过先前的方法调用返回的函数传递到 `app.use()`。 中间件通常挂载在所有需要它的路由之前。
|
||||
|
||||
**注意:** `extended` 是一个配置选项, 告诉 `body-parser` 需要使用哪个解析。 当 `extended=false` 时,它使用经典编码 `querystring` 库。 当 `extended=true`时,它使用 `qs` 库进行解析。
|
||||
|
||||
当使用 `extended=false` 时,值可以只是字符串或数组。 使用 `querystring` 时返回的对象并不继承的 JavaScript `Object`,这意味着 `hasOwnProperty`、`toString` 等函数将不可用。 拓展版本的数据更加灵活,但稍逊于 JSON。
|
||||
|
||||
# --hints--
|
||||
|
||||
应该挂载 “body-parser” 中间件
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/add-body-parser').then(
|
||||
(data) => {
|
||||
assert.isAbove(
|
||||
data.mountedAt,
|
||||
0,
|
||||
'"body-parser" is not mounted correctly'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
@ -0,0 +1,52 @@
|
||||
---
|
||||
id: 587d7fb1367417b2b2512bf2
|
||||
title: 使用 .env 文件
|
||||
challengeType: 2
|
||||
forumTopicId: 301521
|
||||
dashedName: use-the--env-file
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`.env` 文件是一个用于将环境变量传给应用程序的隐藏文件, 这是一个除了开发者之外没人可以访问的私密文件,它可以用来存储你想保密或者隐藏的数据, 例如,它可以存储第三方服务的 API 密钥或者数据库 URI, 也可以使用它来存储配置选项, 通过设置配置选项,你可以改变应用程序的行为,而无需重写一些代码。
|
||||
|
||||
在应用程序中可以通过 `process.env.VAR_NAME` 访问到环境变量。 `process.env` 对象是 Node 程序中的一个全局对象,可以给这个变量传字符串。 习惯上,变量名全部大写,单词之间用下划线分隔。 `.env` 是一个 shell 文件,因此不需要用给变量名和值加引号。 还有一点需要注意,当你给变量赋值时等号两侧不能有空格,例如:`VAR_NAME=value`。 通常来讲,每一个变量定义会独占一行。
|
||||
|
||||
# --instructions--
|
||||
|
||||
添加一个环境变量作为配置选项。
|
||||
|
||||
在项目根目录创建一个 `.env` 文件,并存储变量 `MESSAGE_STYLE=uppercase`。
|
||||
|
||||
当向 `/json` 发 GET 请求时,如果 `process.env.MESSAGE_STYLE` 的值为 `uppercase`,那么上一次挑战中的路由处理程序返回的对象的消息则应该大写。 响应对象应该是 `{"message": "Hello json"}` or `{"message": "HELLO JSON"}`,取决于 `MESSAGE_STYLE` 的值。
|
||||
|
||||
**注意:**如果你正在使用 Replit,你无法创建一个 `.env` 文件。 相反,使用内置的 <dfn>SECRETS</dfn> 标签添加变量。
|
||||
|
||||
# --hints--
|
||||
|
||||
端口 `/json` 响应的值,应该随着环境变量 `MESSAGE_STYLE` 的变化而改变。
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/use-env-vars').then(
|
||||
(data) => {
|
||||
assert.isTrue(
|
||||
data.passed,
|
||||
'The response of "/json" does not change according to MESSAGE_STYLE'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```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.
|
||||
*/
|
||||
```
|
Reference in New Issue
Block a user