Node.js案例 - 记账本

news2024/11/19 9:37:57

目录

项目效果

项目的搭建

​编辑

响应静态网页

​编辑

​编辑

结合MongoDB数据库

结合API接口

进行会话控制


项目效果

该案例实现账单的添加删除查看,用户的登录注册。功能比较简单,但是案例主要是使用前段时间学习的知识进行实现的,主要包括express服务的搭建以及使用,并结合MongoDB数据库对数据进行存储以及操作,同时编写相应的API接口,最后进行会话控制,确保数据的安全。如果以下的某一些部分感觉不太理解的话可以看我之前对应的文章。案例中使用到的知识点都是前面文章有涉及到的。

项目的搭建

首先我们直接使用express-generator来快速地搭建express应用骨架,输入命令行:express -e 文件名 然后使用npm i来进行项目依赖的下载。完成之后可以在package.json中对运行的命令 "start": "node./bin/www" 修改为 "start": "nodemon ./bin/www",这样后续的内容修改服务器就会自动地运行了。接下来运行:npm start,进行服务的启动。服务默认是监听3000端口,我们输入http://127.0.0.1:3000进行访问。出现以下页面即服务搭建成功。

接下来,我们需要对路由规则进行配置,我们可以app.js文件中进行查看,app.use('/', indexRouter);我们可以找到对应的路由导入var indexRouter = require('./routes/index'); 因此我们在routes文件夹下面的index.js文件进行路由的配置。

//index.js

var express = require('express');
var router = express.Router();

// 记账本列表
router.get('/account', function(req, res, next) {
   res.send('账单列表');
});

// 记账本列表添加
router.get('/account/create', function(req, res, next) {
  res.send('添加记录');
});

module.exports = router;

输入不同的路径得到不同的结构:

响应静态网页

我们事先准备好了两个页面,一个为账单页面,一个为添加页面。我们借助res.rend()可以对ejs中的内容响应给浏览器的功能来进行操作。在views文件夹下面创建两个ejs文件。将账单页面以及添加页面加入。

//list.ejs

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link
      href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css"
      rel="stylesheet"
    />
    <style>
      label {
        font-weight: normal;
      }
      .panel-body .glyphicon-remove{
        display: none;
      }
      .panel-body:hover .glyphicon-remove{
        display: inline-block
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="row">
        <div class="col-xs-12 col-lg-8 col-lg-offset-2">
          <h2>记账本</h2>
          <hr />
          <div class="accounts">
            <div class="panel panel-danger">
              <div class="panel-heading">2023-04-05</div>
              <div class="panel-body">
                <div class="col-xs-6">抽烟只抽煊赫门,一生只爱一个人</div>
                <div class="col-xs-2 text-center">
                  <span class="label label-warning">支出</span>
                </div>
                <div class="col-xs-2 text-right">25 元</div>
                <div class="col-xs-2 text-right">
                  <span
                    class="glyphicon glyphicon-remove"
                    aria-hidden="true"
                  ></span>
                </div>
              </div>
            </div>
            <div class="panel panel-success">
              <div class="panel-heading">2023-04-15</div>
              <div class="panel-body">
                <div class="col-xs-6">3 月份发工资</div>
                <div class="col-xs-2 text-center">
                  <span class="label label-success">收入</span>
                </div>
                <div class="col-xs-2 text-right">4396 元</div>
                <div class="col-xs-2 text-right">
                  <span
                    class="glyphicon glyphicon-remove"
                    aria-hidden="true"
                  ></span>
                </div>
              </div>
            </div>
            
          </div>
        </div>
      </div>
    </div>
  </body>
</html>
//create.ejs
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>添加记录</title>
    <link
      href="/css/bootstrap.css"
      rel="stylesheet"
    />
    <link href="/css/bootstrap-datepicker.css" rel="stylesheet">
  </head>
  <body>
    <div class="container">
      <div class="row">
        <div class="col-xs-12 col-lg-8 col-lg-offset-2">
          <h2>添加记录</h2>
          <hr />
          <form method="post" action="/account">
            <div class="form-group">
              <label for="item">事项</label>
              <input
                name="title"
                type="text"
                class="form-control"
                id="item"
              />
            </div>
            <div class="form-group">
              <label for="time">发生时间</label>
              <input
                name="time"
                type="text"
                class="form-control"
                id="time"
              />
            </div>
            <div class="form-group">
              <label for="type">类型</label>
              <select name="type" class="form-control" id="type">
                <option value="-1">支出</option>
                <option value="1">收入</option>
              </select>
            </div>
            <div class="form-group">
              <label for="account">金额</label>
              <input
                name="account"
                type="text"
                class="form-control"
                id="account"
              />
            </div>
            
            <div class="form-group">
              <label for="remarks">备注</label>
              <textarea  name="remarks" class="form-control" id="remarks"></textarea>
            </div>
            <hr>
            <button type="submit" class="btn btn-primary btn-block">添加</button>
          </form>
        </div>
      </div>
    </div>
    <script src="/js/jquery.min.js"></script>
    <script src="/js/bootstrap.min.js"></script>
    <script src="/js/bootstrap-datepicker.min.js"></script>
    <script src="/js/bootstrap-datepicker.zh-CN.min.js"></script>
    <script src="/js/main.js"></script>
  </body>
</html>

然后我们修改原本的路由,让其输入/account时为账单页表页面,输入/account/create时为列表添加页面。

// 记账本列表
router.get('/account', function(req, res, next) {
  res.render('list');
});

// 记账本列表添加
router.get('/account/create', function(req, res, next) {
  res.render('create');
});

结合MongoDB数据库

接下来我们结合前几篇文章我们学习到的MongoDB数据库来对数据的添加,删除以及读取等操作。如果不懂这一步操作的小伙伴看完前面发的文章。

我们在我们项目中创建三个文件夹:config,db以及models。config文件用于对配置进行统一的设置,在里面单独地设置域名端口以及数据库名等信息。db文件夹中创建db.js,主要进行导入mongoose,连接mongoose服务,设置成功以及失败的回调等信息。module用于创建文档结构对象。

//config.js
module.exports={
    DBHOST:'127.0.0.1',
    DBPORT:27017,
    DBNAME:'bilibili',
    secret:'atguigu'
}
//db.js
module.exports = function (success, error) {
    //导入配置文件
    const {DBHOST,DBPORT,DBNAME}=require('../config/config');

    //导入mongoose
    const mongoose = require('mongoose');
    //连接mongodb服务
    mongoose.connect(`mongodb://${DBHOST}:${DBPORT}/${DBNAME}`);

    mongoose.connection.once('open', () => {
        success();

    })

    //设置连接错误的回调
    mongoose.connection.on('error', () => {
        error();

    });
    //设置连接关闭的回调
    mongoose.connection.on('close', () => {
        console.log('连接关闭');
    });
}

接着再www文件中导入db.js中的函数,在文件中调用函数,传入两个函数,成功的回调以及失败的回调,成功的回调直接写原本启动http服务的代码,确保数据库连接成功之后再启动http服务。失败的回调我们直接输出失败即可。

//www
const db = require('../db/db');
//连接上数据库再来启动http服务
db(()=>{

//原本文件中的代码

}
},()=>{
  console.log("连接失败")
})

接着想要操作数据库,我们需要先准备好模型文件:

//models/AccountModel.js
const mongoose = require('mongoose');
//创建文档结构对象
let AccountSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true
    },
    time: Date,
    type: {
        type: Number,
        required: true
    },
    account: {
        type: Number,
        required: true
    },
    remarks: {
        type: String
    }
});
//创建文档模型对象
let AccountModel = mongoose.model('accounts', AccountSchema);
//暴露模型对象
module.exports = AccountModel;

模型文件准备好之后,我们在对路由文件,routes/index.js文件来进行操作,在原本的文件中添加新增数据的操作:

//新增记录
router.post('/account', function(req, res, next) {
  AccountModel.create({
    ...req.body,
    time:moment(req.body.time).toDate()
  }).then(data=>{
    res.render('success',{msg:'添加成功~~',url:'/account'});
  }).catch(err=>{
    res.status(500).send('插入失败~~');
  })
});

这里面使用到了一个moment包,主要是用于对日期进行转换为对象形式方便后续的操作。需要在文件中导入const moment=require('moment');

接着我们添加账单,可以使用数据库可视化工具看到自己添加的数据,我使用的是Navicat。新建连接选择Mongo,连接成功之后就可以看到对应的数据库,以及相应的集合。

以上就是我们所添加的数据,但是我们需要在页表页面上也可以看到对应的数据,我们在路由配置文件中进行读取数据的操作。

// 记账本列表
router.get('/account',function(req, res, next) {
  //获取所有账单信息
  AccountModel.find().sort({time:-1}).exec().then(data=>{
    res.render('list',{accounts:data,moment:moment});

  }).catch(err=>{
    res.status(500).send('读取失败~~')
  })
  
});

接着需要修改list.ejs中的代码,方便对数据进行展示:

<!DOCTYPE html>
<html lang="en">
  <body>
    <div class="container">
      <div class="row">
        <div class="col-xs-12 col-lg-8 col-lg-offset-2">
          <div class="row text-right">
            <div class="col-xs-12" style="padding-top: 20px;">
              <form action="/logout" method="post">
                <button class="btn btn-danger">退出</button>
              </form>
            </div>
          </div>
          <hr>
          <div class="row">
            <h2 class="col-xs-6">记账本</h2>
            <h2 class="col-xs-6 text-right"><a href="/account/create" class="btn btn-primary">添加账单</a></h2>
          </div>
   
          <hr />
          <div class="accounts">
            <% accounts.forEach(item =>{ %>
            <div class="panel <%= item.type=== -1 ? 'panel-danger':'panel-success' %>">
              <div class="panel-heading"><%= moment(item.time).format('YYYY-MM-DD') %></div>
              <div class="panel-body">
                <div class="col-xs-6"><%= item.title %></div>
                <div class="col-xs-2 text-center">
                  <span class="label <%= item.type=== -1 ? 'label-warning':'label-success' %>"><%= item.type=== -1  ? '支出':'收入' %></span>
                </div>
                <div class="col-xs-2 text-right"><%= item.account %> 元</div>
                <div class="col-xs-2 text-right">
                    <a class="delBtn" href="/account/<%= item._id %>">
                       <span
                        class="glyphicon glyphicon-remove"
                        aria-hidden="true"
                      ></span>
                    </a>
                </div>
              </div>
            </div>
            <% }) %>          
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

接着我们来对数据进行删除操作,我们在list.ejs中对叉号绑定一个事件,当用户确定删除时再进行删除数据,防止数据误删。

 <div class="col-xs-2 text-right">
      <a class="delBtn" href="/account/<%= item._id %>">
        <span  class="glyphicon glyphicon-remove" aria-hidden="true"></span>
      </a>
 </div>

  <script>
    let delBtns=document.querySelectorAll('.delBtn');
    delBtns.forEach(item =>{
      item.addEventListener('click',function(e){
        if(confirm('您确定要删除该文档吗?')){
          return true;
        }else{
          e.preventDefault();
        }
      })
    })
  </script>

并在路由中设置删除的操作:

//删除记录
router.get('/account/:id',(req,res)=>{

  //获取params 的 id参数
  let id=req.params.id;
  //删除
  AccountModel.deleteOne({_id:id}).then(data=>{
    res.render('success',{msg:'删除成功~~',url:'/account'});
  }).catch(err=>{
    res.status(500).send('删除失败~~')
  })

});

结合API接口

当我们需要将项目推广到更多的客户端程序时,而不仅仅是局限在我们的浏览器进行访问时,我们就需要为它添加对应的API接口,同样的前几篇文章也有介绍了API接口的详细内容。如果不是太了解的小伙伴可以回头去看看。

为了更好地区分,我们在routes文件夹下创建一个名为web的文件,将原本的路由配置文件放入其中,再创建一个名为api的文件夹,创建一个名为account.js的文件用于存放API路由的配置。文件路径修改之后需要修改引入该文件的路径。并在app.js中导入并使用:

const accountRouter=require('./routes/api/account');

app.use('/api',accountRouter);

在api的路由文件中实现创建账单接口、删除账单接口、获取单条数据接口以及更新账单接口。对应代码如下:

// /api/account.js
const express = require('express');
const jwt = require('jsonwebtoken');
//导入moment
const moment = require('moment');
const AccountModel = require('../../models/AccountModel');
//导入中间件
let checkTokenMiddleware = require('../../middlewares/checkTokenMiddleware')
const router = express.Router();

// 记账本列表
router.get('/account', checkTokenMiddleware,function (req, res, next) {
    AccountModel.find().sort({ time: -1 }).exec().then(data => {
        res.json({
            //响应码 
            code: '0000',
            //响应信息
            msg: '读取成功',
            //响应数据
            data: data
        })
    }).catch(err => {
        res.json({
            //响应码 
            code: '1001',
            //响应信息
            msg: '读取失败',
            //响应数据
            data: null
        })
    })

});

// 记账本列表添加
router.get('/account/create',checkTokenMiddleware, function (req, res, next) {
    res.render('create');
});

//新增记录
router.post('/account', checkTokenMiddleware,function (req, res) {

    AccountModel.create({
        ...req.body,
        time: moment(req.body.time).toDate()
    }).then(data => {
        res.json({
            //响应码 
            code: '0000',
            //响应信息
            msg: '创建成功',
            //响应数据
            data: data
        })
    }).catch(err => {
        res.json({
            //响应码 
            code: '1002',
            //响应信息
            msg: '创建失败',
            //响应数据
            data: null
        })
    })

});

//删除记录
router.delete('/account/:id', checkTokenMiddleware,(req, res) => {
    //获取params 的 id参数
    let id = req.params.id;
    //删除
    AccountModel.deleteOne({ _id: id }).then(data => {
        res.json({
            //响应码 
            code: '0000',
            //响应信息
            msg: '删除成功',
            //响应数据
            data: {}
        })
    }).catch(err => {
        res.json({
            //响应码 
            code: '1003',
            //响应信息
            msg: '删除失败',
            //响应数据
            data: null
        })
    })
});
//获取当个账单信息
router.get('/account/:id', checkTokenMiddleware,(req, res) => {
    //获取params 的 id参数
    let id = req.params.id;
    //查询数据库
    AccountModel.findById(id).then(data => {
        res.json({
            //响应码 
            code: '0000',
            //响应信息
            msg: '读取成功',
            //响应数据
            data: data
        })
    }).catch(err => {
        res.json({
            //响应码 
            code: '1004',
            //响应信息
            msg: '读取失败',
            //响应数据
            data: null
        })
    })
});

//更新单个账单信息
router.patch('/account/:id', checkTokenMiddleware,(req, res) => {
    //获取params 的 id参数
    let id = req.params.id;
    AccountModel.updateOne({ _id: id }, req.body).then(data => {
        //再次查询数据库
        AccountModel.findById(id).then(data => {
            res.json({
                //响应码 
                code: '0000',
                //响应信息
                msg: '更新成功',
                //响应数据
                data: data
            }).catch(err => {
                res.json({
                    //响应码 
                    code: '1004',
                    //响应信息
                    msg: '读取失败',
                    //响应数据
                    data: null
                })
            })
        }).catch(err => {
            res.json({
                //响应码 
                code: '1005',
                //响应信息
                msg: '更新失败',
                //响应数据
                data: null
            })

        })
    });

})
module.exports = router;

那如何对我们写好的接口做测试呢,在前面的文章中,介绍了Apipost软件来测试接口,我们来尝试一下,发一个GET请求来获取表单的信息数据。成功得到对应的数据。

进行会话控制

接下来我们使用我们学过的session以及token来对数据进行保护。具体的知识点可以看我前几篇发的文章。

接下来,我们为项目添加一个注册的页面,在routes中web文件夹下创建一个auth.js文件,用户配置注册以及登录时的session相关信息。我们同样使用模板引擎来响应注册页面。将该创建好的路由文件在app.js中进行导入以及使用。

创建一个reg.ejs文件:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>注册</title>
  <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet" />
</head>

<body>
  <div class="container">
    <div class="row">
      <div class="col-xs-12 col-md-8 col-md-offset-2 col-lg-4 col-lg-offset-4">
        <h2>注册</h2>
        <hr />
        <form method="post" action="/reg">
          <div class="form-group">
            <label for="item">用户名</label>
            <input name="username" type="text" class="form-control" id="item" />
          </div>
          <div class="form-group">
            <label for="time">密码</label>
            <input name="password" type="password" class="form-control" id="time" />
          </div>
          <hr>
          <button type="submit" class="btn btn-primary btn-block">注册</button>
        </form>
      </div>
    </div>
  </div>
</body>

</html>

在响应注册页面

//注册
router.get('/reg',(req,res)=>{
    res.render('auth/reg');
});

我们需要先创建用户模型,后续才能对其进行插入数据库以及设置session等操作。

const mongoose = require('mongoose');
//创建文档结构对象
let UserSchema = new mongoose.Schema({
    username:String,
    password:String

});
//创建文档模型对象
let UserModel = mongoose.model('users', UserSchema);
//暴露模型对象
module.exports = UserModel;

将该模型导入到对应的配置文件中,并进行注册等相关操作:

/导入用户模型
const UserModel=require('../../models/UserModel');
const md5=require('md5');

//注册用户
router.post('/reg',(req,res)=>{
    UserModel.create({...req.body,password:md5(req.body.password)}).then(data=>{
        res.render('success',{msg:'注册成功',url:'/login'});

    }).catch(err=>{
        res.status(500).send('注册失败')
    })
});

接下来实现用户登录功能,我们同样创建一个login.ejs文件放置登录页面模板,复制注册页面的代码,将对应的文字以及路径修改一下即可。这部分不进行代码展示。进行登录的相关操作,当用户登录之后,我们需要对他的session进行写入,并返回sessionid。

//登录
router.get('/login',(req,res)=>{
    res.render('auth/login');
});
//登录操作
router.post('/login',(req,res)=>{
    //获取用户名和密码
    let {username,password}=req.body;
    UserModel.findOne({username:username,password:md5(password)}).then(data=>{
        if(!data){
            return res.send('账号或者密码错误~~')
        }
        //写入session
        req.session.username=data.username;
        req.session._id=data._id;
        //登录成功响应
        res.render('success',{msg:'登录成功',url:'/account'});

    }).catch(err=>{
        res.status(500).send('登录失败')
    })
});

这部分需要先安装express-session以及connect-mongo,并在app.js中进行导入,并设置中间件。

//导入 express-session 
const session = require("express-session");
const MongoStore = require('connect-mongo');

//导入配置项
const {DBHOST, DBPORT, DBNAME} = require('./config/config');
//设置 session 的中间件
app.use(session({
  name: 'sid',   //设置cookie的name,默认值是:connect.sid
  secret: 'atguigu', //参与加密的字符串(又称签名)  加盐
  saveUninitialized: false, //是否为每次请求都设置一个cookie用来存储session的id
  resave: true,  //是否在每次请求时重新保存session  20 分钟    4:00  4:20
  store: MongoStore.create({
    mongoUrl: `mongodb://${DBHOST}:${DBPORT}/${DBNAME}` //数据库的连接配置
  }),
  cookie: {
    httpOnly: true, // 开启后前端无法通过 JS 操作
    maxAge: 1000 * 60 * 60 * 24 * 7 // 这一条 是控制 sessionID 的过期时间的!!!
  },
}))

当我们登录成功之后我们可以在数据库中看到我们对应的session信息。

写入之后,我们还需要判断用户是否登录,若用户没有进行登录则拒绝访问,跳转到登录页面。我们在web文件夹下的index.js中编写一个中间件。

//检测登录的中间件
const checkLoginMiddleware = (req, res, next) => {
    //判断
    if(!req.session.username){
      return res.redirect('/login');
    }
    next();
  }

并在下面的路由规则中使用它。这里只对查看记账本列表做演示,其他的一致。

// 记账本列表
router.get('/account',checkLoginMiddleware,function(req, res, next) {
  AccountModel.find().sort({time:-1}).exec().then(data=>{
    res.render('list',{accounts:data,moment:moment});

  }).catch(err=>{
    res.status(500).send('读取失败~~')
  })
  
});

接下来继续在auth.js中实现退出登录功能。

//退出登录
router.post('/logout',(req,res)=>{
    //销毁session
    req.session.destroy(()=>{
        res.render('success',{msg:'退出成功',url:'/login'})
    })
})

在退出登录界面,部分进行修改,防止CSRF跨站请求伪造。它会导致用户的session被获取。大部分的CSRF跨站请求伪造都是使用一个天生具有跨域能力的标签。但是它们发送的请求都是get请求,因此我们将原本的退出修改为post请求。可以防止发生。

<div class="col-xs-12" style="padding-top: 20px;">
   <form action="/logout" method="post">
        <button class="btn btn-danger">退出</button>
   </form>
</div>

我们接着在web文件夹下的index.js对首页添加路由规则:

//添加首页路由规则
router.get('/',(req,res)=>{
  //重定向
  res.redirect('/account');
});

在app.js中添加404的响应的模板,在views中创建一个404.ejs文件。

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  res.render('404');
});

以上的操作,我们使用了session对网页端进行了约束,接下来我们使用token来对接口来进行约束。在api文件夹下创建一个auth.js文件对其进行设置。这部分就不再详细介绍了,文件相应的代码如下:

var express = require('express');
var router = express.Router();
//导入jwt
const jwt=require('jsonwebtoken');
//读取配置项
const {secret} =require('../../config/config');
//导入用户模型
const UserModel=require('../../models/UserModel');
const md5=require('md5');


//登录操作
router.post('/login',(req,res)=>{
    //获取用户名和密码
    let {username,password}=req.body;
    UserModel.findOne({username:username,password:md5(password)}).then(data=>{
        if(!data){
            return res.json({
                code:'2002',
                msg:'用户名或者密码错误',
                data:null
            })
        }
        //创建当前用户token
        let token=jwt.sign({
            username:data.username,
            _id:data._id
        },secret,{
            expiresIn:60 * 60 * 24 *7
        });
        //响应token
        res.json({
            code:'0000',
            msg:'登录成功',
            data:token
        })
        

    }).catch(err=>{
        res.json({
            code:'2001',
            msg:'数据库读取失败',
            data:null
        })
    })
});

//退出登录
router.post('/logout',(req,res)=>{
    //销毁session
    req.session.destroy(()=>{
        res.render('success',{msg:'退出成功',url:'/login'})
    })
})


module.exports = router;

中间件文件:

//checkTokenMiddleware.js
const jwt=require('jsonwebtoken');
//读取配置项
const {secret} =require('../config/config');
module.exports = (req, res, next) => {
    //获取token
    let token = req.get('token');
    //判断
    if (!token) {
        return res.json({
            code: '2003',
            msg: 'token 缺失',
            data: null
        })
    }
    //校验token
    jwt.verify(token, secret, (err, data) => {
        if (err) {
            return res.json({
                code: '2004',
                msg: '校验失败',
                data: null
            })
        }
         //保存用户的信息
         req.user=data;
        //如果执行成功
        next();

    });

}

通过Apipost来进行校验,当没有携带token时,获取不到数据,当设置请求头token数据时,能够获取到对应的数据。

好啦!本文就到这里了,Node.js系列的文章就告一段落了!如果有不足之处还请见谅~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1269905.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java后端开发——SpringMVC商品管理程序

Java后端开发——SpringMVC商品管理程序 今日目标 Spring MVC框架介绍掌握SpringMVC的核心类的原理及配置掌握SpringMVC的常用注解掌握SpringMVC的增删改查编程 Spring MVC框架介绍 Spring MVC&#xff08;Model-View-Controller&#xff09;是一个基于Java的开源框架&#x…

移动开发新风向?HarmonyOS、车载、音视频、flutter

目前&#xff0c;移动开发已经处于饱和的阶段&#xff0c;Android开发也不如当年盛况&#xff0c;已经不再像前几年前那么火爆。正如一种编程语言如果经历过盛极一时&#xff0c;那么必然有这样的一条曲线&#xff0c;像我们学的正弦曲线先急速上升&#xff0c;然后到达顶点&am…

拼图 游戏

运行出的游戏界面如下&#xff1a;按住A不松开&#xff0c;显示完整图片&#xff1b;松开A显示随机打乱的图片 User类 package domain;/*** ClassName: User* Author: Kox* Data: 2023/2/2* Sketch:*/ public class User {private String username;private String password;p…

服务器数据恢复—V7000存储raid5崩溃导致上层卷无法使用的数据恢复案例

服务器数据恢复环境&#xff1a; 某品牌V7000存储中有一组由几十块硬盘组建的raid5阵列。上层操作系统为windows server&#xff0c;NTFS分区。 服务器故障&#xff1a; 有一块硬盘出现故障离线&#xff0c;热备盘自动上线替换离线硬盘。在热备盘上线同步数据的过程&#xff0c…

WordPress 外链跳转插件

WordPress 外链跳转插件是本站开发的一款WordPress插件&#xff0c;能对文中外链添加一层过滤&#xff0c;有效防止追踪&#xff0c;以及提醒用户。 类似于知乎、CSDN打开其他链接的提示。 后台可以设置白名单 学习资料源代码&#xff1a;百度网盘 密码&#xff1a;123

低功耗蓝牙模块在运动健身追踪中的革新应用

随着健康和健身意识的不断提升&#xff0c;人们对运动追踪技术的需求日益增加。低功耗蓝牙模块的引入为运动健身追踪领域带来了新的可能性。本文将探讨低功耗蓝牙模块在运动健身追踪中的创新应用&#xff0c;重点关注其在设备连接性、数据传输和用户体验方面的优势。 随着科技的…

上海亚商投顾:沪指震荡下跌 成交量继续下破8000亿

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日震荡调整&#xff0c;深成指、创业板指午后跌超1%&#xff0c;北证50指数跌超7%&#xff0c;超百只北…

基于web宠颐生宠物医院系统设计与实现

基于web宠颐生医院系统开发与实现 摘要&#xff1a;时代飞速发展&#xff0c;网络也飞速发展&#xff0c;互联网许多的行业都可以用互联网实现了&#xff0c;互联网已经成为了人们生活中重要的一部分&#xff0c;或多或少的影响着我们的生活&#xff0c;互联网在给我带了方便的…

【洛谷算法题】P5716-月份天数【入门2分支结构】

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5716-月份天数【入门2分支结构】&#x1f30f;题目描述&#x1f30f;输入格式&a…

安卓apk抓包

起因 手机&#xff08;模拟器&#xff09;有时候抓不到apk的包&#xff0c;需要借助Postern设置一个代理&#xff0c;把模拟器的流量代理到物理机的burp上。 解决方案 使用Postern代理&#xff0c;把apk的流量代理到burp。 Postern是一个用于代理和网络流量路由的工具&#xf…

【Openstack Train安装】二、NTP安装

网络时间协议&#xff1a;Network Time Protocol&#xff08;NTP&#xff09;是用来使计算机时间同步化的一种协议&#xff0c;它可以使计算机对其服务器或时钟源&#xff08;如石英钟&#xff0c;GPS等等)做同步化&#xff0c;它可以提供高精准度的时间校正&#xff08;LAN上与…

MySQL中的JOIN与IN:性能对比与最佳实践

文章目录 JOIN与IN的基本介绍JOININ JOIN与IN性能对比使用JOIN的查询使用IN的查询 何时使用JOIN何时使用IN性能优化的其他考虑因素总结 &#x1f389;MySQL中的JOIN与IN&#xff1a;性能对比与最佳实践 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT陈…

Nginx反向代理和负载均衡详细教程

1、Nginx反向代理概述 关于正向代理和反向代理&#xff0c;我们在前面的章节已经通过一张图给大家详细的介绍过了&#xff0c;简而言之就是正向代理代理的对象是客户端&#xff0c;反向代理代理的是服务端&#xff0c;这是两者之间最大的区别。 Nginx即可以实现正向代理&#x…

rabbitmq-server-3.11.10.exe

rabbitmq需要erlang环境 otp_win64_25.1.exe erlang-CSDN博客 https://www.rabbitmq.com/download.htmlhttps://www.rabbitmq.com/install-windows.htmlhttps://github.com/rabbitmq/rabbitmq-server/releases/download/v3.11.10/rabbitmq-server-3.11.10.exe C:\Users\Admi…

【Unity记录】EDM4U(External Dependency Manager)使用说明

GitHub - googlesamples/unity-jar-resolver: Unity plugin which resolves Android & iOS dependencies and performs version management 引入谷歌包时发现有这个玩意&#xff0c;主要用途是自动搜索工程内任意文件夹下的Editor/*Dependencies.xml文件 <dependencie…

Langchain-Chatchat的安装过程

参考&#xff1a;LLMs之RAG&#xff1a;LangChain-Chatchat(一款中文友好的全流程本地知识库问答应用)的简介(支持 FastChat 接入的ChatGLM-2/LLaMA-2等多款主流LLMs多款embe_一个处女座的程序猿的博客-CSDN博客 1、安装过程中出现了 GPU驱动版本 是11.8 而 python -c "…

小航助学题库蓝桥杯题库c++选拔赛(22年3月)(含题库教师学生账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09; 需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;

边界突破之linux系统上线Cobalt Strike

别低头&#xff0c;皇冠会掉&#xff1b;别流泪&#xff0c;坏人会笑 基础文件 加载插件 服务端开启监听 windows/beacon_https/reverse_https 类型的beacon 生成木马Beacon 命令如下 linux ./genCrossC2.Linux [TeamServer的IP] [HTTPS监听器端口] [.cobaltstrike.beacon_k…

基于ASP.Net的图书管理系统的设计与实现

摘 要 图书馆管理系统是一整套高科技技术与书本管理知识结合的产物。它把传统书籍静态的服务这个缺陷完美化&#xff0c;完成多媒体数据的交互、远程网络连接、检查搜索智能化、多数据库无障碍联系、跨时空信息服务。图书管理系统用计算机程序替代了传统手工记录的工作模式&am…

C# WPF上位机开发(倒计时软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 生活当中&#xff0c;我们经常会遇到倒计时的场景&#xff0c;比如体育运动的时候、考试的时候等等。正好最近我们学习了c# wpf开发&#xff0c;完…