4-8 开发 博客详情路由
controller
-blog.js
中定义一个getDetail()
用于返回假数据:
1 | const getDetail = () => { |
router
-blog.js
中引入getDetail()
,修改“获取博客详情”返回的内容:
1 | //获取博客详情 |
将我们在发送get请求时的id获取并传入getDetail()
,得到的对象data
传入SucessModel()
中去新建一个对象,该对象是格式化后的数据对象,在app.js
中该对象会通过res.end()
显示在页面上:
nodejs读取文件的简单示例
读取a.json
文件内容
新建文件夹promise-test
,该文件夹下新建一个index.js
用于读取文件。新建一个**files
文件夹用于存放3个文件a.json
、b.json
、c.json
**。
a.json
:
1 | { |
index.js
:
1 | const fs = require("fs");// fs是node自带的模块,用于读取文件 |
运行index.js可以打印出a.json的数据:
通过回调函数的方式获取文件内容
修改index.js
获取a.json文件的内容:
- 创建
getFileContent()
- **回调函数(参数函数)
callback
**:获取文件内容,转换为JSON对象后通过参数传给回调函数callback
。 - 参数
fileName
:需要获取内容的文件名(a.json
)。
- **回调函数(参数函数)
- 调用
getFileContent()
时只需要传入需要获取的文件名,(回调函数接受获取到的数据对象作为参数)即可在回调函数中对获取到的内容进行操作。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26const fs = require("fs");// fs是node自带的模块,用于读取文件
const path = require("path");//path是node自带的模块,用于获取路径
// callback方式获取文件内容
function getFileContent(fileName, callback) {
//path的resolve()用于拼接路径,__dirname是node的全局变量,可直接使用,表示当前文件的目录
const fullFileName = path.resolve(__dirname, "files", fileName)
//通过fs.readFile()读取绝对路径的文件
fs.readFile(fullFileName, (err, data) => {
//fs.readFile()读取文件是异步的
if (err) {
// 如果报错就抛出
console.log(err);
return;
}
// 读取到的数据data默认二进制
// 没报错就将data转换为字符串再转换为JSON对象后传给回调函数callback
callback(JSON.parse(data.toString()));
})
}
// 测试
getFileContent("a.json", aData => {
console.log("a data", aData)
})
依次获取文件内容(回调地狱)
b.json
:
1 | { |
c.json
:
1 | { |
index.js:
1 | // 依次获取三个文件内容,回调地狱 |
使用Promise避免回调地狱
- 使用**
getFileContent()
只需要传入文件名fileName**,(避免回调地狱,不需要使用回调函数)返回的是一个Promise对象。 then()
返回的Promise对象可以通过我们的设置返回新的Promise对象。- then()中的参数函数(回调函数)获取到的参数是由调用它的Promise对象在决议为成功/失败时(即**
resolve()
/reject()
中作为参数)返回的数据**。
关于Promise和resolve()、reject():
- 在
getFileContent()
中我们返回的是一个Promise对象,这个对象中保存着一个异步操作的结果(成功/失败)。(异步操作放在Promise的参数函数中) - 当Promise的参数函数中保存的异步操作成功,则可以调用resolve()将获取到的数据通过参数传递给then()的第一个参数函数。
- 如果异步操作失败,则可以调用reject()将错误信息通过参数传递给then()的第二个参数函数/catch()的参数函数。
- 注意:调用resolve()/reject()都不会终止Promise的参数函数的执行(他们只改变Promise对象的状态并传递参数),所以为了防止resolve()或reject()后面的函数还继续执行,最好在resolve或reject前面加上return语句,这样函数就结束了,后面语句自然也就不会执行了(
return resolve();
)。
index.js
:
1 | const fs = require("fs");// fs是node自带的模块,用于读取文件 |
补充:使用async await
会更加简单,会在后面讲koa2框架时讲解。
4-9 处理post请求
之前我们处理的两个路由都是get请求的,现在我们来处理post请求的路由。
复习:post请求的简单示例
可参考笔记“开发博客项目之接口(1)”post请求的例子
注意:数据流获取数据的方式是异步的,所以我们可以使用Promise来获取数据
通过Promise来解析postData
- 回到**
blog-1
文件夹中,在app.js
中新建函数getPostData()
,在该函数中通过Promise来解析postData**。- 其中我们没有用到
reject()
是因为在这里我们并不把 get请求 或者 请求类型为JSON以外的类型的数据(比如form-data
类型)判定为错误的,只需要返回空数据就好。
- 其中我们没有用到
- 在
app.js
中的serverHandle()
中,我们会处理不同的路由,在处理路由前就需要使用getPostData()
解析PostData。解析成功以后的数据会作为参数传到then()
中,此时我们需要将所有路由的处理都放在then()
的回调函数中,让接下来在router
中处理路由时都能拿到PostData。
app.js
中添加函数getPostData()
用于解析PostData:
1 | const getPostData = (req) => { |
app.js
中使用函数getPostData()
获取PostData,原本req.body是空的,现在把获取到的postData放进去。
把所有 路由的处理 都放在then()
的回调函数中,让接下来在router
中处理路由时都能拿到PostData:
1 | const serverHandle = (req, res) => { |
4-10 开发 新建和更新博客路由
新建博客路由
- 在
controller-blog.js
中新建一个函数newBlog()
,先通过它返回假数据。 - 在
router-blog.js
中引入newBlog()
,开发新建博客的路由"/api/blog/new"
在controller-blog.js
中新建一个函数newBlog()
,先通过它返回假数据:
1 | // 兼容:(ES6)如果没有blogData就给一个空对象 |
在router-blog.js
中引入newBlog()
,将controller中处理好的数据经过SuccessModel格式化后显示在页面上:
1 | //新建一篇博客 |
启动项目,使用postman测试一下效果:
可以在控制台看到postData:
更新博客路由
- 和新建博客路由的思路类似,主要区别在更新博客我们还需要从get请求中获取一个id,以此判断更新哪一篇博客。
- 要更新就需要获取id,在“获取博客详情”时我们使用了id,现在“更新博客路由”也要使用id,那我们就可以把获取id的表达式提取到路由处理的外面。
controller-blog.js
中处理数据:
1 | // 更新一篇博客 |
router-blog.js
中获取数据并以正确格式显示在页面上:
1 | //更新一篇博客 |
通过postman测试updateBlog()返回true时:
控制台:
通过postman测试updateBlog()返回false时:
4-11 开发 删除博客路由和登录路由
删除博客路由
- 删除博客就很简单,只需要获取一个id即可。
- 在
controller-blog.js
中新建方法delBlog()
,根据传入的id删除博客。 - 在
router-blog.js
中引用delBlog()
,
controller-blog.js
:
1 | const delBlog = (id) => { |
router-blog.js
:
1 | //删除一篇博客 |
通过postman测试updateBlog()返回true时:
注意:我们虽然没有post数据,但是由于我们规定了method === "POST"
,所以必须通过postman进行测试。
登录路由
blog.js中的路由就写完了,接下来就要处理user.js相关的路由。
- 在
controller
文件夹中新建user.js
文件,创建函数loginCheck
用于处理登录路由的逻辑。 - 在
router-user.js
中,引入函数loginCheck
,在函数handleUserPouter
接收app.js
中传入的参数req
、res
,在该函数中处理登录路由,返回由SuccessModel
格式后的数据。
**controller-user.js
**:
1 | const loginCheck = (username, password) => { |
router-user.js
:
1 | const { loginCheck } = require("../controller/user"); |
通过postman测试登录数据正确时:
总结系统架构设计的四层
第一层: www.js [开启 Server]
第二层:app.js [通信设置层]
第三层:router文件夹 [路由相关业务逻辑层]
第四层:controller文件夹 [数据处理层]
- 一开始进入第一层,项目执行的是
bin
文件夹下的**www.js
,这里面只是createServer
的逻辑,端口连通**什么的,和我们的业务逻辑没有关系。 - 第二层:
app.js
,用来设置系统比较基础的功能(处理blog
、user
路由)或者定义一些公共参数(可以通过res/req传给路由,比如:获取path
、解析query
、异步获取PostData)还有设置返回类型(JSON),还是不涉及业务逻辑的处理。 - 第三层:router文件夹下的两个路由文件
blog.js
、user.js
,稍微涉及逻辑层,但只管路由。- 来了什么路由就分配什么数据,通过SuccessModel/ErrorModel 处理数据格式。
- 匹配到路由(接口)以后会去处理一些数据,然后(通过SuccessModel)会给你返回一个 正确的格式。至于这些数据是怎么去匹配的,怎么去筛选的,是正确的还是错误的他不管,他只管和路由(接口)有关的数据分配。
- 第四层:
controller.js
是最关心数据的层次,他没有res
、req
、path
、query
这些东西,只是对传入的数据进行计算处理再返回。(我们目前还没有计算,接下来会有)。至于数据返回后是怎么分配给路由的controller.js
是不管的。
一层层拆分是一个由最基础的http服务向逻辑层转变的过程。
我们一开始先在www.js
上创建服务器server并监听8000端口,这个服务器做的事情放到app.js
的serverHandle中。
然后在app.js
中将HTTP请求req和响应res传到路由组件(router
文件夹),并设置一些 公共参数 通过req/res传给路由组件以供使用。
在router
文件夹下的路由组件blog.js
、user.js
中,根据app.js
传过来的req、res匹配对应的接口并将从controller.js
中得到的数据进行一些格式的处理。最终返回显示在页面上。
在controller.js
中对获取到的数据进行8计算处理再返回8。