本文介绍使用Node.js和Express框架搭建后端,结合MySQL数据库实现用户登录与注册功能。重点解析异步查询中Promise的应用、Session会话状态管理以及中文乱码问题的解决,并展望了后续文章页渲染的技术选型。
项目背景与技术选型
为了完成数据库课程设计,后端计划采用Node.js进行搭建,前端则使用Vue开发一个博客系统(近期正在学习相关技术)。起初考虑不使用框架以夯实基础,但由于需要在三周内完成项目并应对多项考试,最终选择了Express框架,这显著降低了开发和学习成本。
数据库方面选择了较为规范的MySQL,其代码复杂度高于MongoDB。鉴于这是数据库课程作业,若使用MongoDB可能因未涉及主键、外键等设置而被判定不合格,因此必须使用关系型数据库。
后端服务初始化与入口配置
首先在主入口JS文件中引入处理登录注册业务的模块,并启动服务监听指定端口。
Express路由与静态资源配置
var express = require('express');
var user = require('./controll/user');
var app = new express();
app.use(express.static('public')); //静态资源的入口
app.use('/user',user); //这样写是为了以后如果改变了请求路径,也不用改逻辑js而是改这行的路径即可
var server = app.listen(3000)
前端交互与AJAX请求实现
前端HTML页面结构如下:
用户登录与注册前端逻辑
$("#signup").click(function(){
$.ajax({
url: "/user/signup",
type: 'post',
data: $("#formid").serialize(),
success: function(data) {
if(data.status == 99999) {
alert("3秒后跳转到首页");
setTimeout(function() {
location.href='http://'+window.location.host+'/html/index.html';
},3000)
}else {
alert('登录名或密码错误')
}
}
})
})
$("#register").click(function(){
$.ajax({
url: "/user/register",
type: 'post',
data: $("#formRegister").serialize(),
success: function(data) {
console.log(data);
if(data.status == 99999) {
alert("注册成功")
}else {
alert("登录名已经有人用")
}
}
})
})
后端核心逻辑与异步处理
后端user.js模块代码较多,此处不再逐一解释,若后续有空隙再补充说明。主要关注以下几个关键点:
MySQL连接封装与Session管理
var mysql = require('mysql');
var express = require('express');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var session = require('cookie-session');
function select(sql) {
var promise = new Promise(function(resolve,reject) {
var result = null;
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'root'
});
connection.connect();
connection.query("USE test");
connection.query(sql, function (err, results, fields) {
if (err) {
console.log("err");
reject(err);
}else {
console.log("yes");
if(results.length > 0) {
resolve({status: 99999});
}else {
resolve({status: 00000});
}
}
}
);
connection.end();
})
return promise;
}
var router = express.Router();
router.use(bodyParser.urlencoded({ extended: true }));
router.use(cookieParser())
router.use(session({
secret: 'blog'
}))
router.use(function timeLog(req,res,next) {
var _user = req.session.user;
if(_user) {
//router.locals.user = user;
}
next();
})
//登录
router.post('/signup',function(req, res) {
var _user = req.body.user;
var name = _user.name;
var password = _user.password;
select('SELECT * FROM name WHERE name = "'+ name + '" AND password = "' + password + '";').then(function(data) {
//session存user name和userid
req.session.user = name;
data.status > 50 ? data.url = '/html/index.html' : null;
res.json(data);
res.end();
req.redirect("/");
}).catch(function(err){})
})
//注册
router.post('/register',function(req, res) {
var _user = req.body.user;
var name = _user.name;
var password = _user.password;
console.log(name+" "+password)
select('SELECT * FROM name WHERE name = "'+ name + '";')
.then(function(data) {
if(data.status == 99999) {
console.log("已有此用户名")
res.json({status:00000});
res.end();
}else {
select('INSERT INTO name(name,password) VALUES ("'+name+'", "'+password+'");')
.then(function(data) {
res.json({status:99999});
res.end();
}).catch(function(err){})
}
}).catch(function(err){
})
});
//退出
router.get("/logout",function(req, res) {
delete req.session.user;
res.end();
})
//获取session
router.get("/session",function(req, res) {
console.log("user in session");
console.log(req.session.user);
res.json({user:req.session.user});
res.end();
})
module.exports = router;
代码很多,不一一解释了,如果以后有空再补回。先说几个点
- 数据库连接逻辑封装在select方法中。由于需要返回查询结果,必须使用return,但数据库查询是异步操作,直接在select函数末尾return无效。因此,return语句需置于查询完成的回调函数中,但回调函数内的return无法直接返回给select函数外部。解决方案是采用ES6的Promise机制,具体细节建议参考相关文档深入学习。
- 获取查询结果后,需明确登录与注册的逻辑分支,区分成功与失败的情况。在注册环节曾遇到误区:由于用户名不能重复,需先查询数据库,若存在则返回status=99999。起初对Promise理解不足,误以为查询失败会触发reject(err),实际上查询无结果时返回空数组[],仍执行resolve(),因此需通过返回的status值来区分业务状态。
- 关于保存登录状态,即Session机制,目前理解尚不深入。此步骤可置于router的中间件中执行,使得经过该router的请求均能执行相应逻辑。若存在req.session.user,则将当前locals.user进行赋值。
- 注册功能调试耗时较长,除对Promise理解不透彻外,还涉及MySQL不支持中文输入的问题。当时未设置utf8编码,导致报错并进入reject(err)分支。不仅需将MySQL语言设置为utf8,还需注意在Navicat新建属性时字符类型的设置,否则字符集冲突会导致插入语句失败。
后续功能规划与技术展望
最后,对项目的未来展望进行简要说明。
文章页面计划采用Jade模板引擎渲染整个HTML后返回。考虑到文章内容变动较少,且Vue更适合处理如评论等小组件,而文章主体多为静态内容,因此采用后端直接渲染返回的方式,避免额外的请求与渲染开销。
结合当前进度与能力,除文章的增删改查功能外,博客主要提供查看和评论文章的功能。对于Vue的具体应用场景尚不明确,可能因对vue-router等模块接触和理解不够深入。预计主要功能为实现一个简洁版的Markdown编辑器。
以上为本文的全部内容,希望对读者的学习有所帮助,也欢迎大家继续支持本站。