代码地址:https://github.com/changeclass/Node_Blog.git
# 接口设计
# 初始化路由
这一步实现上述接口。实现的只是一个简单的路由,并不会真的处理数据。即只实现路由的处理。
入口文件为 /bin/www.js
,即创建服务器时,传入一个核心路由(入口)的函数。
在这个核心路由中,负责汇集所有分路由(blog 相关、user 相关)。
const handleBlogRouter = require('./src/router/blog') | |
const handleUserRouter = require('./src/router/user') | |
const serverHandle = (req, res) => { | |
// 设置返回格式 JSON | |
res.setHeader('Content-type', 'application/json') | |
// 处理请求路径 | |
// 请求地址 | |
const url = req.url | |
// 请求路径 | |
req.path = url.split('?')[0] | |
// 处理 Blog 路由 | |
const blogData = handleBlogRouter(req, res) | |
if (blogData) { | |
res.end(JSON.stringify(blogData)) | |
return | |
} | |
// 处理 User 路由 | |
const userData = handleUserRouter(req, res) | |
if (userData) { | |
res.end(JSON.stringify(userData)) | |
return | |
} | |
// 未命中 返回 404 | |
res.writeHead(404, {'Content-type': 'text/plain'}) | |
res.write('404 Not Found\n') | |
res.end() | |
} | |
module.exports = serverHandle |
接下来只需要处理每个分路由即可。
大致逻辑如下,分路由几乎相同。
const handleBlogRouter = (req, res) => { | |
// 请求方式 | |
const method = req.method | |
// 获取博客列表 | |
if (method === 'GET' && req.path === '/api/blog/list') { | |
return { | |
msg: '这是获取博客列表的接口' | |
} | |
} | |
} | |
module.exports = handleBlogRouter |
# 规范返回格式
为了方便规范返回格式,创建一个模型类,用于创建返回信息。
class BaseModel { | |
constructor(data, message) { | |
if (typeof data === 'string') { | |
this.message = data | |
data = null | |
message = null | |
} | |
if (data) { | |
this.data = data | |
} | |
if (message) { | |
this.message = message | |
} | |
} | |
} | |
// 成功信息 | |
class SuccessModel extends BaseModel{ | |
constructor(data,message) { | |
super(data,message); | |
this.errno = 0 | |
} | |
} | |
// 失败信息 | |
class ErrorModel extends BaseModel{ | |
constructor(data,message) { | |
super(data,message); | |
this.errno = -1 | |
} | |
} | |
module.exports = { | |
SuccessModel,ErrorModel | |
} |
成功信息与失败信息继承于信息。信息包含的属性有 data 与 message。失败编号为 - 1,成功编号为 0。然后将其导出,即可创建对象后将其返回。
# 开发路由 —— 博客列表
返回博客列表应该在路由处获取,但为了降低耦合,将获取逻辑写入 controller
文件夹下的 blog。js
文件。于是 src 文件就变成了如图。
由于未连接数据库,因此返回数据也是假数据,但格式正确。
const getList = (author, keyword) => { | |
// 返回假数据,但格式正确 | |
return [ | |
{ | |
id: 1, | |
title: '标题A', | |
content: '内容A', | |
createTime: 1601799298039, | |
author: '张三' | |
}, | |
{ | |
id: 2, | |
title: '标题B', | |
content: '内容B', | |
createTime: 1601799329721, | |
author: '李四' | |
} | |
] | |
} | |
module.exports = { | |
getList | |
} |
至此,已经完成了获取数据的逻辑,并将其导出。只需要在路由处调用此方法即可获取到数据。
// 获取博客列表 | |
if (method === 'GET' && req.path === '/api/blog/list') { | |
const author = req.query.author || '' | |
const keyword = req.query.keyword || '' | |
const listData = getList(author,keyword) | |
return new SuccessModel(listData) | |
} |
# 开发路由 —— 博客详情
与上面的道理相似。
const getDetail = (id) => { | |
// 返回假数据 | |
return { | |
id: 1, | |
title: '标题A', | |
content: '内容A', | |
createTime: 1601799298039, | |
author: '张三' | |
} | |
} | |
module.exports = { | |
getList,getDetail | |
} |
接下来在路由处调用此方法即可获取到假数据。
# 获取 POST Data
由于 POST Data 的获取不是简单的逻辑,因此单独在 app.js
文件夹里分离一个函数用于获取。但是获取为异步获取,因此使用 Promise。
// 处理 post data | |
const getPostData = (req) => { | |
return new Promise(((resolve, reject) => { | |
if (req.method !== 'POST') { | |
resolve({}) | |
return | |
} | |
if (req.headers['Content-Type'] !== 'application/json') { | |
resolve({}) | |
return | |
} | |
let postData = '' | |
req.on('data',chunk=>{ | |
postData+=chunk.toString() | |
}) | |
req.on('end',()=>{ | |
if(!postData){ | |
resolve({}) | |
return | |
} | |
resolve(JSON.parse(postData)) | |
}) | |
})) | |
} |
只接受 JSON 格式的请求数据。接下来处理路由的逻辑应该全部放在处理 POST Data 之后。
getPostData(req).then(postData=>{ | |
req.body = postData | |
// 处理 Blog 路由 | |
const blogData = handleBlogRouter(req, res) | |
if (blogData) { | |
res.end(JSON.stringify(blogData)) | |
return | |
} | |
// 处理 User 路由 | |
const userData = handleUserRouter(req, res) | |
if (userData) { | |
res.end(JSON.stringify(userData)) | |
return | |
} | |
// 未命中 返回 404 | |
res.writeHead(404, {'Content-type': 'text/plain'}) | |
res.write('404 Not Found\n') | |
res.end() | |
}) |
# 开发路由 —— 新建和修改
新建和修改的路由也比较简单,同样的返回假数据。
// 新建博客 | |
const newBlog = (blogData = {}) => { | |
//blogData 是一个博客对象, | |
return { | |
id: 3 // 表示新建博客的数据表 ID | |
} | |
} | |
// 更新博客 | |
const updataBlog = (id, blogData = {}) => { | |
//id 为更新博客的 ID | |
//blogData 为更新的对象 | |
return true | |
} |
接下来只需要在路由逻辑中调用此方法获取数据即可
// 新建一篇博客 | |
if (method === 'POST' && req.path === '/api/blog/new') { | |
const data = newBlog(req.body) | |
return new SuccessModel(data) | |
} | |
// 更新一篇博客 | |
if (method === 'POST' && req.path === '/api/blog/update') { | |
const result = updataBlog(id,req.body) | |
if(result){ | |
return new SuccessModel(result) | |
}else{ | |
return new ErrorModel("更新失败") | |
} | |
} |
# 开发路由 —— 删除和登录
删除逻辑与新建逻辑相似。
const delBlog = (id)=>{ | |
//id 就是要删除博客的 ID | |
return true | |
} |
// 删除一篇博客 | |
if (method === 'POST' && req.path === '/api/blog/del') { | |
const result = delBlog(id) | |
if(result){ | |
return new SuccessModel(result) | |
}else{ | |
return new ErrorModel("删除失败") | |
} | |
} |
为了区分,user 的逻辑在新建一个 controller,用于编写处理 user 相关的逻辑。
const loginCheck = (username,password)=>{ | |
// 假数据 | |
if(username==='zhangsan'&&password==='123'){ | |
return true | |
} | |
} | |
module.exports = { | |
loginCheck | |
} |
接下来在 user 的路由中进行调用此方法。
const {loginCheck} = require('../controller/user') | |
const {SuccessModel, ErrorModel} = require('../model/resModel') | |
const handleUserRouter = (req, res) => { | |
// 请求方式 | |
const method = req.method | |
// 登陆 | |
if (method === 'POST' && req.path === '/api/user/login') { | |
const {username,password} = req.body | |
const result = loginCheck(username,password) | |
if(result){ | |
return new SuccessModel(result) | |
}else{ | |
return new ErrorModel("登陆失败") | |
} | |
} | |
} | |
module.exports = handleUserRouter |