博客项目笔记(一) | 青训营笔记


theme: condensed-night-purple

highlight: a11y-dark

这是我参与「第五届青训营 」伴学笔记创作活动的第 11 天

将项目放到GitHub上

  1. 在GitHub上创建仓库
  2. 将项目克隆 git clone https://github.com/wjx1117/myblog.git
  3. 提交项目 git add . --> git commit -m '初始化' --> git push https://www.bilibili.com/video/BV15741177Eh/?p=148&spmidfrom=333.999.toprightbarwindowhistory.content.click&vd_source=55438e5075015e3541432477ef1c8c5d

项目所需

  • 雪花id SnowFlake
  • 数据库:MySQL
  • wang editor 富文本编辑器

服务端搭建 server

app.js

安装模块

multer:处理上传功能的中间介 sqlite3:数据库 方便服务端的移植 这里改为mysql2 uuid:很难重复的id生成,认为可以生成唯一的标志 例如token

雪花id SnowFlake

迄今为止最全面的分布式主键ID生成器 https://gitee.com/yitter/idgenerator?from=giteesearch

开放跨域请求

json:前后端交互 js // 中间介 app.use(express.json()) // json支持 // 跨域请求 app.use(function (req, res, next) { // 设置允许跨域的域名,*代表允许任意域名跨域 res.header("Access-Control-Allow-Origin", "*"); // 允许的header类型 res.header("Access-Control-Allow-Headers", "*"); // 跨域允许的请求方式 res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS"); if(req.method == "OPTIONS") res.sendStatus(200); // 让options尝试请求快速结束 else next(); });

报错:src refspec main does not match any 解决:https://blog.csdn.net/bjbz_cxy/article/details/113931821

连接数据库 SnowFlake生成ID

数据库 myblog

  • 表1:admin --- 后台管理员 , account --- 登陆账号
  • 表2:blog
  • 表3:category

代码:

```js // 连接数据库,方便之后操作数据库 const mysql = require('mysql2') const GenId = require("../utils/SnowFlake") const genid = new GenId({ WorkerId: 1 });

// 创建连接配置 let db = mysql.createConnection({ host : 'localhost', // ip地址 port : '3306', // 端口 默认3306 user: 'root', // 数据库账号密码 password : '123456', database : 'myblog' // 数据库名字 })

module.exports = {db, genid} ```

配置路由

js // 要在app.js里注册路由 app.use('/test', require('./routers/TestRouter')) router.get('/test',(req, res) => { db.query('select * from admin',[],(err, res)=> { console.log(res); }) res.send({ id : genid.NextId() }) })

测试

访问http://localhost:8080/test/test 测试是否成功配置路由以及连接数据库

使用Promise封装数据库操作

后续可能我们会对数据库进行好几层的操作,一层套一层,会造成回调地狱,这时候代码很难阅读 所以要进行promise封装,变成同步操作 ```js // 封装 DbUtils.js db.async = {}

// promise封装 db.async.query = (sql, params) => { return new Promise((resolve, reject) => { db.query(sql, params, (err, rows) => { resolve({err, rows}) }) }) } js // TestRouter.js router.get('/test', async (req, res) => { // 同步写法 // 把回调函数变成可异步执行 添加async let out = await db.async.query('select * from admin', [])

res.send({ id : genid.NextId(), out }) }) ```

管理员登陆和token生成

```js const {v4 : uuidv4} = require('uuid')

router.post('/login',async (req, res) => { let { account, password} = req.body let { err, rows} = await db.async.query('select * from admin where account=? and password=?', [account, password])

if(err == null && rows.length > 0){ let logintoken = uuidv4() let updatetokensql = 'UPDATE admin SET token=? where id=?' await db.async.query(updatetokensql,[logintoken, rows[0].id])

let admin_info = rows[0]
admin_info.token = login_token
admin_info.password = ''
// 返回给前端的数据不应包含password

res.send({
  code : 200,
  msg : '登陆成功',
  data : admin_info
})

}else { res.send({ code : 500, msg : '登陆失败' }) } }) ```

分类表增删改查

  • 添加接口 post('/add' insert into category (id, name) values (?, ?)
  • 列表接口 不写分页 因为分类数据量不大 get('/list' select * from category
  • 修改接口 put('/update' update category set name =? where id=?
  • 删除接口 delete('/delete' delete from category where id =?

    报错: Unexpected token n in JSON at position 6
    解决:JSON规定,JSON数据的key与value必须使用**双引号""**包裹,否则在转换过程中会导致错误
    
    报错:Unexpected string in JSON at position 36
    解决:category拼写错误  json写错

博客表增删改查

  • 添加博客
  • 修改博客
  • 删除博客
  • 查询博客

查询分页接口

前端传递的参数:

  • keyword 关键字 在标题或内容中包含关键字
  • category_id 分类编号 按照分类进行查询
  • age 页码
  • page_size 分页大小

分页:将数据按照pagesize分为几页,想要显示第几页

模糊查询 like

WHERE 子句中可以使用等号 = 来设定获取数据的条件。

使用LIKE子句代替等号 = ,获取含有字符的所有记录。

使用百分号 %字符来表示任意字符,类似于UNIX或正则表达式中的星号 *

如果没有使用百分号 %, LIKE 子句与等号 = 的效果是一样的。

代码

```js router.get('/search', async (req, res) => { let {keyword, categoryid, page, pagesize} = req.query

// 进行判断 前端不用必须传值 page = page == null ? 1 : page pagesize = pagesize == null ? 10 : pagesize keyword = keyword == null ? '' : keyword categoryid = categoryid == null ? 0 : categoryid

// 组装 where 条件 let where_sqls = [] let params = []

if(categoryid != 0) { wheresqls.push(" categoryid = ? ") params.push(categoryid) } if(keyword != '') {

// 这里的语句要加括号,否则之后拼接时会造成错误
// 语句前后最好加空格,隔开,以免之后拼接字符串出现问题
where_sqls.push(" (title like ? or content like ?) ")
params.push('%'+ keyword + '%')
params.push('%'+ keyword + '%')

}

// 组装 where 语句 let wheresqlstr = '' if(wheresqls.length > 0){ wheresqlstr = ' where ' + wheresqls.join(" and ") } // wheresqls里:[" categoryid = ? ", " (title like ? or content like ?) "] // 组装完: where category_id = ? and (title like ? or content like ?)

// 查分页数据 let searchsql = 'select * from blog' + wheresqlstr + " order by createtime desc limit ?,?" let searchparams = params.concat((page - 1) * pagesize, page_size) // 使用 LIMIT 属性来设定返回的记录数 limit通常用来实现分页 从几开始,取几个
// 如果是 第 2 页 每页 10 条数据 // 那么要查询 从数组的 (2 - 1) * 10 开始,取10个

// 查数据总数 // COUNT(expression) 返回查询的记录总数,expression 参数是一个字段或者 * 号 // 不用 as 取别名的话,数据名为 count() 也可以使用["count()"] 数组下标 let searchcountsql = "select count (*) as count from blog " + wheresqlstr let searchcountparams = params

let search = await db.async.query(searchsql, searchparams) let count = await db.async.query(searchcountsql, searchcountparams) ```

上传接口

使用 wang editor 富文本编辑器 , 编写博客时需要上传图片

在 app.js 中设置静态资源路径 app.use(express.static(path.join(__dirname, "public")))

fs.renameSync()方法用于将给定旧路径下的文件同步重命名为给定新路径。 如果目标文件已经存在,它将覆盖目标文件。 重命名的过程中,可以间接实现移动文件的效果

substring使用详解:https://blog.csdn.net/qq_31676483/article/details/116570993

process.cwd():程序运行的目录的路径

```js // 解析 for(let file of files) { // 获取文件后缀 aaa.jpg 找到最后一个. 进行裁剪jpg let fileext = file.originalname.substring(file.originalname.lastIndexOf(".") +1) // 随机文件名 let filename = genid.NextId() + "." + fileext // 修改文件名加移动文件 fs.renameSync( process.cwd() + "/public/upload/temp/" + file.filename, process.cwd() + "/public/upload/" + filename )

// 保存名字
ret_files.push("/upload/" + file_name)

} ```

token验证端口

一些接口应该 登录 后才能进行相关操作

解决:登陆后前端保存返回的token,之后将 token 添加到请求头中,

token 验证代码

```js let {token} = req.headers console.log(token);

let admintokensql = "select * from admin where token = ?" let adminres = await db.async.query(admintokensql, [token]) if(adminres.err != null || admin_res.rows.length == 0) { res.send({ code : 403, msg : '请先登录' }) return } ```

中间件验证登录

如果有很多接口都需要 token验证,此时一个个的去添加代码很麻烦,使用中间件更加方便 给需要登陆验证的接口的路径添加/_token js // all:所有接口 类似中间件 // 验证约定:ADMIN_TOKEN_PATH // 原先:category/add 添加token验证:category/_token/add // 只要路径中含有_token 都会进行token验证 const ADMIN_TOKEN_PATH = "/_token" app.all("*", async (req, res, next) => { if(req.path.indexOf(ADMIN_TOKEN_PATH) > -1) { // token 验证代码 } else { next() } })

猜你喜欢

转载自blog.csdn.net/weixin_50945128/article/details/129377824