写这个系列的文章的初衷是想利用socket.io自己开发一个聊天室。聊天室要取到用户的头像和昵称,以及拉取历史聊天记录,就需要开发注册、登录、保存、获取聊天记录的功能。于是需要搭建一个后台服务,提供接口供前端调用,技术栈要用到Mysql和Express。
本系列文章非手把手类型,而是从设计者的角度,阐述项目中遇到的问题和选择,以及如何解决这些问题,为什么这么选:
一、后台接口项目是不是可以和Vue-Cli生成的前端项目合并成一个项目?如果可以,是把后端代码放到前端项目里,还是把前端代码放在后端项目里?
我们先来看第一种:
当我们用指令npm run dev把项目跑起来的时候,webpack会启动一个服务,并在本地的8080端口(默认)进行监听,这个时候,本地的静态资源(html、js、css、img等)会被打到这个地址,以便我们访问。
这个功能是在Vue-Cli项目的build/webpack.dev.conf.js文件中进行配置的。具体是在devWebpackConfig对象里的devServer选项:
devServer: {
clientLogLevel: 'warning',
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }
]
},
hot: true,
contentBase: false, // since we use CopyWebpackPlugin.
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll
}
}
既然webpack已经帮我们搭建了一个服务,我们也可以在这个服务里加上一个中间件,来给前端页面提供数据接口:
来自webpack官方文档
在devServer的before对象里,我们可以像上图webpack官方文档所描述的那样去moke数据,也可以用下面的方法连接数据库,从数据库中获取真实数据:
// webpack.dev.config.js
const express = require('express')
const mysql = require('mysql')
const app = express()
const bodyParser = require('body-parser')
const conn = mysql.createConnection({
host: '127.0.0.1',
user: 'root',
password: 'password',
database: 'test',
multipleStatements: true
})
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
……
const devWebpackConfig = merge(baseWebpackConfig, {
……
devServer: {
before (app) {
app.all('*', (req, res, next) => {
res.header('X-Powered-By', '3.2.1')
res.header('Cache-Contrl', 'no-store')
next()
})
app.disable('etag')
app.get(`/api/getUser`, (req, res) => {
const sqlStr = 'select * from user'
conn.query(sqlStr, (err, results) => {
if (err) {
return res.json({
err_code: 1,
message: '资料不存在',
affextedRows: 0
})
}
res.json({
err_code: 200,
message: results,
affextedRows: results.affextedRows
})
})
})
},
……
}
……
})
……
这样写的好处是解决了跨域的问题,但是后端的逻辑和webpack的配置混在一起,耦合紧密,不利于后期管理维护。
那么把前端代码放在后端的项目里又会是什么情况呢?
随便在网上搜了一下,确实有这样的项目模板,比如https://github.com/southerncross/vue-express-dev-boilerplate,他的目录结构如下:
.
├── LICENSE
├── README.md
├── index.js
├── nodemon.json
├── package.json
├── src
│ ├── client
│ │ ├── App.vue
│ │ ├── components
│ │ │ └── Hello.vue
│ │ └── index.js
│ └── server
│ ├── index.js
│ ├── public
│ │ └── favicon.ico
│ ├── router.js
│ └── views
│ ├── error.jade
│ └── index.jade
└── webpack.config.js
在webpack.config.js中,他将vue项目中的入口文件index.js打包生成的build.js放在了服务模块public静态文件夹的javascripts中:
module.exports = {
entry: path.join(__dirname, 'src/client/index.js'),
output: {
path: path.join(__dirname, 'src/server/public/javascripts/'),
publicPath: '/javascripts/',
filename: 'build.js'
}
……
}
再在src/server/views/index.jade中,引入build.js文件:
doctype html
html
head
title= title
body
#app
script(src='/javascripts/build.js')
这样做的好处是,前后端代码在同一个服务上,但逻辑分开写在两个文件夹里,不仅解决了请求接口的跨域问题,也便于后台的中间件对页面请求做一些统一的处理。
接下来我们再躯体考虑后台项目部分:
二、Express项目的结构怎么设计?
如下图所示,当前项目主要分为四层:db层存放数据库层面的逻辑,包括连接和mysql语句;model层存放操作数据库的方法;controller层处理接口的业务逻辑;routes层封装接口。
项目结构图
项目结构搭好了,我们来看具体的业务逻辑:
三、注册流程是怎样的?需要几个接口?
见下图:
注册流程
接口逻辑
接口层面的问题解决了,我们再来看数据库层面:
四、注册需要几张表?要保存哪些字段?
最开始设计的是两张表,user表和email表。email表用来保存用户注册时的昵称、密码、邮件地址、验证码、过期时间等,当他完成注册以后成为正式用户,再把这其中的昵称、密码和邮箱地址保存在user表里。也就是说,user表里保存的是正式用户,email表里保存发送过邮件的用户:
user表
email表
但是这样的设计并不是最合理的,首先两张表有重合的字段,会造成数据冗余;其次当这些重合的字段有变化的时候,两张表都需要更新。于是调整表结构为一张表,即用户表,注册成功与否用一个字段status来表示:
合并后的user表
然鹅,当数据库小白的我满怀信心带着表结构跑去请教大牛时,被告知这样的结构还是不合理,因为verification_code、verification_code_expired_time这两个字段保存在用户表里,对用户来说没有意义,应该分离开来。
根据大牛的建议,最后的表结构如下:
新user表
新email表
以上从项目设计、项目结构、业务逻辑、接口和数据库的设计,由整体到细节作了阐释,下一篇将从继续细节入手,对项目的登录部分进行分析,尽请期待……
网友评论