express -e blog
cd blog
yarn install
npm start
localhost:3000
bootstrap、jquey 添加项目public中
<html>
<head>
<title>个人博客系统——注册登录页title>
<link rel='stylesheet' href='/stylesheets/style.css' />
<link rel="stylesheet" href="/bootstrap/css/bootstrap.css">
<script src="/javascripts/jquery.js">script>
<script src="/bootstrap/js/bootstrap.js">script>
head>
<body>
<img src="/images/a.png" alt="" class="bg_img">
<div class="registor wrap">
<div class="card">
<div class="card-header bg-primary text-white text-center">
注册页面
div>
<div class="card-body">
<form>
<div class="form-group row">
<label for="inputEmail3" class="col-sm-3 col-form-label">用户名label>
<div class="col-sm-9">
<input type="email" class="form-control" id="inputEmail3">
div>
div>
<div class="form-group row">
<label for="inputPassword3" class="col-sm-3 col-form-label">密 码label>
<div class="col-sm-9">
<input type="password" class="form-control" id="inputPassword3">
div>
div>
<div class="form-group row">
<label for="inputPassword3" class="col-sm-3 col-form-label">重复密码label>
<div class="col-sm-9">
<input type="password" class="form-control" id="inputPassword3">
div>
div>
<div class="form-group row">
<div class="col-sm-9 offset-sm-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="gridCheck1">
<label class="form-check-label" for="gridCheck1">
Example checkbox
label>
div>
div>
div>
<div class="form-group row">
<div class="col-sm-9 offset-sm-3">
<button type="submit" class="btn btn-primary btn-block">注 册button>
div>
div>
<div class="form-group row">
<div class="col-sm-9 offset-sm-3">
已有账号,立刻去 <a href="" class="a_to_login">登录a>
div>
div>
form>
div>
div>
div>
<div class="login wrap">
<div class="card">
<div class="card-header bg-primary text-white text-center">
登录页面
div>
<div class="card-body">
<form>
<div class="form-group row">
<label for="inputEmail3" class="col-sm-3 col-form-label">用户名label>
<div class="col-sm-9">
<input type="email" class="form-control" id="inputEmail3">
div>
div>
<div class="form-group row">
<label for="inputPassword3" class="col-sm-3 col-form-label">密 码label>
<div class="col-sm-9">
<input type="password" class="form-control" id="inputPassword3">
div>
div>
<div class="form-group row">
<div class="col-sm-9 offset-sm-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="gridCheck1">
<label class="form-check-label" for="gridCheck1">
Example checkbox
label>
div>
div>
div>
<div class="form-group row">
<div class="col-sm-9 offset-sm-3">
<button type="submit" class="btn btn-primary btn-block">登录button>
div>
div>
<div class="form-group row">
<div class="col-sm-9 offset-sm-3">
已有账号,立刻去 <a href="" class="a_to_reg">注册a>
div>
div>
form>
div>
div>
div>
body>
html>
<script>
//点击 a 切换注册、登录模块
$('.a_to_login').on('click',function(e){
//取消默认行为
e.preventDefault();
$('.registor').hide();
$('.login').show();
})
$('.a_to_reg').on('click',function(e){
//取消默认行为
e.preventDefault();
$('.login').hide();
$('.registor').show();
})
script>
//注册
$('.btn_reg').on('click',function(){
// 清空上次的错误提示
$('.tip').hide();
//获取各个input value
var form_reg = document.getElementById('form_reg');
// 实例化formData 讲form表单数据,进行序列化
var formData = new FormData(form_reg);
console.log(formData);
//get('key') 获取key值
console.log(formData.get('user'));
console.log(formData.get('pass'));
console.log(formData.get('repass'));
//在 提交数据之前,要进行本地验证
//1.验证用户名是否合法 (不允许是汉字,长度5-12)
// var reg=/[\u4e00-\u9fa5]/;
var reg = /^[0-9a-zA-Z_$]{5,12}$/g;
// var str = 'abc_好';
// console.log(reg.test(str));
if(!reg.test(formData.get('user'))){
$('.tip_user').show();
return;
}
//2.验证密码是否合法 纯数字,5-12
var pass_reg = /^\d{5,12}$/;
if(!pass_reg.test(formData.get('pass'))){
$('.tip_pass').show();
return;
}
//3. 重复密码验证
if(formData.get('pass')!=formData.get('repass')){
$('.tip_repass').show();
return;
}
$.ajax({
url:'/api/reg',
type:'post',
processData: false,//不对数据进行转换
contentType: false,
data:formData,
success:function(res){
console.log(res);
}
})
})
var express = require('express');
var router = express.Router();
var formidable = require('formidable');
/* 注册 */
router.post('/reg',function(req, res, next) {
// 创建formidable表单解析对象
var form = new formidable.IncomingForm();
// formidable对象对提交的数据进行解析
form.parse(req,function(err,filds,files){ //err错误对象,filds:普通表单提交的数据。files:提交的二进制文件
if(!err){
console.log(filds);
}
})
});
module.exports = router;
var mongoose = require('mongoose');
module.exports=mongoose.Schema({
user:String,
pass:String,
isAdmin:{type:Boolean,default:false}
},{timestamps:true})
备注:isAdmin字段,用来区分用户的身份。在这里,我们用false来表示普通用户身份,用true来表示管理员身份。所以,给isAdmin设置了默认值:false。
var mongoose = require('mongoose');
var user=require('../schema/user');
module.exports=mongoose.model('User',user)
/* 注册 */
router.post('/reg',function(req, res, next) {
// 创建formidable表单解析对象
var form = new formidable.IncomingForm();
// formidable对象对提交的数据进行解析
form.parse(req,function(err,filds,files){ //err错误对象,filds:普通表单提交的数据。files:提交的二进制文件
if(!err){
console.log(filds); // {user:xxx,pass:xxx,repass:xxx}
//注册逻辑
/*
第一步:先查询数据库,是否存在该用户
第二步:如果没有该用户,直接向前端响应json ,注册失败
第三部:没有该用户,写入数据库,向前端响应json,注册成功
*/
//查询第一条数据,
User.findOne({user:filds.user},function(err,data){
// 如果用该用户:data--->{user:xxx,pass:xxx,isAdmin:false} true
// 如果没用该用户 data-->null false
if(!err){
console.log(data);
if(data){ // 有该用户
res.json({code:0,msg:'该用户已存在'})
}else{ // 没有该用户
User.create({user:filds.user,pass:filds.pass},function(err){
if(!err){
res.json({code:1,msg:'注册成功'})
}
})
}
}
})
}
})
});
//登陆
$('.btn_login').on('click',function(){
$('.tip').hide();
//原生dom
var form = document.querySelector("#form_login");
var formData = new FormData(form);
console.log(formData.get('user'));
console.log(formData.get('pass'));
//本地数据验证
var user_reg = /^[0-9a-zA-Z_$]{5,12}$/g;
if(!user_reg.test(formData.get('user'))){
$('.tip_login_user').show();
return;
}
//密码验证
var pass_reg = /^\d{5,12}$/;
if(!pass_reg.test(formData.get('pass'))){
$('.tip_login_pass').show();
return;
}
//请求接口
$.ajax({
url:'/api/login',
type:'post',
data:formData,
processData:false,
contentType:false,
success:function(res){
console.log(res);
}
})
})
//登陆
router.post('/login',function(req,res,next){
//接受数据 formData
// 创建formidable 解析表单数据对象
var form = new formidable.IncomingForm();
form.parse(req,function(err,filds,files){// err:错误参数, filds:普通form表单数据, files:文件数据。
if(!err){
console.log(filds);
//登陆逻辑
/*
第一步:查询数据库,看数据库是否存在该用户。
第二步:如果不存在该用户,向前进响应jons,登陆失败。
第三步:存在该用户。
第四部:进一步判断,该用户提交的密码,和数据中的密码是否一致:
如果不一致:向前端响应josn,登陆失败。
如果一致:向前端响应josn,登陆成功。
*/
User.findOne({user:filds.user},function(err,data){
if(!err){
if(data){
if(data.pass==filds.pass){ //密码页一致
res.json({code:1,msg:'登陆成功'})
}else{
res.json({code:2,msg:'密码错误'})
}
}else{
res.json({code:0,msg:'该用户不存在'})
}
}
})
}
})
})
HTTP Cookie(也叫 Web Cookie或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。
HTTP是无状态的。每次发送的http请求,都是一次独立请求,跟上次没有任何关系。
用户登录—> 服务器验证登录----->登录成功----->给前端响应josn + 向前端响应cookie({user:xxx,isAdmin:false})------>请求首页(携带cookie)----->服务器端 劫持 cookie---->根据cookie的内容渲染不同页面。
//先响应cookie(设置相应头) ,再josn
// cookie ---> {userInfo:{user:xxx,isAdmin:false}}
// 参数一:属性名
//参数二:属性值
// 参数三:配置 path:'/a' 设置写入cookie的路径
//magAge:毫秒, 设置cookie的有效期。30天免登录 1000*60*60*24*30
res.cookie('userInfo',{user:data.user,isAdmin:data.isAdmin},{path:'/a/b',maxAge:1000*60*60*24*30})
res.json({code:1,msg:'登陆成功'})
if(res.code==1){
//跳转到首页。?, cookie
location.href='/'; // 携带cookie
}
router.get('/', function(req, res, next) {
//劫持cookie
console.log(req.cookies)
if(req.cookies.userInfo){ //用户刚才登录成功了
if(req.cookies.userInfo.isAdmin){ //管理员
res.render('admin/home.ejs')
}else{ //普通用户
res.render('main/home.ejs')
}
}else{ // 用户没有登录
res.render('index', { title: 'Express' });
}
});
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>管理员页面title>
<link rel="stylesheet" href="/bootstrap/css/bootstrap.css">
<script src="/javascripts/jquery.js">script>
<script src="/bootstrap/js/bootstrap.js">script>
head>
<body>
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
<a class="navbar-brand" href="/">返回首页a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon">span>
button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="/api/findUser">用户列表 <span class="sr-only">(current)span>a>
li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
分类
a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#">添加分类a>
<a class="dropdown-item" href="#">查看分类a>
div>
li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
内容
a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="#">添加内容a>
<a class="dropdown-item" href="#">查看内容a>
div>
li>
ul>
<a class="btn btn-outline-success my-2 my-sm-0 text-light" href="/api/out" type="button">退出a>
div>
nav>
<% if(tip==0){ %>
<% include ./one.ejs %>
<% }else if(tip==1){ %>
<% include ./userList.ejs %>
<% }else if(tip==2){ %>
<% include ./userEdit.ejs %>
<% } %>
body>
html>
//退出
router.get('/out',function(req,res,next){
//清除 cookie
res.clearCookie('userInfo');
res.redirect('/');
})
// 用户列表查询
router.get('/findUser',function(req,res,next){
//查询用户信息
User.find({},function(err,datas){
if(!err){
res.render('admin/home.ejs',{tip:1,userList:datas})
}
})
})
//用户删除
router.get('/del',function(req,res,next){
User.remove({_id:req.query._id},function(err){
if(!err){
//查询用户信息
User.find({},function(err,datas){
if(!err){
res.render('admin/home.ejs',{tip:1,userList:datas})
}
})
}
})
})
//用户修改
router.get('/edit',function(req,res,next){
User.findOne({_id:req.query._id},function(err,data){
res.render('admin/home.ejs',{tip:2,userOne:data})
})
})
//确认修改
router.post('/update',function(req,res,next){
console.log(req.body)
var aa=req.body.isAdmin==1?true:false;
User.update({_id:req.body._id},{$set:{user:req.body.user,pass:req.body.pass,isAdmin:aa}},function(err){
if(!err){
//查询用户信息
User.find({},function(err,datas){
if(!err){
res.render('admin/home.ejs',{tip:1,userList:datas})
}
})
}
})
})
<div class="jumbotron jumbotron-fluid">
<div class="container">
<h1 class="display-4">欢迎您超级管理员!h1>
<p class="lead">This is a modified jumbotron that occupies the entire horizontal space of its parent.p>
div>
div>
<div class="container mt-3">
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">编号th>
<th scope="col">用户名th>
<th scope="col">密码th>
<th scope="col">是否是管理员th>
<th scope="col">注册时间th>
<th scope='col'>操作th>
tr>
thead>
<tbody>
<% for(var index in userList){ %>
<tr>
<th scope="row">
<%= index*1+1 %>
th>
<td>
<%= userList[index].user %>
td>
<td>
<%= userList[index].pass %>
td>
<td>
<%= userList[index].isAdmin?"是":"否" %>
td>
<td>
<%= userList[index].createdAt?userList[index].createdAt.toLocaleString():'2020-12-12 12:12:20' %>
td>
<td>
<a class="btn btn-info btn-sm" href="/api/del?_id=<%= userList[index]._id %>">删除a>
<a class="btn btn-info btn-sm" href='/api/edit?_id=<%= userList[index]._id %>'>修改a>
td>
tr>
<% } %>
tbody>
table>
div>
<div class="container mt-4">
<form action="/api/update" method="post">
<input type="text" value='<%= userOne._id %>' name='_id' hidden>
<div class="form-group">
<label for="exampleInputEmail1">用户名label>
<input type="text" value='<%= userOne.user %>' name='user' class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
div>
<div class="form-group">
<label for="exampleInputPassword1">密码label>
<input type="text" name='pass' value='<%= userOne.pass %>' class="form-control" id="exampleInputPassword1">
div>
<div class="form-group">
<label for="exampleFormControlSelect1">设置身份label>
<select class="form-control" id="exampleFormControlSelect1" name="isAdmin">
<option value="1">管理员option>
<option value="0">普通用户option>
select>
div>
<button type="submit" class="btn btn-primary">确认修改button>
form>
div>
创建schema模型
var mongoose = require('mongoose');
module.exports=new mongoose.Schema({
fenlei:{type:String,required:true}
})
创建model模型
var mongoose = require('mongoose');
var fenleiSchema = require('../schema/fenlei');
module.exports=mongoose.model('Fenlei',fenleiSchema);
分类页面
<div class="container mt-3">
<% if(onOff){ %>
<div class="alert alert-primary m-5" role="alert">
操作成功!
div>
<% } %>
<form action="/api/addFenlei" method="post">
<div class="form-group row">
<label for="inputPassword" class="col-sm-1 offset-sm-2 col-form-label">分类名:label>
<div class="col-sm-9">
<input type="text" name="fenlei" class="form-control" id="inputPassword">
div>
div>
<div class="form-group row">
<div class="col-sm-9 offset-sm-3">
<button type="submit" class="btn btn-primary btn-block btn-md">确认添加button>
div>
div>
form>
div>
后端接口文件
// 展示 分类添加模块
router.get('/showFenlei',function(req,res,next){
res.render('admin/home.ejs',{tip:3,onOff:false})
})
// 添加分类
router.post('/addFenlei',function(req,res,next){
//?接收数据。写入数据。
//创建entity模型 (文档模型)
var entity = new Fenlei({fenlei:req.body.fenlei});
entity.save(function(err){
if(!err){
console.log('添加成功')
res.render('admin/home.ejs',{tip:3,onOff:4})
}
})
})
home.ejs中也要修改
<% if(tip==0){ %>
<% include ./one.ejs %>
<% }else if(tip==1){ %>
<% include ./userList.ejs %>
<% }else if(tip==2){ %>
<% include ./userEdit.ejs %>
<% }else if(tip==3){%>
<!-- 展示分类添加模块 -->
<% include ./fenlei.ejs %>
<% }else if(tip==4){%>
<% } %>
var mongoose = require('mongoose');
module.exports=new mongoose.Schema({
fenlei:{ //集合关联
// 类型
type:mongoose.Schema.Types.ObjectId,
// 引用
ref:'Fenlei'
},
title:{
type:String,
default:''
},
description:{
type:String,
default:''
},
content:{
type:String,
default:''
}
},{timestamps:true})
model模型
var mongoose = require('mongoose');
var contentSchema = require("../schema/content");
module.exports=mongoose.model('Content',contentSchema);
admin/home.ejs
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
内容
a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="/api/showAddContent">添加内容a>
<a class="dropdown-item" href="#">查看内容a>
div>
li>
-----------------------------------------
<% if(tip==0){ %>
<% include ./one.ejs %>
<% }else if(tip==1){ %>
<% include ./userList.ejs %>
<% }else if(tip==2){ %>
<% include ./userEdit.ejs %>
<% }else if(tip==3){%>
<% include ./fenlei.ejs %>
<% }else if(tip==4){%>
<% include ./findFenlei.ejs %>
<% }else if(tip==5){%>
<% include ./content.ejs %>
<% } %>
创建content.ejs
<div class="container">
<form action="/api/addContent" method="post">
<div class="form-group">
<label for="exampleFormControlSelect1">请选择分类label>
<select class="form-control" id="exampleFormControlSelect1" name="fenlei">
<% for(var index in fenlei){ %>
<option value="<%= fenlei[index]._id %>"><%= fenlei[index].fenlei %>option>
<% } %>
select>
div>
<div class="form-group">
<label for="title">标题label>
<input type="text" class="form-control" id="title" name="title">
div>
<div class="form-group">
<label for="description">描述label>
<input type="text" class="form-control" id="description" name='description'>
div>
<div class="form-group">
<label for="content">内容label>
<textarea id="" cols="30" rows="10" class="form-control" name="content">textarea>
div>
<button type="submit" class="btn btn-primary btn-block">提交button>
form>
div>
后端接口
/api/showAddContent
//添加内容页面
router.get('/showAddContent',function(req,res,next){
//
Fenlei.find({},function(err,datas){ //[{fenlei:html,_id:xxxx},{fenlei:node,_id:xxx},{}]
if(!err){
res.render('admin/home.ejs',{tip:5,fenlei:datas})
}
})
})
/api/addContent
//添加内容
router.post("/addContent",function(req,res,next){
console.log(req.body);
Content.create({fenlei:req.body.fenlei,title:req.body.title,description:req.body.description,content:req.body.content},function(err){
if(!err){
Fenlei.find({},function(err,datas){ //[{fenlei:html,_id:xxxx},{fenlei:node,_id:xxx},{}]
if(!err){
res.render('admin/home.ejs',{tip:5,fenlei:datas})
}
})
}
})
})
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<link rel="stylesheet" href="/bootstrap/css/bootstrap.css">
<link rel="stylesheet" href="/stylesheets/main.css">
head>
<body>
<img src="/images/1.jpg" class="top_img" alt="">
<h1 class="top_title">我是普通用户的首页h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="#">全部a>li>
<% for(var index in fenlei){ %>
<li class="breadcrumb-item"><a href="#"><%= fenlei[index].fenlei %>a>li>
<% } %>
<li class="breadcrumb-item active" aria-current="page">
<a href="/api/out">退出a>
li>
ol>
nav>
body>
html>
router.get('/', function(req, res, next) {
//劫持cookie
console.log(req.cookies)
if(req.cookies.userInfo){ //用户刚才登录成功了 {user:admin123,isAdmin:true}
if(req.cookies.userInfo.isAdmin){ //管理员
res.render('admin/home.ejs',{tip:0})
}else{ //普通用户
Fenlei.find({},function(err,datas){
if(!err){
res.render('main/home.ejs',{fenlei:datas})
}
})
}
}else{ // 用户没有登录
res.render('index', { title: 'Express' });
}
});
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/api/findcontent?type=all">全部a>li>
<% for(var index in fenlei){ %>
<li class="breadcrumb-item"><a href="/api/findcontent?type=<%= fenlei[index]._id %>"><%= fenlei[index].fenlei %>a>li>
<% } %>
<li class="breadcrumb-item active" aria-current="page">
<a href="/api/out">退出a>
li>
ol>
nav>
<% for(var index in content){ %>
<div style='border:1px solid black'>
<h1>标题:<%= content[index].title %>h1>
<p>内容:<%= content[index].content %>p>
div>
<% } %>
router.get('/', function(req, res, next) {
//劫持cookie
console.log(req.cookies)
if(req.cookies.userInfo){ //用户刚才登录成功了 {user:admin123,isAdmin:true}
if(req.cookies.userInfo.isAdmin){ //管理员
res.render('admin/home.ejs',{tip:0})
}else{ //普通用户
Fenlei.find({},function(err,datas){
if(!err){
Content.find({},function(err,docs){
if(!err){
res.render('main/home.ejs',{fenlei:datas,content:docs})
}
})
}
})
}
}else{ // 用户没有登录
res.render('index', { title: 'Express' });
}
});
//分类 内容查看
router.get('/findcontent',function(req,res,next){
//接受参数,再查下 all, 123456
console.log(req.query)
var query;
if(req.query.type=='all'){
query={}
}else{
query={fenlei:req.query.type}
}
Content.find(query,function(err,datas){
if(!err){
Fenlei.find({},function(err,docs){
if(!err){
res.render('main/home.ejs',{content:datas,fenlei:docs})
}
})
}
})
})