0%

Node+Koa2+Mysql 搭建简易博客

目录结构

  • config 存放默认文件
  • lib 存放操作数据库文件
  • controller 逻辑控制文件
  • middlewares 存放判断登录与否文件
  • public 存放样式和头像文件
  • routes 存放路由文件
  • views 存放模板文件
  • index 程序主文件
  • package.json 包括项目名、作者、依赖等等

目录结构

首先配置config

我们新建default.js文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const config = {
// 启动端口
port: 3000,
// 数据库配置
database: {
DATABASE: 'nodesql',
USERNAME: 'root',
PASSWORD: '123456',
PORT: '3306',
HOST: 'localhost'
}
}

module.exports = config

这是我们所需的一些字段,包括端口和数据库连接所需,最后我们把它exports暴露出去,以便可以在别的地方使用

配置index.js文件

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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}`)

我们使用koa-mysql-session来进行数据库的操作, 使用koa-static配置静态资源,目录设置为public,使用ejs模板引擎,使用koa-bodyparser来解析提交的表单信息,使用koa-router做路由,使用koa-static-cache来缓存文件.

之前我们配置了default.js,我们就可以在这里使用了。

配置lib的mysql.js文件

首先我们建立了数据库的连接池,以便后面的操作都可以使用到。

我们创建了一个函数query,通过返回promise的方式以便可以方便用.then()来获取数据库返回的数据。

然后我们定义了三个表的字段,通过createTable来创建我们后面所需的三个表,包括posts(存储文章),users(存储用户),comment(存储评论),create table if not exists users()表示如果users表不存在则创建该表,避免每次重复建表报错的情况。

后面我们定义了一系列的方法,最后把他们exports暴露出去。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
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
}

下面是我们建的表:
数据库表

现在感觉有点枯燥,那我们先来实现一下注册吧

实现注册页面

routers/singup.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

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
26
27
28
29
30
31
32
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<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>
</body>
</html>

我们先安装supervisor

1
npm i supervisor -g

supervisor的作用是会监听文件的变化,而我们修改文件之后不必去重启程序

1
supervisor --harmony index

现在访问 localhost:3000/signup 看看效果吧。注意数据库一定要是开启的状态,不能关闭

完善注册功能

首先我们来完善一下样式吧,稍微美化一下
public/index.css

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
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

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<!DOCTYPE html>
<html lang="en">
<head>
<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>
</head>
<body>
<header>
<div class="user_name">
<% if(session.user){ %>
Hello,<%= session.user %>
<% } %>
<% if(!session.user){ %>
欢迎注册登录^_^
<% } %>
</div>
<div class="message">登录成功</div>
<div class="user_right">
<% if(session.user){ %>
<div class="has_user">
<a target="__blank" href="https://github.com/wclimb/Koa2-blog">GitHub</a>
<% if(type == 'all'){ %>
<a class="active" href="/posts">全部文章</a>
<% }else{ %>
<a href="/posts">全部文章</a>
<% }%>
<% if(type == 'my'){ %>
<a class="active" href="/posts?author=<%= session.user %>">我的文章</a>
<% }else{ %>
<a href="/posts?author=<%= session.user %>">我的文章</a>
<% }%>
<% if(type == 'create'){ %>
<a class="active" href="/create">发表文章</a>
<% }else{ %>
<a href="/create">发表文章</a>
<% }%>

<span class="signout">登出</span>
</div>
<% } %>
<% if(!session.user){ %>
<div class="none_user has_user">
<a target="__blank" href="https://github.com/wclimb/Koa2-blog">GitHub</a>
<% if(type == 'all'){ %>
<a class="active" href="/posts">全部文章</a>
<% }else{ %>
<a href="/posts">全部文章</a>
<% }%>
<% if(type == 'signup'){ %>
<a class="active" href="/signup">注册</a>
<% }else{ %>
<a href="/signup">注册</a>
<% }%>
<% if(type == 'signin'){ %>
<a class="active" href="/signin">登录</a>
<% }else{ %>
<a href="/signin">登录</a>
<% }%>
</div>
<% } %>
</div>
</header>

首先我们看到用到了session.user,这个值从哪来呢?请看下面的代码

1
2
3
4
5
6
7
// 注册页面
router.get('/signup', async(ctx, next) => {
await checkNotLogin(ctx)
await ctx.render('signup', {
session: ctx.session,
})
})

我们可以看到我们向模板传了一个session值,session:ctx.session,这个值存取的就是用户的信息,包括用户名、登录之后的id等,session一般是你关闭浏览器就过期了,等于下次打开浏览器的时候就得重新登录了,用if判断他存不存在,就可以知道用户是否需要登录,如果不存在用户,则只显示全部文章 注册 登录 ,如果session.user存在则有登出的按钮。

在上面我们会看到我用了另外一个if判断,判断type类型,这样做的目的是比如我们登录注册页面,注册页面的导航会高亮,其实就是添加了class:active;
之后我们每个ejs文件的头部会这样写<%- include("header",{type:'signup'}) %> 登录页面则是<%- include("header",{type:'signin'}) %>

修改views/signup.ejs

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<%- include("header",{type:'signup'}) %>
<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>
<% include footer %>

修改routers/signup.js

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 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位的
  • 使用我们之前说的bodyParse来解析提交的数据,通过ctx.request.body得到
  • 我们引入了数据库的操作 findDataByName和insertData,因为之前我们在/lib/mysql.js中已经把他们写好,并暴露出来了。意思是先从数据库里面查找注册的用户名,如果找到了证明该用户名已经被注册过了,如果没有找到则使用insertData增加到数据库中
  • ctx.body 是我们通过ajax提交之后给页面返回的数据,比如提交ajax成功之后msg.data=1的时候就代表用户存在,msg.data出现在后面的signup.ejs模板ajax请求中
  • 上传头像之前要新建好文件夹,我们ajax发送的是base64的格式,然后使用fs.writeFile来生成图片

模板引擎ejs

我们使用的是ejs,语法可以见ejs
之前我们写了这么一段代码:

1
2
3
4
5
router.get('/signup',async (ctx,next)=>{
await ctx.render('signup',{
session:ctx.session,
})
})

这里就用到了ejs所需的session 我们通过渲染signup.ejs模板,将值ctx.session赋值给session,之后我们就可以在signup.ejs中使用了
ejs的常用标签为:

  • <% code %>:运行 JavaScript 代码,不输出
  • <%= code %>:显示转义后的 HTML内容
  • <%- code %>:显示原始 HTML 内容

<%= code %><%- code %>的区别在于:
<%= code %>不管你写什么都会原样输出,比如code为 <strong>hello</strong>的时候 <%= code %> 会显示<strong>hello</strong>
而<%- code %>则显示hello.

完整源码及部署教程:https://github.com/growvv/Koa2-blog

参考链接: wclimb-Node+Koa2+Mysql 搭建简易博客