- 从0开始搭建,不使用任何框架
- 使用nodemon检测文件变化,自动重启node
- 使用cross-env设置环境变量,兼容mac linux 和window
# 开始搭建
blog-1
--- bin
---www.js
---app.js
---package.json
// package.json
"dev": "cross-env NODE_ENV=dev nodemon ./bin/www.js",
"prd": "cross-env NODE_ENV=production nodemon ./bin/www.js"
// www.js和server配置相关;app.js和业务相关
// app.js
const serverHandle = (req, res) => {
// 设置返回格式JSON
res.setHeader('Content-Type', 'application/json')
const resData = {
name: '双越100',
site: 'imooc',
evn: process.env.NODE_ENV,
}
res.end(JSON.stringify(resData))
}
module.exports = serverHandle
// www.js
const http = require('http');
const PORT = 8000;
const serverHandle = require('../app');
const server = http.createServer(serverHandle);
server.listen(PORT);
# 开发接口
- 初始化路由:根据之前的技术方案设计,做出路由
- 返回假数据:将路由和数据处理分离,以符合设计原则
# 代码演示
// src/router/blog.js
const hanldeBlogRouter = (req, res) => {
const method = req.method
// 获取博客列表
if (method === 'GET' && req.path === '/api/blog/list') {
return {
msg: '这是获取博客列表的接口',
}
}
// 获取博客详情
if (method === 'GET' && req.path === '/api/blog/detail') {
return {
msg: '这是获取博客详情的接口',
}
}
// 新建一篇博客
if (method === 'POST' && req.path === '/api/blog/new') {
return {
msg: '这是新建博客的接口',
}
}
// 更新一篇博客
if (method === 'POST' && req.path === '/api/blog/update') {
return {
msg: '这是更新博客的接口',
}
}
// 删除一篇博客
if (method === 'POST' && req.path === '/api/blog/del') {
return {
msg: '这是删除博客的接口',
}
}
}
module.exports = hanldeBlogRouter
// src/router/user.js
const handleUserRouter = (req, res) => {
const method = req.method
// 登录
if (method === 'POST' && req.path === '/api/user/login') {
return {
msg: '这是登录的接口',
}
}
}
module.exports = handleUserRouter
// app.js
const handleBlogRouter = require('./src/router/blog')
const handleUserRouter = require('./src/router/user')
const serverHandle = (req, res) => {
// 设置返回格式JSON
res.setHeader('Content-Type', 'application/json')
// const resData = {
// name: '双越100',
// site: 'imooc',
// evn: process.env.NODE_ENV,
// }
// res.end(JSON.stringify(resData))
// 处理 path
const url = req.url
req.path = url.split('?')[0]
console.log(req.path)
// 处理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 Fount\n')
res.end()
}
module.exports = serverHandle
# 博客列表路由
问题:直接req.query 获取到的是undefined;需要使用url组件
const http = require('http');
const url = require('url');
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
const query = parsedUrl.query;
// 获取特定参数的值
const name = query.name;
const age = query.age;
// 返回结果
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`Name: ${name}, Age: ${age}`);
});
server.listen(3000, () => {
console.log('Server is running on port 3000');
});
博客列表
// model/resModel 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, } // controller/blog.js const getList = (author, keyword) => { // 先返回假数据(格式是正确的) return [ { id: 1, title: '标题A', content: '内容A', createTime: 123123, author: '张三', }, { id: 1, title: '标题B', content: '内容B', createTime: 123123, author: '李四', }, ] } module.exports = { getList: getList, } // router/blog.js const { getList } = require('../controller/blog') const { SuccessModel, ErrorModel } = require('../model/resModel') const hanldeBlogRouter = (req, res) => { const method = req.method // 获取博客列表 if (method === 'GET' && req.path === '/api/blog/list') { // return { // msg: '这是获取博客列表的接口', // } const author = req.query.author || '' const keyword = req.query.keyword || '' const listData = getList(author, keyword) return new SuccessModel(listData) } // 获取博客详情 if (method === 'GET' && req.path === '/api/blog/detail') { return { msg: '这是获取博客详情的接口', } } // 新建一篇博客 if (method === 'POST' && req.path === '/api/blog/new') { return { msg: '这是新建博客的接口', } } // 更新一篇博客 if (method === 'POST' && req.path === '/api/blog/update') { return { msg: '这是更新博客的接口', } } // 删除一篇博客 if (method === 'POST' && req.path === '/api/blog/del') { return { msg: '这是删除博客的接口', } } } module.exports = hanldeBlogRouter博客详情
// controller/blog const getList = (author, keyword) => { // 先返回假数据(格式是正确的) return [ { id: 1, title: '标题A', content: '内容A', createTime: 123123, author: '张三', }, { id: 1, title: '标题B', content: '内容B', createTime: 123123, author: '李四', }, ] } const getDetail = (id) => { // 先返回假数据 return { id: 1, title: '标题A', content: '内容A', createTime: '123233', author: '张三', } } module.exports = { getList: getList, getDetail: getDetail, } // src/router/blog const { getList, getDetail } = require('../controller/blog') const { SuccessModel, ErrorModel } = require('../model/resModel') const hanldeBlogRouter = (req, res) => { const method = req.method // 获取博客列表 if (method === 'GET' && req.path === '/api/blog/list') { // return { // msg: '这是获取博客列表的接口', // } const author = req.query.author || '' const keyword = req.query.keyword || '' const listData = getList(author, keyword) return new SuccessModel(listData) } // 获取博客详情 if (method === 'GET' && req.path === '/api/blog/detail') { // return { // msg: '这是获取博客详情的接口', // } let id = req.query.id const data = getDetail(id) return new SuccessModel(data) } // 新建一篇博客 if (method === 'POST' && req.path === '/api/blog/new') { return { msg: '这是新建博客的接口', } } // 更新一篇博客 if (method === 'POST' && req.path === '/api/blog/update') { return { msg: '这是更新博客的接口', } } // 删除一篇博客 if (method === 'POST' && req.path === '/api/blog/del') { return { msg: '这是删除博客的接口', } } } module.exports = hanldeBlogRouter- 读取文件
const fs = require('fs') const path = require('path') // const fullFileName = path.resolve(__dirname, 'files', 'a.json') // fs.readFile(fullFileName, (err, data) => { // if (err) { // console.error(err) // return // } // console.log(data.toString()) // }) // callback 方式获取一个文件的内容 // function getFileContent(fileName, callback) { // const fullFileName = path.resolve(__dirname, 'files', fileName) // fs.readFile(fullFileName, (err, data) => { // if (err) { // console.error(err) // return // } // callback(JSON.parse(data.toString())) // // console.log(data.toString()) // }) // } // //测试 callback-hell(回调地狱) // getFileContent('a.json', (aData) => { // console.log('a data', aData) // getFileContent(aData.next, (bData) => { // console.log('b data', bData) // getFileContent(bData.next, (cData) => { // console.log('b data', cData) // }) // }) // }) // 用promise获取文件内容 function getFileContent(fileName) { return new Promise((resolve, reject) => { const fullFileName = path.resolve(__dirname, 'files', fileName) fs.readFile(fullFileName, (err, data) => { if (err) { reject(err) return } resolve(JSON.parse(data.toString())) }) }) } getFileContent('a.json').then((aData) => { console.log('a data', aData) return getFileContent(aData.next) }).then((bData) => { console.log('b data', bData) return getFileContent(bData.next) })处理 POSTData
// app.js const querystring = require('querystring') const handleBlogRouter = require('./src/router/blog') const handleUserRouter = require('./src/router/user') // 用于处理 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({}) } resolve(JSON.parse(postData)) }) }) } const serverHandle = (req, res) => { // 设置返回格式JSON res.setHeader('Content-Type', 'application/json') // const resData = { // name: '双越100', // site: 'imooc', // evn: process.env.NODE_ENV, // } // res.end(JSON.stringify(resData)) // 处理 path const url = req.url req.path = url.split('?')[0] // 解析 query req.query = querystring.parse(url.split('?')[1]) // 处理 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 Fount\n') res.end() }) } module.exports = serverHandle新建&更新路由
// controller/blog.js const getList = (author, keyword) => { // 先返回假数据(格式是正确的) return [ { id: 1, title: '标题A', content: '内容A', createTime: 123123, author: '张三', }, { id: 1, title: '标题B', content: '内容B', createTime: 123123, author: '李四', }, ] } const getDetail = (id) => { // 先返回假数据 return { id: 1, title: '标题A', content: '内容A', createTime: '123233', author: '张三', } } const newBlog = (blogData = {}) => { console.log('new blog', blogData) // blogData 是一个博客对象,包含title content属性 return { id: 3, // 表示新建博客,插入到数据表里的id } } const updateBlog = (id, blogData = {}) => { // id 就是要更新博客的id // blogData 是一个博客对象,包含title content属性 console.log('update blog', id, blogData) return false } module.exports = { getList: getList, getDetail: getDetail, newBlog: newBlog, updateBlog: updateBlog, } // src/router/blog.js const { getList, getDetail, newBlog, updateBlog, } = require('../controller/blog') const { SuccessModel, ErrorModel } = require('../model/resModel') const hanldeBlogRouter = (req, res) => { const method = req.method let id = req.query.id // 获取博客列表 if (method === 'GET' && req.path === '/api/blog/list') { // return { // msg: '这是获取博客列表的接口', // } const author = req.query.author || '' const keyword = req.query.keyword || '' const listData = getList(author, keyword) return new SuccessModel(listData) } // 获取博客详情 if (method === 'GET' && req.path === '/api/blog/detail') { // return { // msg: '这是获取博客详情的接口', // } const data = getDetail(id) return new SuccessModel(data) } // 新建一篇博客 if (method === 'POST' && req.path === '/api/blog/new') { const blogData = req.body const data = newBlog(req.body) return new SuccessModel(data) // return { // msg: '这是新建博客的接口', // } } // 更新一篇博客 if (method === 'POST' && req.path === '/api/blog/update') { const result = updateBlog(id, req.body) if (result) { return new SuccessModel() } else { return new ErrorModel('更新失败') } // return { // msg: '这是更新博客的接口', // } } // 删除一篇博客 if (method === 'POST' && req.path === '/api/blog/del') { return { msg: '这是删除博客的接口', } } } module.exports = hanldeBlogRouter删除博客路由和登录路由
// src/router/blog.js const { getList, getDetail, newBlog, updateBlog, delBlog, } = require('../controller/blog') const { SuccessModel, ErrorModel } = require('../model/resModel') const hanldeBlogRouter = (req, res) => { const method = req.method let id = req.query.id // 获取博客列表 if (method === 'GET' && req.path === '/api/blog/list') { // return { // msg: '这是获取博客列表的接口', // } const author = req.query.author || '' const keyword = req.query.keyword || '' const listData = getList(author, keyword) return new SuccessModel(listData) } // 获取博客详情 if (method === 'GET' && req.path === '/api/blog/detail') { // return { // msg: '这是获取博客详情的接口', // } const data = getDetail(id) return new SuccessModel(data) } // 新建一篇博客 if (method === 'POST' && req.path === '/api/blog/new') { const blogData = req.body const data = newBlog(req.body) return new SuccessModel(data) // return { // msg: '这是新建博客的接口', // } } // 更新一篇博客 if (method === 'POST' && req.path === '/api/blog/update') { const result = updateBlog(id, req.body) if (result) { return new SuccessModel() } else { return new ErrorModel('更新失败') } // return { // msg: '这是更新博客的接口', // } } // 删除一篇博客 if (method === 'POST' && req.path === '/api/blog/del') { const result = delBlog(id, req.body) if (result) { return new SuccessModel() } else { return new ErrorModel('删除博客失败') } } } module.exports = hanldeBlogRouter // src/controller/blog.js const getList = (author, keyword) => { // 先返回假数据(格式是正确的) return [ { id: 1, title: '标题A', content: '内容A', createTime: 123123, author: '张三', }, { id: 1, title: '标题B', content: '内容B', createTime: 123123, author: '李四', }, ] } const getDetail = (id) => { // 先返回假数据 return { id: 1, title: '标题A', content: '内容A', createTime: '123233', author: '张三', } } const newBlog = (blogData = {}) => { console.log('new blog', blogData) // blogData 是一个博客对象,包含title content属性 return { id: 3, // 表示新建博客,插入到数据表里的id } } const updateBlog = (id, blogData = {}) => { // id 就是要更新博客的id // blogData 是一个博客对象,包含title content属性 console.log('update blog', id, blogData) return false } const delBlog = (id) => { // id 就是要删除博客的id return true } module.exports = { getList: getList, getDetail: getDetail, newBlog: newBlog, updateBlog: updateBlog, delBlog, } // src/router/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 console.log(loginCheck) const result = loginCheck(username, password) if (result) { return new SuccessModel() } return new ErrorModel('登录失败') } } module.exports = handleUserRouter // src/controller/user.js const loginCheck = (username, password) => { // 先使用假数据 if (username === 'zhangsan' && password === '123') return true return false } module.exports = { loginCheck, }
# 总结
- nodejs处理http请求的常用技能,postman的使用
- nodejs开发博客项目的接口(未连接数据库,未使用登录)
- 理解为何要将router和controller分开
# 补充--路由和API
API:前端和后端、不同端(子系统)之间对接的一个术语
- url(路由),/api/blog/list 输入、输出
路由
- API的一部分
- 后端系统内部的一个定义