DAY4-新闻分类分页、后台登录业务


一、新闻分类

MongoDB中聚合的方法使用aggregate()。
因为新闻和分类之间是有关联关系的,一篇新闻属于一个分类,一个分类是存在多条新闻的。那么这个关系我们称之为一对多;(1个分类对多个新闻)需要使用的mongoose 里面的聚合操作
aggregate:http://www.mongoosejs.net/docs/api.htmL#aggregate_Aggregate

https://juejin.cn/post/6844904008537243661

/* 5. 新闻资源列表 */
router.get('/list', async (req, res) => {
    const infos=await newsModel.aggregate([{
       /*联表操作 新闻表里的cateId要和分类表的_id产生联系*/
        $lookup:
            {
                /*要关联的表category*/
                from: "category",
                /*自己表里和别的表产生关系的id*/
                localField: "cateId",
                /*关联表的id*/
                foreignField: "_id",
                /*查询到的关联信息的key值*/
                as: "cateInfo"
            }
    }]);
    console.log(infos);
    res.render('news-list', {title: '新闻资源的展示', infos,moment});
});

html

<td>{{@ $value.cateInfo[0].cateName }}</td>

在这里插入图片描述

二、分页操作

db.news.find({},{title:1}).limit(1);
limit(x) 获取指定x条数的信息
db.news.find({},{title:1}).skip(2).limit(1);
跳过两条数据取一条
skip(y) 跳过y条数据

router.get('/list', async (req, res) => {
    let{page=1,size=2}=req.query;
    page=parseInt(page);
    size=parseInt(size);
    //总记录数
    let count=await newsModel.count();
    //偏移量
    let offset=(page-1)*size;
    //有时候会出现小数,向上取整Math.ceil()
    let totalPage=Math.ceil(count/size);
    const infos=await newsModel.aggregate([
        {
            $skip:offset
        },
        {
            $limit: size
        },
        {
       /*联表操作 新闻表里的cateId要和分类表的_id产生联系*/
        $lookup:
            {
                /*要关联的表category*/
                from: "category",
                /*自己表里和别的表产生关系的id*/
                localField: "cateId",
                /*关联表的id*/
                foreignField: "_id",
                /*查询到的关联信息的key值*/
                as: "cateInfo"
            }
    }]);
    console.log(infos);
    res.render('news-list', {page,size,count,totalPage, title: '新闻资源的展示', infos,moment});
});

html

<div class="page">
	 <div>
         <a class="prev" href="">&lt;&lt;</a>
         <!-- 根据总的页数 totalPage ,生成页码链接-->
         <!-- 循环操作-->
         <!-- 如果遍历的 i 和 传递的 page 相等,则代表是当前页,当前页就要使用 span标签 -->
         <% for(let i = 1; i <= totalPage; i++ ){ %>
         	<% if( i == page ) {%>
               <span class="current"><%= page %></span>
           	<%}else{%>
         <a class="num" href="/admin/news/list?page=<%= i %>&size=<%= size %>"><%= i %></a>
          	 <%}%>
        <% } %>
		<a class="next" href="">&gt;&gt;</a>
	</div>
</div>

在这里插入图片描述
在这里插入图片描述

三、新闻详情

<a href="/admin/news/detail/{{@ $value._id}}" title="详情">
	<i class="layui-icon">&#xe6fc;</i>
</a>

定义查看详情操作的路由

/*9.新闻的详情 detail*/
router.get('/detail/:id', async (req, res) => {
    //接收一个id
    let newsId = req.params.id;
    console.log('newId', newsId);
   /*尝试把 字符串_id 转换为 MongoDB 里面 ObjectId 类型 */
    const info = await newsModel.aggregate([
        {
            $match: {_id: mongoose.Types.ObjectId(newsId)} // $match 代表根据条件查询
        },
        {
            $lookup:
                {
                    /*要关联的表 category*/
                    from: "category",
                    /*自己表里面和别的表产生关系的id*/
                    localField: "cateId",
                    /*关联表的id*/
                    foreignField: "_id",
                    /*查询到的关联信息的key值*/
                    as: "cateInfo"
                }
        }]);
    // res.json( info );
    res.render('news-detail', { title: '新闻资源的详情', info: info[0], moment});
});

在这里插入图片描述
在这里插入图片描述

四、后台登录

登录之后才能进入后台页面进行操作
在路由目录新建back.js作为后台登录路由

const express = require('express');
const router = express.Router(); // 路由器
/*后台登录*/
router.get('/login',(req,res)=>{
    res.render('login');
});
module.exports = router;

app.js将back路由引入

const backRouter = require('./routes/back.js');
app.use('/admin', backRouter);

http://localhost:3000/admin/login
在这里插入图片描述

一般管理员是没有注册的,会在最初始创建一张表初始化数据给到数据登录
在这里插入图片描述
登录表单

<form method="post" action="/admin/checklogin" enctype="application/x-www-form-urlencoded" class="layui-form" >

在back.js里新增checklogin路由

const express = require('express');
const router = express.Router(); // 路由器
/*引入admin模型*/
const adminModel=require('../models/admin.js');
/*后台登录*/
router.get('/login',(req,res)=>{
    res.render('login',{title: '后台登录'});
});
router.post('/checklogin',async(req,res)=>{
    /*接收用户输入的用户名和密码*/
    let{username,password}=req.body;
    /*用户身份校验*/
    /*用户身份的校验*/
    /*需要根据用户名去查询用户的信息;
    * 1.存在
    *1.1 校验密码是否正确
    * 正确:登录
    * 错误:未登录
    *2.不存在,直接提示用户信息不存在**/
    const userInfo=await adminModel.findOne({username});
    if(userInfo){
        //校验密码是否正确
        if(userInfo.password==password){
            //密码正确
            res.redirect('/admin/index');
        }else{
            res.redirect('back');
        }
    }else{
        //用户信息不存在
        res.redirect('back');
    }
    // res.json(userInfo);
});
module.exports = router;

定义表的模型admin
models/admin.js

const mongoose = require('../db/mongodb.js');
const Schema = mongoose.Schema({
    username: {
        type: String,
    },
    password: {
        type: String,
    },
}, {timestamps: true});
const Model = mongoose.model('Admin', Schema, 'admin');
module.exports = Model;

这样登录后输入用户名和密码就能跳转后台页面
在这里插入图片描述

一般数据不存储明文密码 ,一般都是加密之后的混淆密码,一般使用md5加密

npm install md5

1.通过 md5函数可以把一个字符串转换为一个32位的16进制字符串:78e731027d8fd50ed642340b7c9a63b3
2. md5是单向不可逆,只能有md5---->32位字符串,没办法反推回去。
3. 相同输入,输出是一样的
先创建了一个测试test.js

var md5=require('md5');
console.log(md5('admin'));

这里打印出来的密文我修改给数据库
在这里插入图片描述
然后修改back.js

const md5=require('md5');
//修改这里
if(userInfo.password==md5(password)){

再去尝试登录 这时候输入的密码还是明文密码
是可以登录的!

五、登录优化

虽然现在可以登录了,但是没有什么实质性的操作,我通过http地址还是可以随意访问后台页面,所以现在要进行修改,经过登录之后才能进入后台页面和操作后台页面。

但是http协议无状态。不知道多次会话之问是否有关系。该如何处理?专业术语:跨请求共享状态。
什么样的技术可以实现跨请求共亨状态?答: cookie或者session 或者token
cookie的原理:
1.第一次的请求的时候,服务器会生成标识信息;然后发生一个指令,返回给浏览器。然后浏览器会把这个生成的标识保存起来。
2.下一次请求的时候。浏览器会自发的携带这个生成标识到服务器。
里面生成一个共享状态
来标识用户是否登录。然后在其他的请求里面是否得到状态。

cookie-parser

npm install cookie-parser

在app.js里引入

const cookieParser = require('cookie-parser')
/*cookie处理*/
app.use(cookieParser())

routes/back.js修改

if(userInfo.password==md5(password)){
            //密码正确1.记录登录的标识,实现跨请求共享数据2.前往后台的首页
            req.cookie( 'isLogin', 1);//给浏览器发送设置cookie的指令
            req.cookie( 'username' , username);//给浏览器发送设置cookie的指令
            res.send('cookie ok');
            // res.redirect('/admin/index');
        }else{
            res.redirect('back');
        }

测试一下是否成功
在这里插入图片描述
在这里插入图片描述
routes/back.js修改

if(userInfo.password==md5(password)){
            //密码正确1.记录登录的标识,实现跨请求共享数据2.前往后台的首页
            req.cookie( 'isLogin', 1);//给浏览器发送设置cookie的指令
            req.cookie( 'username' , username);//给浏览器发送设置cookie的指令
            res.redirect('/admin/index');
        }else{
            res.redirect('back');
        }

index.js修改

router.get('/index', (req, res) => {
    console.log(req.cookies);
    if(req.cookies['isLogin']!=1){
    //    用户未登录
        res.redirect('/admin/login')
    }else {
        res.render('index');
    }
});

news.js修改

/* 在5.新闻资源列表里最开始增加这段判断 */
router.get('/list', async (req, res) => {
    
    if(req.cookies['isLogin']!=1){
    //    用户未登录
        res.redirect('/admin/login');
        return;
    }

这样登录成功才能进到后台,cookie清楚后进入后台之前需要先登录。

但是这样子也很麻烦,要在每个页面路由文件里都这样配置,想统一配置,怎么办?
–使用express中间件

express官网-指南-使用中间件

app.js

app.use(function (req, res, next) {
    console.log('Time:', Date.now(),req.url)
    //登录操作不需要做cookies验证
    if(req.url=='/admin/login/'){
        next();
        return;
    }
    //未登录时请求cookie肯定是没有的 cookie有的时候才做以下判断
    if(req.cookies && req.cookies['isLogin']!=1){
        //    用户未登录
        res.redirect('/admin/login')
    }else {
        next();
    }
    // next() //进行路由规则的匹配 req.url=== xxx
})

定义后台相关的外置路由 一般我们不在 app.use 直接使用中间件做验证。因为有些请求不一定要做验证,例如前台的请求 。所以我们在单个的路由的配置里面做中间件验证。

app.use('/admin', (req, res, next)=>{
    /* 我们在定义每一个路由关系的时候,还可以设置的中间件 */
    console.log('我们在定义每一个路由关系的时候,还可以设置的中间件', req.url);
    next();
    // res.send('index路由信息!');
} , indexRouter);

封装成一个函数,这样更方便使用。
新建目录middleware/verifyLogin.js

/*使用这个中间件函数去验证用户是否登录*/
function verifyLogin(req, res, next) {
    if (req.cookies && req.cookies['isLogin'] != 1) {
        res.redirect('/admin/login');
    } else {
        next();
    }
}
module.exports = {
    verifyLogin,
}

app.js

let verifyFn = verifyLoginMiddleware.verifyLogin;
/*下面的路由是不要做登录检测*/
app.use('/admin', commonRouter);
// 这里面是登录的业务代码,需做登录检测的
app.use('/admin', verifyFn, indexRouter);
app.use('/admin/website', verifyFn, websiteRouter);
app.use('/admin/news', verifyFn, newsRouter);
app.use('/admin', verifyFn, backRouter);

六、退出登录

/*后台登录退出*/
router.get('/logout',(req,res)=>{
    res.cookie('isLogin',0);
    res.cookie('username','');
    res.redirect('/admin/login');
});

在这里插入图片描述
在这里插入图片描述

七、代码优化

controller:控制器,主要是负责业务代码。为了后期的维护和新功能的增加,不建议把 routes下的文件写的过于臃肿。
新建controllers文件夹,里面的文件和路由的文件相关:
路由里的new.js在控制文件夹为newsControll.js

const newsModel = require('../models/news.js');
const  moment = require('moment');
/* 类名:文件名的大驼峰 */
class NewsController {
    /* 5. 新闻资源列表 */
    static getNewsList = async (req, res) => {
       /* MongoDB 提供了一个 limit(可以获取指定的条数) 和 skip(跳过多少条记录进行获取)
        * 利用这两个 api 可以完成分页功能。
        * 分页的原理:
        * 1. 当前页(page)
        * 2. 当前页显示的数量(size)
        * 3. 总的记录数(count)
        * 4. 总共的页数:总记录数 / 每页显示的数量(totalPage)*/
        /* http://localhost:3000/admin/news/list?page=1&size=2 */
        let {page = 1, size = 2} = req.query;
        // res.json( req.query);
        page = parseInt(page);
        size = parseInt(size);
        let count = await newsModel.count();
        let offset = (page - 1) * size;
        let totalPage = Math.ceil(count / size); // 小数 11条/2条 = 5.5 向上取整 5.3 = 6
        const infos = await newsModel.aggregate([
            {
                $skip: offset
            },
            {
                $limit: size
            },
            {
                /* 这个我们称之为联表操作。我们现在是新闻表(cateId)要和分类表(_id)产生联系*/
                $lookup:
                    {
                        /*要关联的表 category*/
                        from: "category",
                        /*自己表里面和别的表产生关系的id*/
                        localField: "cateId",
                        /*关联表的id*/
                        foreignField: "_id",
                        /*查询到的关联信息的key值*/
                        as: "cateInfo"
                    }
            }]);
        console.log(infos);
        console.log('page, size, count, totalPage', page, size, count, totalPage);
        res.render('news-list', {page, size, count, totalPage, title: '新闻资源的展示', infos, moment});
    }
}
module.exports = NewsController;

news.js对应修改

const  NewsController = require('../controllers/newsController.js');

/* 5. 新闻资源列表 */
router.get('/list', NewsController.getNewsList);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值