Node+Koa2+Mysql 搭建简易博客
亲爱的编程学习爱好者,如果你点开了这篇文章,说明你对《Node+Koa2+Mysql 搭建简易博客》很感兴趣。本篇文章就来给大家详细解析一下,主要介绍一下MySQL、Node.js、javascript、koa.js,希望所有认真读完的童鞋们,都有实质性的提高。
Koa2-blog
2018-1-5 更新教程(新增上传头像、新增分页、样式改版、发布文章和评论支持markdown语法)
现在GitHub的代码结构有变现在GitHub的代码结构有变,接口名也有变动。
Node+Koa2+Mysql 搭建简易博客
预览地址
写在前面
本篇教程一方面是为了自己在学习的过程加深记忆,也是总结的过程。另一方面给对这方面不太了解的同学以借鉴经验。如发现问题还望指正,
如果你觉得这篇文章帮助到了你,那就赏脸给个star吧,https://github.com/wclimb/Koa...
下一篇可能是 Node + express + mongoose 或 zepto源码系列
感谢您的阅读^_^
ps:关于markdown代码缩进问题,看起来不太舒服,但复制到编辑器是正常的哟!
演示效果

开发环境
- nodejs
const Koa = require('koa'); const path = require('path') const bodyParser = require('koa-bodyparser'); const ejs = require('ejs'); const session = require('koa-session-minimal'); const MysqlStore = require('koa-mysql-session'); const config = require('./config/default.js'); const router=require('koa-router') const views = require('koa-views') // const koaStatic = require('koa-static') const staticCache = require('koa-static-cache') const app = new Koa() // session存储配置 const sessionMysqlConfig = { user: config.database.USERNAME, password: config.database.PASSWORD, database: config.database.DATABASE, host: config.database.HOST, } // 配置session中间件 app.use(session({ key: 'USER_SID', store: new MysqlStore(sessionMysqlConfig) })) // 配置静态资源加载中间件 // app.use(koaStatic( // path.join(__dirname , './public') // )) // 缓存 app.use(staticCache(path.join(__dirname, './public'), { dynamic: true }, { maxAge: 365 * 24 * 60 * 60 })) app.use(staticCache(path.join(__dirname, './images'), { dynamic: true }, { maxAge: 365 * 24 * 60 * 60 })) // 配置服务端模板渲染引擎中间件 app.use(views(path.join(__dirname, './views'), { extension: 'ejs' })) app.use(bodyParser({ formLimit: '1mb' })) // 路由(我们先注释三个,等后面添加好了再取消注释,因为我们还没有定义路由,稍后会先实现注册) //app.use(require('./routers/signin.js').routes()) app.use(require('./routers/signup.js').routes()) //app.use(require('./routers/posts.js').routes()) //app.use(require('./routers/signout.js').routes()) app.listen(3000) console.log(`listening on port ${config.port}`)
我们使用
// 注册用户 let insertData = function( value ) { let _sql = "insert into users set name=?,pass=?,avator=?,moment=?;" return query( _sql, value ) }
我们写了一个_sql的sql语句,意思是插入到users的表中(在这之前我们已经建立了users表)然后要插入的数据分别是name、pass、avator、moment,就是用户名、密码、头像、注册时间,最后调用
var mysql = require('mysql'); var config = require('../config/default.js') var pool = mysql.createPool({ host : config.database.HOST, user : config.database.USERNAME, password : config.database.PASSWORD, database : config.database.DATABASE }); let query = function( sql, values ) { return new Promise(( resolve, reject ) => { pool.getConnection(function(err, connection) { if (err) { reject( err ) } else { connection.query(sql, values, ( err, rows) => { if ( err ) { reject( err ) } else { resolve( rows ) } connection.release() }) } }) }) } // let query = function( sql, values ) { // pool.getConnection(function(err, connection) { // // 使用连接 // connection.query( sql,values, function(err, rows) { // // 使用连接执行查询 // console.log(rows) // connection.release(); // //连接不再使用,返回到连接池 // }); // }); // } let users = `create table if not exists users( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, pass VARCHAR(100) NOT NULL, avator VARCHAR(100) NOT NULL, moment VARCHAR(100) NOT NULL, PRIMARY KEY ( id ) );` let posts = `create table if not exists posts( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, title TEXT(0) NOT NULL, content TEXT(0) NOT NULL, md TEXT(0) NOT NULL, uid VARCHAR(40) NOT NULL, moment VARCHAR(100) NOT NULL, comments VARCHAR(200) NOT NULL DEFAULT '0', pv VARCHAR(40) NOT NULL DEFAULT '0', avator VARCHAR(100) NOT NULL, PRIMARY KEY ( id ) );` let comment = `create table if not exists comment( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, content TEXT(0) NOT NULL, moment VARCHAR(40) NOT NULL, postid VARCHAR(40) NOT NULL, avator VARCHAR(100) NOT NULL, PRIMARY KEY ( id ) );` let createTable = function( sql ) { return query( sql, [] ) } // 建表 createTable(users) createTable(posts) createTable(comment) // 注册用户 let insertData = function( value ) { let _sql = "insert into users set name=?,pass=?,avator=?,moment=?;" return query( _sql, value ) } // 删除用户 let deleteUserData = function( name ) { let _sql = `delete from users where name="${name}";` return query( _sql ) } // 查找用户 let findUserData = function( name ) { let _sql = `select * from users where name="${name}";` return query( _sql ) } // 发表文章 let insertPost = function( value ) { let _sql = "insert into posts set name=?,title=?,content=?,md=?,uid=?,moment=?,avator=?;" return query( _sql, value ) } // 更新文章评论数 let updatePostComment = function( value ) { let _sql = "update posts set comments=? where id=?" return query( _sql, value ) } // 更新浏览数 let updatePostPv = function( value ) { let _sql = "update posts set pv=? where id=?" return query( _sql, value ) } // 发表评论 let insertComment = function( value ) { let _sql = "insert into comment set name=?,content=?,moment=?,postid=?,avator=?;" return query( _sql, value ) } // 通过名字查找用户 let findDataByName = function ( name ) { let _sql = `select * from users where name="${name}";` return query( _sql) } // 通过文章的名字查找用户 let findDataByUser = function ( name ) { let _sql = `select * from posts where name="${name}";` return query( _sql) } // 通过文章id查找 let findDataById = function ( id ) { let _sql = `select * from posts where id="${id}";` return query( _sql) } // 通过评论id查找 let findCommentById = function ( id ) { let _sql = `select * FROM comment where postid="${id}";` return query( _sql) } // 查询所有文章 let findAllPost = function () { let _sql = ` select * FROM posts;` return query( _sql) } // 查询分页文章 let findPostByPage = function (page) { let _sql = ` select * FROM posts limit ${(page-1)*10},10;` return query( _sql) } // 查询个人分页文章 let findPostByUserPage = function (name,page) { let _sql = ` select * FROM posts where name="${name}" order by id desc limit ${(page-1)*10},10 ;` return query( _sql) } // 更新修改文章 let updatePost = function(values){ let _sql = `update posts set title=?,content=?,md=? where id=?` return query(_sql,values) } // 删除文章 let deletePost = function(id){ let _sql = `delete from posts where id = ${id}` return query(_sql) } // 删除评论 let deleteComment = function(id){ let _sql = `delete from comment where id=${id}` return query(_sql) } // 删除所有评论 let deleteAllPostComment = function(id){ let _sql = `delete from comment where postid=${id}` return query(_sql) } // 查找评论数 let findCommentLength = function(id){ let _sql = `select content from comment where postid in (select id from posts where id=${id})` return query(_sql) } // 滚动无限加载数据 let findPageById = function(page){ let _sql = `select * from posts limit ${(page-1)*5},5;` return query(_sql) } // 评论分页 let findCommentByPage = function(page,postId){ let _sql = `select * from comment where postid=${postId} order by id desc limit ${(page-1)*10},10;` return query(_sql) } module.exports = { query, createTable, insertData, deleteUserData, findUserData, findDataByName, insertPost, findAllPost, findPostByPage, findPostByUserPage, findDataByUser, findDataById, insertComment, findCommentById, updatePost, deletePost, deleteComment, findCommentLength, updatePostComment, deleteAllPostComment, updatePostPv, findPageById, findCommentByPage }
下面是我们建的表
users posts comment id id id name name name pass title content avator content moment moment md postid - uid avator - moment - - comments - - pv - - avator - - id主键递增
- name: 用户名
- pass:密码
- avator:头像
- title:文章标题
- content:文章内容和评论
- md:markdown语法
- uid:发表文章的用户id
- moment:创建时间
- comments:文章评论数
- pv:文章浏览数
- postid:文章id
现在感觉有点枯燥,那我们先来实现一下注册吧
实现注册页面
routers/singup.js
const router = require('koa-router')(); const userModel = require('../lib/mysql.js'); const md5 = require('md5') const checkNotLogin = require('../middlewares/check.js').checkNotLogin const checkLogin = require('../middlewares/check.js').checkLogin const moment = require('moment'); const fs = require('fs') // 注册页面 router.get('/signup', async(ctx, next) => { await checkNotLogin(ctx) await ctx.render('signup', { session: ctx.session, }) }) module.exports = router
使用get方式得到'/signup'页面,然后渲染signup模板,这里我们还没有在写signup.ejs
views/signup.ejs
<meta charset="UTF-8"><title>注册</title><div class="container"> <form class="form create" method="post"> <div> <label>用户名:</label> <input placeholder="请输入用户名" type="text" name="name"></div> <div> <label>密码:</label> <input placeholder="请输入密码" class="password" type="password" name="password"></div> <div> <label>重复密码:</label> <input placeholder="请确认密码" class="repeatpass" type="password" name="repeatpass"></div> <div> <label>上传头像:</label> <input type="file" name="avator" id="avator"><input type="hidden" id="avatorVal"><img class="preview" alt="预览头像"></div> <div class="submit">注册</div> </form> </div>
我们先安装supervisor
body, header, ul, li, p, div, html, span, h3, a, blockquote { margin: 0; padding: 0; color: #333; } body { margin-bottom: 20px; } ul,li{ list-style-type: none; } a { text-decoration: none; } header { width: 60%; margin: 20px auto; } header:after{ content: ''; clear: both; display: table; } header .user_right{ float: right } header .user_right .active{ color: #5FB878; background: #fff; border: 1px solid #5FB878; box-shadow: 0 5px 5px #ccc; } header .user_name { float: left } .user_name { font-size: 20px; } .has_user a, .has_user span, .none_user a { padding: 5px 15px; background: #5FB878; border-radius: 15px; color: #fff; cursor: pointer; border: 1px solid #fff; transition: all 0.3s; } .has_user a:hover,.has_user span:hover{ color: #5FB878; background: #fff; border: 1px solid #5FB878; box-shadow: 0 5px 5px #ccc; } .posts{ border-radius: 4px; border: 1px solid #ddd; } .posts > li{ padding: 10px; position: relative; padding-bottom: 40px; } .posts .comment_pv{ position: absolute; bottom: 5px; right: 10px; } .posts .author{ position: absolute; left: 10px; bottom: 5px; } .posts .author span{ margin-right: 5px; } .posts > li:hover { background: #f2f2f2; } .posts > li:hover pre{ border: 1px solid #666; } .posts > li:hover .content{ border-top: 1px solid #fff; border-bottom: 1px solid #fff; } .posts > li + li{ border-top: 1px solid #ddd; } .posts li .title span{ position: absolute; left: 10px; top: 10px; color: #5FB878; font-size: 14px; } .posts li .title{ margin-left: 40px; font-size: 20px; color: #222; } .posts .userAvator{ position: absolute; left: 3px; top: 3px; width: 40px; height: 40px; border-radius: 5px; } .posts .content{ border-top: 1px solid #f2f2f2; border-bottom: 1px solid #f2f2f2; margin: 10px 0 0 0 ; padding: 10px 0; margin-left: 40px; } .userMsg img{ width: 40px; height: 40px; border-radius: 5px; margin-right: 10px; vertical-align: middle; display: inline-block; } .userMsg span{ font-size: 18px; color:#333; position: relative; top: 2px; } .posts li img{ max-width: 100%; } .spost .comment_pv{ position: absolute; top: 10px; } .spost .edit { position: absolute; right: 20px; bottom: 5px; } .spost .edit p { display: inline-block; margin-left: 10px; } .comment_wrap { width: 60%; margin: 20px auto; } .submit { display: block; width: 100px; height: 40px; line-height: 40px; text-align: center; border-radius: 4px; background: #5FB878; cursor: pointer; color: #fff; float: left; margin-top: 20px ; border:1px solid #fff; } .submit:hover{ background: #fff; color: #5FB878; border:1px solid #5FB878; } .comment_list{ border: 1px solid #ddd; border-radius: 4px; } .cmt_lists:hover{ background: #f2f2f2; } .cmt_lists + .cmt_lists{ border-top: 1px solid #ddd; } .cmt_content { padding: 10px; position: relative; border-radius: 4px; word-break: break-all; } .cmt_detail{ margin-left: 48px; } .cmt_content img{ max-width: 100%; } /* .cmt_content:after { content: '#content'; position: absolute; top: 5px; right: 5px; color: #aaa; font-size: 13px; } */ .cmt_name { position: absolute; right: 8px; bottom: 5px; color: #333; } .cmt_name a { margin-left: 5px; color: #1E9FFF; } .cmt_time{ position: absolute; font-size: 12px; right: 5px; top: 5px; color: #aaa } .form { margin: 0 auto; width: 50%; margin-top: 20px; } textarea { width: 100%; height: 150px; padding:10px 0 0 10px; font-size: 20px; border-radius: 4px; border: 1px solid #d7dde4; -webkit-appearance: none; resize: none; } textarea#spContent{ width: 98%; } .tips { margin: 20px 0; color: #ec5051; text-align: center; } .container { width: 60%; margin: 0 auto; } .form img.preview { width:100px; height:100px; border-radius: 50%; display: none; margin-top:10px; } input { display: block; width: 100%; height: 35px; font-size: 18px; padding: 6px 7px; border-radius: 4px; border: 1px solid #d7dde4; -webkit-appearance: none; } input:focus,textarea:focus{ outline: 0; box-shadow: 0 0 0 2px rgba(51,153,255,.2); border-color: #5cadff; } input:hover,input:active,textarea:hover,textarea:active{ border-color: #5cadff; } .create label { display: block; margin: 10px 0; } .comment_wrap form { width: 100%; margin-bottom: 85px; } .delete_comment, .delete_post { cursor: pointer; } .delete_comment:hover, .delete_post:hover, a:hover { color: #ec5051; } .disabled{ user-select: none; cursor: not-allowed !important; } .error{ color: #ec5051; } .success{ color: #1E9FFF; } .container{ width: 60%; margin:0 auto; } .message{ position: fixed; top: -100%; left: 50%; transform: translateX(-50%); padding: 10px 20px; background: rgba(0, 0, 0, 0.7); color: #fff; border-bottom-left-radius: 15px; border-bottom-right-radius: 15px; z-index: 99999; } .markdown pre{ display: block; overflow-x: auto; padding: 0.5em; background: #F0F0F0; border-radius: 3px; border: 1px solid #fff; } .markdown blockquote{ padding: 0 1em; color: #6a737d; border-left: 0.25em solid #dfe2e5; margin: 10px 0; } .markdown ul li{ list-style: circle; margin-top: 5px; }
我们再把模板引擎的header和footer独立出来
/views/header.ejs
顺便引入index.css和jq<meta charset="UTF-8"><title>koa2-blog</title><link rel="icon" href="http://www.wclimb.site/images/avatar.png"><link rel="stylesheet" href="/index.css"><script src="http://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script><script> function fade(txt){ $('.message').text(txt) $('.message').animate({ top:0 }) setTimeout(function(){ $('.message').animate({ top: '-100%' }) },1500) } $(function(){ $('.signout').click(()=>{ $.ajax({ url: "/signout", type: "GET", cache: false, dataType: 'json', success: function (msg) { if (msg) { fade('登出成功') setTimeout(()=>{ window.location.href = "/posts" },1500) } }, error: function () { alert('异常'); } }) }) }) </script><header><div class="user_name"> Hello, 欢迎注册登录^_^ </div> <div class="message">登录成功</div> <div class="user_right"> <div class="has_user"> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq5roGCUgXuytMyero5ko5XFfIfNhNCyr5q5aWO7ZKikx5CBpoNrcqCyzJehlnlw062tfs6B3LKkkrqBZK92gqC0jYqbfaN-a77Qs7GDhp6as3uG3oaVsbOFmIVhu6yKnrSKdW0' rel='nofollow'>GitHub</a> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaeSbIdoyNyzon2rhM--e4aUh6elaZK6m2C8dnmss2uCmn2jpGy0t86kgaybz7Guf9CGurFrkeCbbQ' rel='nofollow'>全部文章</a> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaeSbIdoyNyzon2rhM--e4aUh6elaZK6m2C8dnmss2uCmn2jpGy0t86kgaybz7Guf9CGurFrkeCbbQ' rel='nofollow'>全部文章</a> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaeSbIdoyN2foJiKidTGZ3qdgtDcaYffhWmshnmryGuOqpNrpa7HzJtpl2SN3q6IetuCtr6nnKqjmqurfZ6ua4qcfn2gr7Oqu22CeYjesYiVzZHdrW6HqptkvIZ9Z76zhaqKjY-is9CzbQ' rel='nofollow'>我的文章</a> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaeSbIdoyN2foJiKidTGZ3qdgtDcaYffhWmshnmryGuOqpNrpa7HzJtpl2SN3q6IetuCtr6nnKqjmqurfZ6ua4qcfn2gr7Oqu22CeYjesYiVzZHdrW6HqptkvIZ9Z76zhaqKjY-is9CzbQ' rel='nofollow'>我的文章</a> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycZqTpo-gyaq7on2rhM-tZoPQhqfLsIathWWweYGtso2cmYmzgm20p9FsjoaEmL2uft6St7qjhtN9ZQ' rel='nofollow'>发表文章</a> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycZqTpo-gyaq7on2rhM-tZoPQhqfLsIathWWweYGtso2cmYmzgm20p9FsjoaEmL2uft6St7qjhtN9ZQ' rel='nofollow'>发表文章</a> <span class="signout">登出</span> </div> <div class="none_user has_user"> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq5roGCUgXuytMyero5ko5XFfIfNhNCyr5q5aWO7ZKikx5CBpoNrcqCyzJehlnlw062tfs6B3LKkkrqBZK92gqC0jYqbfaN-a77Qs7GDhp6as3uG3oaVsbOFmIVhu6yKnrSKdW0' rel='nofollow'>GitHub</a> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaeSbIdoyNyzon2rhM--e4aUh6elaZK6m2C8dnmss2uCmn2jpGy0t86kgaybz7Guf9CGurFrkeCbbQ' rel='nofollow'>全部文章</a> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaeSbIdoyNyzon2rhM--e4aUh6elaZK6m2C8dnmss2uCmn2jpGy0t86kgaybz7Guf9CGurFrkeCbbQ' rel='nofollow'>全部文章</a> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaqRkJ2tybunon2rhM-tZoPQhqfLsIathWWweYGtso2cmYmzgm20p9FsjoaEmL2uft6St7qjhtN9ZQ' rel='nofollow'>注册</a> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaqRkJ2tybunon2rhM-tZoPQhqfLsIathWWweYGtso2cmYmzgm20p9FsjoaEmL2uft6St7qjhtN9ZQ' rel='nofollow'>注册</a> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaqRkJ2txrqaon2rhM-tZoPQhqfLsIathWWweYGtso2cmYmzgm20p9FsjoaEmL2uft6St7qjhtN9ZQ' rel='nofollow'>登录</a> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaqRkJ2txrqaon2rhM-tZoPQhqfLsIathWWweYGtso2cmYmzgm20p9FsjoaEmL2uft6St7qjhtN9ZQ' rel='nofollow'>登录</a> </div> </div> </header>
首先我们看到用到了session.user,这个值从哪来呢?请看下面的代码
// 注册页面 router.get('/signup', async(ctx, next) => { await checkNotLogin(ctx) await ctx.render('signup', { session: ctx.session, }) })
我们可以看到我们向模板传了一个session值,session:ctx.session,这个值存取的就是用户的信息,包括用户名、登录之后的id等,session一般是你关闭浏览器就过期了,等于下次打开浏览器的时候就得重新登录了,用if判断他存不存在,就可以知道用户是否需要登录,如果不存在用户,则只显示
修改views/signup.ejs
<div class="container"> <form class="form create" method="post"> <div> <label>用户名:</label> <input placeholder="请输入用户名" type="text" name="name"></div> <div> <label>密码:</label> <input placeholder="请输入密码" class="password" type="password" name="password"></div> <div> <label>重复密码:</label> <input placeholder="请确认密码" class="repeatpass" type="password" name="repeatpass"></div> <div> <label>上传头像:</label> <input type="file" name="avator" id="avator"><input type="hidden" id="avatorVal"><img class="preview" alt="预览头像"></div> <div class="submit">注册</div> </form> </div> <script> $(window).keyup(function (e) { //console.log(e.keyCode) if (e.keyCode == 13) { $('.submit').click() } }) $('#avator').change(function(){ if (this.files.length != 0) { var file = this.files[0], reader = new FileReader(); if (!reader) { this.value = ''; return; }; console.log(file.size) if (file.size >= 1024 * 1024 / 2) { fade("请上传小于512kb的图片!") return } reader.onload = function (e) { this.value = ''; $('form .preview').attr('src', e.target.result) $('form .preview').fadeIn() $('#avatorVal').val(e.target.result) }; reader.readAsDataURL(file); }; }) $('.submit').click(()=>{ // console.log($('.form').serialize()) if ($('input[name=name]').val().trim() == '') { fade('请输入用户名!') }else if($('input[name=name]').val().match(/[<'">]/g)){ fade('请输入合法字符!') }else if($('#avatorVal').val() == ''){ fade('请上传头像!') }else{ $.ajax({ url: "/signup", data: { name: $('input[name=name]').val(), password: $('input[name=password]').val(), repeatpass: $('input[name=repeatpass]').val(), avator: $('#avatorVal').val(), }, type: "POST", cache: false, dataType: 'json', success: function (msg) { if (msg.data == 1) { $('input').val('') fade('用户名存在') } else if (msg.data == 2){ fade('请输入重复的密码') } else if(msg.data == 3){ fade('注册成功') setTimeout(()=>{ window.location.href = "/signin" },1000) } }, error: function () { alert('异常'); } }) } }) </script>
先看我们请求的url地址,是'/signup',为什么是这个呢?我们看下面这段代码(后面有完整的)
router.post('/signup', async(ctx, next) => { //console.log(ctx.request.body) let user = { name: ctx.request.body.name, pass: ctx.request.body.password, repeatpass: ctx.request.body.repeatpass, avator: ctx.request.body.avator } .... }
我们的请求方式是post,地址是
const router = require('koa-router')(); const userModel = require('../lib/mysql.js'); const md5 = require('md5') const checkNotLogin = require('../middlewares/check.js').checkNotLogin const checkLogin = require('../middlewares/check.js').checkLogin const moment = require('moment'); const fs = require('fs') // 注册页面 router.get('/signup', async(ctx, next) => { await checkNotLogin(ctx) await ctx.render('signup', { session: ctx.session, }) }) // post 注册 router.post('/signup', async(ctx, next) => { //console.log(ctx.request.body) let user = { name: ctx.request.body.name, pass: ctx.request.body.password, repeatpass: ctx.request.body.repeatpass, avator: ctx.request.body.avator } await userModel.findDataByName(user.name) .then(async (result) => { console.log(result) if (result.length) { try { throw Error('用户已经存在') } catch (error) { //处理err console.log(error) } // 用户存在 ctx.body = { data: 1 };; } else if (user.pass !== user.repeatpass || user.pass === '') { ctx.body = { data: 2 }; } else { // ctx.session.user=ctx.request.body.name let base64Data = user.avator.replace(/^data:image\/\w+;base64,/, ""); let dataBuffer = new Buffer(base64Data, 'base64'); let getName = Number(Math.random().toString().substr(3)).toString(36) + Date.now() await fs.writeFile('./public/images/' + getName + '.png', dataBuffer, err => { if (err) throw err; console.log('头像上传成功') }); await userModel.insertData([user.name, md5(user.pass), getName, moment().format('YYYY-MM-DD HH:mm:ss')]) .then(res=>{ console.log('注册成功',res) //注册成功 ctx.body = { data: 3 }; }) } }) }) module.exports = router
- 我们使用md5实现密码加密,长度是32位的
- 使用我们之前说的
router.get('/signup',async (ctx,next)=>{ await ctx.render('signup',{ session:ctx.session, }) })
这里就用到了ejs所需的session 我们通过渲染signup.ejs模板,将值ctx.session赋值给session,之后我们就可以在signup.ejs中使用了
ejs的常用标签为:-
const router = require('koa-router')(); const userModel = require('../lib/mysql.js') const md5 = require('md5') const checkNotLogin = require('../middlewares/check.js').checkNotLogin const checkLogin = require('../middlewares/check.js').checkLogin router.get('/signin', async(ctx, next) => { await checkNotLogin(ctx) await ctx.render('signin', { session: ctx.session, }) }) module.exports=router
修改 /views/signin.ejs
<div class="container"> <form class="form create" method="post "> <div> <label>用户名:</label> <input placeholder="用户名" type="text" name="name"></div> <div> <label>密码:</label> <input placeholder="密码" type="password" name="password"></div> <div class="submit">登录</div> </form> </div>
修改 index.js 文件 把下面这段代码注释去掉,之前注释是因为我们没有写signin的路由,以免报错,后面还有文章页和登出页的路由,大家记住一下
app.use(require('./routers/signin.js').routes())
现在注册一下来看看效果吧
const router = require('koa-router')(); const userModel = require('../lib/mysql.js') const md5 = require('md5') const checkNotLogin = require('../middlewares/check.js').checkNotLogin const checkLogin = require('../middlewares/check.js').checkLogin router.get('/signin', async(ctx, next) => { await checkNotLogin(ctx) await ctx.render('signin', { session: ctx.session, }) }) router.post('/signin', async(ctx, next) => { console.log(ctx.request.body) let name = ctx.request.body.name; let pass = ctx.request.body.password; await userModel.findDataByName(name) .then(result => { let res = result if (name === res[0]['name'] && md5(pass) === res[0]['pass']) { ctx.body = true ctx.session.user = res[0]['name'] ctx.session.id = res[0]['id'] console.log('ctx.session.id', ctx.session.id) console.log('session', ctx.session) console.log('登录成功') }else{ ctx.body = false console.log('用户名或密码错误!') } }).catch(err => { console.log(err) }) }) module.exports = router
我们进行登录操作,判断登录的用户名和密码是否有误,使用md5加密
我们可以看到登录成功返回的结果是<div class="container"> <form class="form create" method="post "> <div> <label>用户名:</label> <input placeholder="用户名" type="text" name="name"></div> <div> <label>密码:</label> <input placeholder="密码" type="password" name="password"></div> <div class="submit">登录</div> </form> </div> <script> $(window).keyup(function(e){ //console.log(e.keyCode) if (e.keyCode == 13) { $('.submit').click() } }) $('.submit').click(()=>{ if ($('input[name=name]').val().trim() == '' || $('input[name=password]').val().trim() == '' ) { fade('请输入用户名或密码') }else{ console.log($('.form').serialize()) $.ajax({ url: "/signin", data: $('.form').serialize(), type: "POST", cache: false, dataType: 'json', success: function (msg) { if (!msg) { $('input').val('') fade('用户名或密码错误') } else{ fade('登录成功') setTimeout(()=>{ window.location.href = "/posts" },1500) } }, error: function () { alert('异常'); } }) } }) </script>
我们增加了ajax请求,在routers/signin.js里,我们设置如果登录失败就返回false,登录成功返回true
const router = require('koa-router')(); const userModel = require('../lib/mysql.js') const moment = require('moment') const checkNotLogin = require('../middlewares/check.js').checkNotLogin const checkLogin = require('../middlewares/check.js').checkLogin; const md = require('markdown-it')(); // 重置到文章页 router.get('/', async(ctx, next) => { ctx.redirect('/posts') }) // 文章页 router.get('/posts', async(ctx, next) => { let res, postsLength, name = decodeURIComponent(ctx.request.querystring.split('=')[1]); if (ctx.request.querystring) { console.log('ctx.request.querystring', name) await userModel.findDataByUser(name) .then(result => { postsLength = result.length }) await userModel.findPostByUserPage(name,1) .then(result => { res = result }) await ctx.render('selfPosts', { session: ctx.session, posts: res, postsPageLength:Math.ceil(postsLength / 10), }) } else { await userModel.findPostByPage(1) .then(result => { //console.log(result) res = result }) await userModel.findAllPost() .then(result=>{ postsLength = result.length }) await ctx.render('posts', { session: ctx.session, posts: res, postsLength: postsLength, postsPageLength: Math.ceil(postsLength / 10), }) } }) // 首页分页,每次输出10条 router.post('/posts/page', async(ctx, next) => { let page = ctx.request.body.page; await userModel.findPostByPage(page) .then(result=>{ //console.log(result) ctx.body = result }).catch(()=>{ ctx.body = 'error' }) }) // 个人文章分页,每次输出10条 router.post('/posts/self/page', async(ctx, next) => { let data = ctx.request.body await userModel.findPostByUserPage(data.name,data.page) .then(result=>{ //console.log(result) ctx.body = result }).catch(()=>{ ctx.body = 'error' }) }) module.exports = router
修改 index.js
app.use(require('./routers/posts.js').routes())的注释去掉
修改 views/posts.ejs
const router = require('koa-router')(); router.get('/signout', async(ctx, next) => { ctx.session = null; console.log('登出成功') ctx.body = true }) module.exports = router
把session设置为null即可
修改 index.js
app.use(require('./routers/posts.js').routes())的注释去掉,现在把注释的路由全部取消注释就对了
然后我们看看
// 发表文章页面 router.get('/create', async(ctx, next) => { await ctx.render('create', { session: ctx.session, }) }) // post 发表文章 router.post('/create', async(ctx, next) => { let title = ctx.request.body.title, content = ctx.request.body.content, id = ctx.session.id, name = ctx.session.user, time = moment().format('YYYY-MM-DD HH:mm:ss'), avator, // 现在使用markdown不需要单独转义 newContent = content.replace(/[']/g, (target) => { return { '': '>', "'": ''' }[target] }), newTitle = title.replace(/[']/g, (target) => { return { '': '>', "'": ''' }[target] }); //console.log([name, newTitle, content, id, time]) await userModel.findUserData(ctx.session.user) .then(res => { console.log(res[0]['avator']) avator = res[0]['avator'] }) await userModel.insertPost([name, newTitle, md.render(content), content, id, time,avator]) .then(() => { ctx.body = true }).catch(() => { ctx.body = false }) })
修改 views/create.ejs
create.ejs
<div class="container"> <form style="width:100%" method="post" class="form create"> <div> <label>标题:</label> <input placeholder="请输入标题" type="text" name="title"></div> <div> <label>内容:</label> <textarea placeholder="请输入内容" name="content" id="" cols="42" rows="10"></textarea></div> <div class="submit">发表</div> </form> </div> <script> $('.submit').click(()=>{ if ($('input[name=title]').val().trim() == '') { fade('请输入标题') }else if ($('textarea').val().trim() == '') { fade('请输入内容') }else{ $.ajax({ url: "/create", data: $('.form').serialize(), type: "POST", cache: false, dataType: 'json', success: function (msg) { if (msg) { fade('发表成功') setTimeout(()=>{ window.location.href="/posts" },1000) }else{ fade('发表失败') } }, error: function () { alert('异常'); } }) } }) </script>
现在看看能不能发表吧
即使我们发表了文章,但是当前我们的posts的页面没有显示,因为还没有获取到数据
我们可以看我们之前写的代码
if (ctx.request.querystring) { ... }else { await userModel.findPostByPage(1) .then(result => { //console.log(result) res = result }) await userModel.findAllPost() .then(result=>{ postsLength = result.length }) await ctx.render('posts', { session: ctx.session, posts: res, postsLength: postsLength, postsPageLength: Math.ceil(postsLength / 10), }) }
if前面这部分我们先不用管,后面会说。只需要看else后面的代码我们通过
<div class="container"> <ul class="posts"><li> <div class="author"> <span title="<%= res.name %>"><a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaeSbIdoyN2foJiKidTGZ3qdgtDcaYffhWmshnmryKaOqnymbqDHurukgZx40a6hlZSH3Lmyhal9mqurfZ6_gI1gf316aL-30WiOdoDdsmZ7zoXNz22Hupicr6yUnrKzhpx-kIZqvt3RdQ' rel='nofollow'>author: </a></span> <span>评论数:</span> <span>浏览量:</span> </div> <div class="comment_pv"> <span></span> </div> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaeSbIdoyNyepZZ6iJyuiGHQhc2mspK-faXDiYGgsqN5nHqmnWi03LOifauEz757hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Jtt' rel='nofollow'> <div class="title"> <img class="userAvator" src="images/<%=%20res.avator%20%>.png" alt="Node+Koa2+Mysql 搭建简易博客" ></div> <div class="content markdown"> </div> </a> </li> </ul><div style="margin-top: 30px" class="pagination" id="page"></div> </div> <script src="http://www.wclimb.site/pagination/pagination.js"></script><script> pagination({ selector: '#page', totalPage: <%= postsPageLength %>, currentPage: 1, prev: '上一页', next: '下一页', first: true, last: true, showTotalPage: true, count: 2//当前页前后显示的数量 },function(val){ // 当前页 $.ajax({ url: "posts/page", type: 'POST', data:{ page: val }, cache: false, success: function (msg) { console.log(msg) if (msg != 'error') { $('.posts').html(' ') $.each(msg,function(i,val){ //console.log(val.content) $('.posts').append( '<li>'+ '<div class=\"author\">'+ '<span title=\"'+ val.name +'\"><a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaeSbIdoyN2foJiKidTGZ3qdgtDcaYffhWmshnmryIBxqpSBhq3H0KysjoWM3rF3htGSlrVwgrp5p6urfZ6usoabio2KbLKnuKSDhonQsZ52l5HQsbKHupdlsXmFrrNrhap9a45pvt3AooODdKI' rel='nofollow'>author: </a></span> <span>评论数:</span> <span>浏览量:</span> </div> <div class="comment_pv"> <span></span> </div> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaeSbIdoyNyepZZ6iJyuiGHQhc2msJqZfmDFq2Wkv3yNqX18jqW_lrdvfauEz62tf8-St7VthaqCnLGGgp-yo31jiaaGsbS3zW2DeYzfsmZ-3oWVuWqR4IqasYNtcQ' rel='nofollow'> <div class="title"> <img class="userAvator" src="../images/<%=%20posts.avator%20%>.png" alt="Node+Koa2+Mysql 搭建简易博客" ></div> <div class="content markdown"> </div> </a> <div class="edit"> <p><a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ykqOUfayktba7sYF6edvHZ4Pegre6fIHffZqrq36fv42JZH19i6S0t7ijgZx8mL2hft2Ht8tuh62FqrBkfa6ya41hibOTorS0o3U' rel='nofollow'>编辑</a></p> <p><a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq5rqJiUpn-yvpawqJd6iJu-e4fXkru2pZCYfqbEiWGgx6eJn3qmsWi03LtxfoaA3Mehh96Ct7p8gd99mqurfp-_jYlkfX2LpLS3uKOBnHyYvaF-3Ye3y26HrYWqsGR9rrJrjWGJs5OitLSjdQ' rel='nofollow'> 删除</a> </span> </div> </div> </div> <p class="tips">还没有评论,赶快去评论吧!</p> <div style="margin-top: 30px" class="pagination" id="page"></div> </div> <script src="http://www.wclimb.site/pagination/pagination.js"></script><script> var userName = "<%- session.user %>" pagination({ selector: '#page', totalPage: <%= commentPageLenght %>, currentPage: 1, prev: '上一页', next: '下一页', first: true, last: true, showTotalPage:true, count: 2//当前页前面显示的数量 },function(val){ // 当前页 var _comment = '' $.ajax({ url: "<%= posts.id %>/commentPage", type: 'POST', data:{ page: val }, cache: false, success: function (msg) { //console.log(msg) _comment = '' if (msg != 'error') { $('.comment_list').html(' ') $.each(msg,function(i,val){ //console.log(val.content) _comment += '<div class=\"cmt_lists\"><div class=\"cmt_content\"><div class=\"userMsg\"><img src = \"../images/'+ val.avator +'.png\" alt="Node+Koa2+Mysql 搭建简易博客" ><span>'+ val.name +'<div class="cmt_detail">'+ val.content + '<span class=\"cmt_time\">'+ val.moment +'<span class=\"cmt_name\">'; if (val.name == userName) { _comment += '<a class=\"delete_comment\" href=\"javascript:delete_comment('+ val.id +');\"> 删除' } _comment += '' }) $('.comment_list').append(_comment) }else{ alert('分页不存在') } } }) }) // 删除文章 $('.delete_post').click(() => { $.ajax({ url: "<%= posts.id %>/remove", type: 'POST', cache: false, success: function (msg) { if (msg.data == 1) { fade('删除文章成功') setTimeout(() => { window.location.href = "/posts" }, 1000) } else if (msg.data == 2) { fade('删除文章失败'); setTimeout(() => { window.location.reload() }, 1000) } } }) }) // 评论 var isAllow = true $('.submit').click(function(){ if (!isAllow) return isAllow = false if ($('textarea').val().trim() == '') { fade('请输入评论!') }else{ $.ajax({ url: '/' + location.pathname.split('/')[2], data:$('.form').serialize(), type: "POST", cache: false, dataType: 'json', success: function (msg) { if (msg) { fade('发表留言成功') setTimeout(()=>{ isAllow = true window.location.reload() },1500) } }, error: function () { alert('异常'); } }) } }) // 删除评论 function delete_comment(id) { $.ajax({ url: "<%= posts.id %>/comment/" + id + "/remove", type: 'POST', cache: false, success: function (msg) { if (msg.data == 1) { fade('删除留言成功') setTimeout(() => { window.location.reload() }, 1000) } else if (msg.data == 2) { fade('删除留言失败'); setTimeout(() => { window.location.reload() }, 1500) } }, error: function () { alert('异常') } }) } </script>
现在点击单篇文章试试,进入单篇文章页面,但是编辑、删除、评论都还没有做,点击无效,我们先不做,先实现每个用户自己发表的文章列表,我们之前在 get '/posts' 里面说先忽略if (ctx.request.querystring) {}里面的代码,这里是做了一个处理,假如用户点击了某个用户,该用户发表了几篇文章,我们需要只显示该用户发表的文章,那么进入的url应该是 /posts?author=xxx ,这个处理在posts.ejs 就已经加上了,就在文章的左下角,作者:xxx就是一个链接。我们通过判断用户来查找文章,继而有了
if (ctx.request.querystring) { console.log('ctx.request.querystring', name) await userModel.findDataByUser(name) .then(result => { postsLength = result.length }) await userModel.findPostByUserPage(name,1) .then(result => { res = result }) await ctx.render('selfPosts', { session: ctx.session, posts: res, postsPageLength:Math.ceil(postsLength / 10), }) }
修改 selfPost.ejs
<div class="container"> <ul class="posts"><li> <div class="author"> <span title="<%= res.name %>"><a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaeSbIdoyN2foJiKidTGZ3qdgtDcaYffhWmshnmryKaOqnymbqDHurukgZx40a6hlZSH3Lmyhal9mqurfZ6_gI1gf316aL-30WiOdoDdsmZ7zoXNz22Hupicr6yUnrKzhpx-kIZqvt3RdQ' rel='nofollow'>author: </a></span> <span>评论数:</span> <span>浏览量:</span> </div> <div class="comment_pv"> <span></span> </div> <a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq6ycaeSbIdoyNyepZZ6iJyuiGHQhc2mspK-faXDiYGgsqN5nHqmnWi03LOifauEz757hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Jtt' rel='nofollow'> <div class="title"> <img class="userAvator" src="images/<%=%20res.avator%20%>.png" alt="Node+Koa2+Mysql 搭建简易博客" ></div> <div class="content markdown"> </div> </a> </li> </ul><div style="margin-top: 30px" class="pagination" id="page"></div> </div> <script src="http://www.wclimb.site/pagination/pagination.js"></script><script> pagination({ selector: '#page', totalPage: <%= postsPageLength %>, currentPage: 1, prev: '上一页', next: '下一页', first: true, last: true, showTotalPage: true, count: 2//当前页前后显示的数量 },function(val){ // 当前页 $.ajax({ url: "posts/self/page", type: 'POST', data:{ page: val, name: location.search.slice(1).split('=')[1] }, cache: false, success: function (msg) { //console.log(msg) if (msg != 'error') { $('.posts').html(' ') $.each(msg,function(i,val){ //console.log(val.content) $('.posts').append( '<li>'+ '<div class=\"author\">'+ '<span title=\"'+ val.name +'\"><a href=\"/posts?author='+ val.name +' \">author: '+ val.name +''+ '<span>评论数:'+ val.comments +''+ '<span>浏览量:'+ val.pv +''+ ''+ '<div class=\"comment_pv\">'+ '<span>'+ val.moment +''+ ''+ '<a href=\"/posts/'+ val.id +'\">'+ '<div class=\"title\">'+ '<img class="userAvator" src="images/' + val.avator + '.png" alt="Node+Koa2+Mysql 搭建简易博客" >' + val.title + ''+ '<div class=\"content\">'+ val.content + ''+ ''+ '</script>
'
)
})
}else{
alert('分页不存在')
}
}
})
})
编辑文章、删除文章、评论、删除评论
评论
修改routers/posts.js
在post.js 后面增加
// 发表评论 router.post('/:postId', async(ctx, next) => { let name = ctx.session.user, content = ctx.request.body.content, postId = ctx.params.postId, res_comments, time = moment().format('YYYY-MM-DD HH:mm:ss'), avator; await userModel.findUserData(ctx.session.user) .then(res => { console.log(res[0]['avator']) avator = res[0]['avator'] }) await userModel.insertComment([name, md.render(content),time, postId,avator]) await userModel.findDataById(postId) .then(result => { res_comments = parseInt(result[0]['comments']) res_comments += 1 }) await userModel.updatePostComment([res_comments, postId]) .then(() => { ctx.body = true }).catch(() => { ctx.body = false }) }) // 评论分页 router.post('/posts/:postId/commentPage', async function(ctx){ let postId = ctx.params.postId, page = ctx.request.body.page; await userModel.findCommentByPage(page,postId) .then(res=>{ ctx.body = res }).catch(()=>{ ctx.body = 'error' }) })
现在试试发表评论的功能吧,之所以这样简单,因为我们之前就在sPost.ejs做了好几个ajax的处理,删除文章和评论也是如此
评论我们也做了分页,所以后面会有一个评论的分页的接口,我们已经在sPost.ejs里面写好了ajax请求删除评论
修改routers/posts.js
继续在post.js 后面增加
// 删除评论 router.post('/posts/:postId/comment/:commentId/remove', async(ctx, next) => { let postId = ctx.params.postId, commentId = ctx.params.commentId, res_comments; await userModel.findDataById(postId) .then(result => { res_comments = parseInt(result[0]['comments']) //console.log('res', res_comments) res_comments -= 1 //console.log(res_comments) }) await userModel.updatePostComment([res_comments, postId]) await userModel.deleteComment(commentId) .then(() => { ctx.body = { data: 1 } }).catch(() => { ctx.body = { data: 2 } }) })
现在试试删除评论的功能吧
删除文章
只有自己发表的文字删除的文字才会显示出来,才能被删除,
修改routers/posts.js
继续在post.js 后面增加
// 删除单篇文章 router.post('/posts/:postId/remove', async(ctx, next) => { let postId = ctx.params.postId await userModel.deleteAllPostComment(postId) await userModel.deletePost(postId) .then(() => { ctx.body = { data: 1 } }).catch(() => { ctx.body = { data: 2 } }) })
现在试试删除文章的功能吧
编辑文章
修改routers/posts.js
继续在post.js 后面增加
// 编辑单篇文章页面 router.get('/posts/:postId/edit', async(ctx, next) => { let name = ctx.session.user, postId = ctx.params.postId, res; await userModel.findDataById(postId) .then(result => { res = result[0] //console.log('修改文章', res) }) await ctx.render('edit', { session: ctx.session, postsContent: res.md, postsTitle: res.title }) }) // post 编辑单篇文章 router.post('/posts/:postId/edit', async(ctx, next) => { let title = ctx.request.body.title, content = ctx.request.body.content, id = ctx.session.id, postId = ctx.params.postId, // 现在使用markdown不需要单独转义 newTitle = title.replace(/[']/g, (target) => { return { '': '>', "'": ''' }[target] }), newContent = content.replace(/[']/g, (target) => { return { '': '>', "'": ''' }[target] }); await userModel.updatePost([newTitle, md.render(content), content, postId]) .then(() => { ctx.body = true }).catch(() => { ctx.body = false }) })
修改views/edit.js
<div class="container"> <form style="width:100%" class="form create" method="post"> <div> <label>标题:</label> <input placeholder="标题" type="text" name="title" value="<%- postsTitle %>"></div> <div> <label>内容:</label> <textarea name="content" id="" cols="42" rows="10"></textarea></div> <div class="submit">修改</div> </form> </div> <script> $('.submit').click(()=>{ $.ajax({ url: '', data: $('.form').serialize(), type: "POST", cache: false, dataType: 'json', success: function (msg) { if (msg) { fade('修改成功') setTimeout(()=>{ window.location.href="/posts" },1000) } }, error: function () { alert('异常'); } }) }) </script>
现在试试编辑文字然后修改提交吧
结语
至此一个简单的blog就已经制作好了,其他扩展功能相信你已经会了吧!如果出现问题,还望积极提问哈,我会尽快处理的
所有的代码都在 https://github.com/wclimb/Koa... 里面,如果觉得不错就star一下吧。有问题可以提问哟
下一篇可能是 Node + express + mongoose 或 zepto源码系列
感谢您的阅读^_^到这里,我们也就讲完了《Node+Koa2+Mysql 搭建简易博客》的内容了。个人认为,基础知识的学习和巩固,是为了更好的将其运用到项目中,欢迎关注golang学习网公众号,带你了解更多关于mysql的知识点!
-

- 上一篇
- 【MySQL】Mysql误操作后使用binlog2sql快速回滚

- 下一篇
- 【踩坑记录】记一次MySQL主从复制延迟的坑
-
- 数据库 · MySQL | 1小时前 |
- MySQL多表连接优化技巧与实战策略
- 221浏览 收藏
-
- 数据库 · MySQL | 13小时前 |
- MySQL排序优化与性能提升技巧
- 153浏览 收藏
-
- 数据库 · MySQL | 14小时前 |
- MySQL中WHERE与HAVING的区别详解
- 340浏览 收藏
-
- 数据库 · MySQL | 20小时前 |
- MySQL排序优化与性能提升技巧
- 368浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- MySQL连接池配置与优化方法
- 297浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- MySQLGROUPBY使用技巧与常见问题
- 306浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- MySQL缓存优化技巧分享
- 392浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- MySQL安装到D盘教程及路径设置详解
- 279浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- MySQL缓存设置及查询作用解析
- 470浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- MySQLcount优化技巧及性能提升方法
- 371浏览 收藏
-
- 数据库 · MySQL | 1天前 |
- MySQLUPDATE替换字段值方法详解
- 292浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 511次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 498次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 千音漫语
- 千音漫语,北京熠声科技倾力打造的智能声音创作助手,提供AI配音、音视频翻译、语音识别、声音克隆等强大功能,助力有声书制作、视频创作、教育培训等领域,官网:https://qianyin123.com
- 95次使用
-
- MiniWork
- MiniWork是一款智能高效的AI工具平台,专为提升工作与学习效率而设计。整合文本处理、图像生成、营销策划及运营管理等多元AI工具,提供精准智能解决方案,让复杂工作简单高效。
- 89次使用
-
- NoCode
- NoCode (nocode.cn)是领先的无代码开发平台,通过拖放、AI对话等简单操作,助您快速创建各类应用、网站与管理系统。无需编程知识,轻松实现个人生活、商业经营、企业管理多场景需求,大幅降低开发门槛,高效低成本。
- 106次使用
-
- 达医智影
- 达医智影,阿里巴巴达摩院医疗AI创新力作。全球率先利用平扫CT实现“一扫多筛”,仅一次CT扫描即可高效识别多种癌症、急症及慢病,为疾病早期发现提供智能、精准的AI影像早筛解决方案。
- 98次使用
-
- 智慧芽Eureka
- 智慧芽Eureka,专为技术创新打造的AI Agent平台。深度理解专利、研发、生物医药、材料、科创等复杂场景,通过专家级AI Agent精准执行任务,智能化工作流解放70%生产力,让您专注核心创新。
- 98次使用
-
- golang MySQL实现对数据库表存储获取操作示例
- 2022-12-22 499浏览
-
- 搞一个自娱自乐的博客(二) 架构搭建
- 2023-02-16 244浏览
-
- B-Tree、B+Tree以及B-link Tree
- 2023-01-19 235浏览
-
- mysql面试题
- 2023-01-17 157浏览
-
- MySQL数据表简单查询
- 2023-01-10 101浏览