在上一节中我们完成了前端的初始化,下面来进行后端框架的搭建。
在这里我们选择的框架是基于nodejs的Express,简单灵活学习成本低。
express提供了路由和中间件的核心功能。路由是可以按照不同路径来调用不同的后端服务,中间件可以拦截请求进行各种校验。之前没接触过,在这顺手学一下。
初始化
首先创建一个工程目录,就命名为vueblog_backend。进入目录命令行输入初始化命令npm init
,就会出现一个json文件,然后就是引入框架npm install express
即可。
下面我们来创建一个简单的接口来测试一下。
js
app.js
// 引入express模块
const express = require('express')
// 创建express实例并设置端口号
const app = express()
const port = 3000
// 设置响应某个路由时执行的操作
// 这里是用get方法访问测试路由时向前端发送Hello World!
app.get('/test', (req, res) => {
res.send('Hello World!')
})
// 监听端口,也就是开启后端
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
在终端运行node app.js
,再在浏览器访问localhost:3000
测试get请求,就可以看到网页上显示helloworld! 但是如果代码有变化,每次都需要ctrl+c关闭服务器再重启,很不方便。
我们下载一个插件nodemon,就可以自动检测工作区的变化并自动重启了。执行npm install nodemon --save-dev
,再把package.json里的start命令改为"start": "nodemon app.js"
。
这样只要在终端执行npm run start
,就能得到自动更新的服务器和页面啦!
传参
前端和后端是如何传递参数的呢?其实就是通过一些请求,GET、POST、PUT之类的。前端向某个路由发出请求,传递req给后端,后端根据接口返回res给前端。
query传参
将请求参数放在URL中 传递的方式,请求参数是键值对,和URL用?分隔,多个参数间使用&分隔。GET 请求只能传query参数。
传入的参数会在req.query
中被拦截,因此可以获取相应的参数。比如下面的例子,访问localhost:3000/test?username=1
就可以在页面上看到被传入的参数1
js
app.get('/test',(req, res)=>{
res.send(`${req.query.username}`);
})
body传参
body传参是通过请求的主体部分 传递的数据,通常用于传递较大的数据,比如JSON和XML格式。常与POST或PUT请求一起用。
js
// 引入express模块
const express = require('express')
// 引入body-parser模块,用于处理请求体数据
const bodyParser = require('body-parser')
// 创建express实例并设置端口号
const app = express()
const port = 3000
// body-parser模块作为插件挂载到express实例上,有json就够用
app.use(bodyParser.json())
// 用post方法访问注册路由时的操作
app.post('/register', (req, res)=> {
const username = req.body.username
res.send(`注册成功,用户名为${username}`)
})
// 监听端口,也就是开启后端
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
使用postman来模拟post请求,输入请求地址,选择body,raw,json。发现可以正常响应。
路由
路由是个什么东西呢?说白了就是平时上网冲浪的网址域名后面的部分。比如https://www.bilibili.com/video/BVxxxxx
,video就是路由。上面我们写的test、register也是接口的路由。根据不同的url请求,服务器返回不同的内容。
如果想要访问多个有相似路径的url,比如user/register,user/login,就得这样写:
js
app.get('/user/register', (req, res) => {
res.send('注册接口')
})。
app.get('/user/login', (req, res) => {
res.send('登录接口')
})。
很啰嗦,也不好直观地看出接口的功能。那么我们可以使用路由分发(也是路由模块化的基础)。格式如下:
js
// 路由拆分
// 先注册路由
const user = express.Router()
app.use('/user', user)
// 再使用路由
user.get('/register', (req, res) => {
res.send('注册接口')
})
user.get('/login', (req, res) => {
res.send('登录接口')
})
路由模块化
如果我们要实现很多个功能,总不能把所有路由都写在app.js里吧,臃肿又不方便。
这时候就涉及到了路由模块化。新建一些js文件,把相关路由写在里面并导出,最后在app.js里引入就可以了
js
register.js
const express = require('express') // 引入express模块
const SHA512 = require('crypto-js/sha512') // 引入crypto-js模块,用于加密密码
var router = express.Router() // 创建子路由
// 挂载具体的路由
router.post('/register',(req, res)=>{
const username = req.body.username;
let password = req.body.password;
password = SHA512(password).toString();
res.send(username);
})
module.exports = router; // 导出路由
js
app.js
const useRegister = require('./register/register.js') // 引入子路由
app.use('/', useCreateDatabase) // 注册子路由,参数是路径前缀和子路由,前缀也可以不加
然后就可以用/register
访问接口了
即使如此,也还是要在app.js里写很多引入注册子路由的语句,有没有更简洁的办法呢?当然是有的!
我们可以把子路由的引入与注册写成一个合集,在app.js里引入这个合集就好了
js
index.js
// 引入数据库创建子路由
const useCreateDatabase = require('../createDatabase/createDatabase.js')
// 引入注册子路由
const useRegister = require('../register/register.js')
// 引入日志中间件
const useLogger = require('../middleware/logger.js')
module.exports = app => { // 导出函数
// 注册子路由,参数是路径前缀和子路由,前缀也可以不加
app.use('/', useCreateDatabase)
app.use(useRegister)
app.use(useLogger)
}
js
app.js
// 引入路由集合模块并调用函数
const routes = require('./routes')
routes(app);
现在我们的项目结构是这样的:
中间件
中间件其实就是自己定义的函数,在访问路由时执行。相比于路由,多了一个next参数,有全局中间件和路由中间件
全局中间件
顾名思义,用户发起任何请求都会执行的中间件。使用之前要使用use函数注册
js
// 全局中间件
// 定义全局中间件
function middleWare(req, res, next) {
console.log('这是全局中间件')
next()
}
// 注册全局中间件
app.use(middleWare)
// 定义路由,会自动使用全局中间件
app.get('/test1', (req, res) => { // 或者写成function(req, res) {}
res.send('test1')
})
可以看到,访问路由时自动执行了middleware的内容
路由中间件
顾名思义,路由中间件就是在某个特定路由中执行的中间件。需要在该路由的参数里插入这个中间件
js
// 路由中间件,在路由地址和路由处理函数之间定义中间件
app.get('/test2', function(req, res, next) {
console.log('这是路由中间件')
next()
}, (req, res) => {
res.send('test2')
})
由于我们之前注册了全局中间件,所以在执行这个路由的时候也会执行middleware
日志中间件
如果我们想打印每次操作的日志,应该怎么办呢?当然是使用全局中间件!不过这次就不写在app.js里了,写在单独的日志文件logger.js里(不要忘记导出)。
js
// 定义日志中间件
function logger(req, res, next) {
const time = new Date()
console.log(`[${time.toLocaleString()}] ${req.method} ${req.url}`)
next()
}
// 导出日志中间件
module.exports = logger
js
app.js
// 引入并注册日志中间件
const logger = require('./middleware/logger.js')
app.use(logger)
app.get('/test3', (req, res) => {
res.send('test3')
})
可以看到还是非常成功的
数据库
项目写好了需要部署,我在阿里云买了云服务器,操作系统选择的Debian,连接上宝塔面板,数据库选择的是MySQL,网上有很多部署相关教程。博主数据库学得一般,相关语句也是参考的网上教程
前期需要做的就是连接数据库并建表。
js
createDatabase.js
const express = require('express') // 引入express模块
const mysql = require('mysql') // 引入mysql模块
var router = express.Router() // 创建子路由
// 连接远程数据库
const connection = mysql.createConnection({
host: 'xxx.xxx.xxx.xxx', // 数据库的地址,我们这里是连接到服务器的数据库
user: 'root', // 用户名和密码,我们这里使用管理员用户
password: 'admin',
port: 3306, // 端口号,默认是3306
database: 'userdata' // 所连接的数据库名称,userdata数据库是之前在宝塔面板中创建好的
})
connection.connect(); // 连接数据库
// 建表
router.get('/createDatabase',(req, res)=>{
connection.query(`create table useraccount
(
username varchar(20),
password varchar(512)
)`,
(err,res)=>{
console.log(err);
});
})
module.exports = router; // 导出路由
再写一个注册路由,需要对用户名进行去重并对密码进行加密后加入数据库
js
const express = require('express') // 引入express模块
const SHA512 = require('crypto-js/sha512') // 引入crypto-js模块,用于加密密码
const mysql = require('mysql') // 引入mysql模块
var router = express.Router() // 创建子路由
// 连接远程数据库
const connection = mysql.createConnection({
host: 'xxx.xxx.xxx.xxx', // 数据库的地址,我们这里是连接到服务器的数据库
user: 'root', // 用户名和密码,我们这里使用管理员用户
password: 'admin',
port: 3306, // 端口号,默认是3306
database: 'userdata' // 所连接的数据库名称,userdata数据库是之前在宝塔面板中创建好的
})
connection.connect(); // 连接数据库
// 注册,加密去重
router.post('/register',(req, res)=>{
const username = req.body.username;
let password = req.body.password;
password = SHA512(password).toString();
const addSql=`insert into useraccount (username, password) values(?,?)`;
const selectSql = `select * from useraccount where username = '${username}'`;
connection.query(selectSql,(err,result)=>{
console.log(result);
if(result.length!==0){
res.status(400).send('用户名已存在');
}else{
connection.query(addSql,[username, password],(err)=>{
if(err){
res.status(500).send(`注册失败, ${err}`);
}else{
res.send('注册成功');
}
})
}
})
})
module.exports = router; // 导出路由
到此为止,我们的前后端初始化就完成了,但是博主的服务器只连接了数据库,还没部署nodejs,打算等完善好再打包上线