前后端交互—开发一个完整的服务器
代码下载
初始化
新建 apiServer 文件夹作为项目根目录,并在项目根目录中运行如下的命令,初始化包管理配置文件:
npm init -y
运行如下的命令,安装 express、cors:
npm i express cors
在项目根目录中新建 app.js 作为整个项目的入口文件,配置 cors 跨域中间件、解析表单数据的中间件,并启动服务器:
// 导入 express 模块
const express = require('express');
// 创建 express 的服务器实例
const app = express();// 配置跨域中间件
const cors = require('cors');
app.use(cors());// 配置解析 application/x-www-form-urlencoded 格式的表单数据的中间件
app.use(express.urlencoded({ extended: false }));// 指定端口号并启动web服务器
app.listen('80', function() {console.log('server running at http://127.0.0.1:80');
});
因为在处理函数中,需要多次调用 res.send() 向客户端响应 处理失败 的结果,为了简化代码, 可以手动封装一个 res.cc() 函数。在 app.js 中所有路由之前,声明一个全局中间件,为 res 对象挂载一个 函数 :
// 在 路由 之前自定义中间件,处理错误的响应数据
app.use(function(req, res, next) {// status 默认值1,表示失败,0表示成功// err 可能是错误对象,也可能是错误描述字符串res.cc = function(err, status = 1) {res.send({status: status,message: err instanceof Error ? err.message : err});};next();
});
- 在项目根目录中,新建 router 文件夹,用来存放所有的 路由 模块,路由模块中,只存放客户端的请求与处理函数之间的映射关系
- 在项目根目录中,新建 routerHandler 文件夹,用来存放所有的 路由处理函数模块 路由处理函数模块中,专门负责存放每个路由对应的处理函数
- 在项目根目录中,新建 db 文件夹,用来存放所有的 数据库 模块
- 在项目根目录中,新建 middleware 文件夹,用来存放所有的 自定义中间件 模块
登录注册
新建 users 表,在 my_db_01 数据库中,新建 ev_users 表如下:
安装并配置 mysql 模块,需要安装并配置 mysql 这个第三方模块,来连接和操作 MySQL 数据库,运行如下命令,安装 mysql 模块:
npm i mysql
在项目根目录中 db 文件夹新建 index.js 文件,在此自定义模块中创建数据库的连接对象:
const mysql = require('mysql');
const db = mysql.createPool({host: 'localhost',port: '3306',user: 'root',password: 'admin123',database: 'my_db_01'
});module.exports = db;
初始化用户路由模块
在 router 文件夹中,新建 user.js 文件,作为用户的路由模块,并初始化代码如下:
const express = require('express') // 创建路由对象
const router = express.Router()
// 注册新用户
router.post('/signin', (req, res) => {res.send('signin OK')
})
// 登录
router.post('/login', (req, res) => {res.send('login OK')
})
// 将路由对象共享出去 module.exports = router
在 app.js 中,导入并使用 用户路由模块 :
// 配置路由
const user = require('./router/user');
app.use('/api', user);
抽离用户路由模块中的处理函数
为了保证 路由模块 的纯粹性,所有的 路由处理函数,必须抽离到对应的 路由处理函数模块中。
在 /routerHandler/user.js 中,使用 exports 对象,分别向外共享如下两个 路由处理函数:
/**
* 在这里定义和用户相关的路由处理函数,供 /router/user.js 模块进行调用 */// 注册用户的处理函数
exports.signin = (req, res) => {res.send('signin OK')
}// 登录的处理函数
exports.login = (req, res) => {res.send('login OK')
}
将 /router/user.js 中的代码修改为如下结构:
const express = require('express');
const router = express.Router();
const handler = require('../routerHandler/user');// 注册
router.post('/signin', handler.signin);// 登录
router.post('/login', handler.login);module.exports = router;
joi 表单数据验证
表单验证的原则:前端验证为辅,后端验证为主,后端 永远不要相信 前端提交过来的 任何内容。
在实际开发中,前后端都需要对表单的数据进行合法性的验证,而且, 后端做为数据合法性验证的最后 一个关口 ,在拦截非法数据方面,起到了至关重要的作用。
单纯的使用 if…else… 的形式对数据合法性进行验证,效率低下、出错率高、维护性差。因此, 推荐使用 第三方数据验证模块 ,来降低出错率、提高验证的效率与可维护性, 让后端程序员把更多的精力放在核心业务逻辑的处理上。
安装 joi 包,为表单中携带的每个数据项,定义验证规则:
npm install joi
在 /middleware/expressJoi.js 中,使用 joi 对象,定义并向外共享表单数据验证中间件:
const joi = require('joi');const expressJoi = function (schemas, options = { strict: false }) {// 自定义校验选项// strict 自定义属性,默认不开启严格模式,会过滤掉那些未定义的参数项// 如果用户指定了 strict 的值为 true,则开启严格模式,此时不会过滤掉那些未定义的参数项if (!options.strict) {// allowUnknown 允许提交未定义的参数项// stripUnknown 过滤掉那些未定义的参数项options = { allowUnknown: true, stripUnknown: true, ...options }}// 从 options 配置对象中,删除自定义的 strict 属性delete options.strict// TODO: 用户指定了什么 schema,就应该校验什么样的数据return function (req, res, next) {['body', 'query', 'params'].forEach(key => {// 如果当前循环的这一项 schema 没有提供,则不执行对应的校验if (!schemas[key]) return// 执行校验const schema = joi.object(schemas[key])const { error, value } = schema.validate(req[key], options)if (error) {console.log('---------------');// 校验失败throw error} else {// 校验成功,把校验的结果重新赋值到 req 对应的 key 上req[key] = value}})// 校验通过next()}
}module.exports = expressJoi
新建 /schema/user.js 用户信息验证规则模块,并初始化代码如下:
// @hapi/joi 包,为表单中携带的每个数据项,定义验证规则
const joi = require('joi');// * string() 值必须是字符串
// * alphanum() 值只能是包含 a-zA-Z0-9 的字符串 * min(length) 最小长度
// * max(length) 最大长度
// * required() 值是必填项,不能为 undefined
// * pattern(正则表达式) 值必须符合正则表达式的规则
const username = joi.string().alphanum().min(2).max(20).required();
const password = joi.string().required().pattern(/^[\S]{6,16}$/);exports.schema = {user: {body: {username,password}}
}
修改 /router/user.js 中的代码如下:
// 导入验证表单数据的中间件
const expressJoi = require('../middleware/expressJoi');
// 导入需要的验证规则对象
const { schema } = require('../schema/user');// 为 注册新用户 配置表单验证中间件
// 在注册新用户的路由中,声明局部中间件,对当前请求中携带的数据进行验证
// 数据验证通过后,会把这次请求流转给后面的路由处理函数
// 数据验证失败后,终止后续代码的执行,并抛出一个全局的 Error 错误,进入全局错误级别中间件中进行 处理
router.post('/signin', expressJoi(schema.user), handler.signin);// 登录
router.post('/login', expressJoi(schema.user), handler.login);
在 app.js 的全局错误级别中间件中,捕获验证失败的错误,并把验证失败的结果响应给客户端:
// 配置 错误处理 中间件
const joi = require('joi');
app.use(function(err, req, res, next) {// console.log(err);if (err instanceof joi.ValidationError) {// 处理数据校验错误} else {// 处理其他未知错误}return res.cc(err);
});
加密处理
为了保证密码的安全性,不建议在数据库以 明文 的形式保存用户密码,推荐对密码进行 加密存储。使用 对用户密码进行加密,优点:
- 加密之后的密码, 无法被逆向破解
- 同一明文密码多次加密,得到的 加密结果各不相同 ,保证了安全性
运行如下命令,安装 bcryptjs:
npm i bcryptjs
调用 bcrypt.hashSync(明文密码, 随机盐的长度) 方法,对用户的密码进行加密处理:
const bcrypt = require('bcryptjs');
// 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串
userinfo.password = bcrypt.hashSync(userinfo.password, 10);
调用 bcrypt.compareSync(用户提交的密码, 数据库中的密码) 方法比较密码是 否一致,返回值是布尔值(true 一致、false 不一致):
// 拿着用户输入的密码,和数据库中存储的密码进行对比
const compareResult = bcrypt.compareSync(userinfo.password, results[0].password);// 如果对比的结果等于 false, 则证明用户输入的密码错误
if (!compareResult) {console.log('密码错误!');
}
注册
- 检测表单数据是否合法
- 检测用户名是否被占用
- 对密码进行加密处理
- 插入新用户
新建 /middleware/constValue.js 常量数据模块,并初始化代码如下:
// sql 语句
// 查找用户名
const selectUN = `select * from users where username = ?`;
// 插入用户
const insertUser = `insert into users set ?`;module.exports = {selectUN,insertUser
}
修改 /routerHandler/user.js 中的代码,实现最终完整的登录逻辑,如下:
const bcrypt = require('bcryptjs');
const constValue = require('../middleware/constValue');
const signin = (req, res) => {const info = req.body;// 数据校验交给 expressJoi 中间件处理// if (info.username && info.password) {db.query(constValue.selectUN, info.username, function(err, result) {if (err) {res.cc(err);} else if (result.length > 0) {res.cc('用户名被占用,请更换其他用户名!');} else {// 调用 bcrypt.hashSync(明文密码, 随机盐的长度) 方法,对用户的密码进行加密处理info.password = bcrypt.hashSync(info.password, 10);db.query(constValue.insertUser, {username: info.username, password: info.password}, function(err, result) {if (err) {res.cc(err);} else if (result.affectedRows === 1) {res.cc('注册成功!', 0);} else {res.cc('注册失败,请稍后重试!');}});}});// } else {// res.cc('用户名或密码不能为空!');// }
};
登录
- 检测表单数据是否合法
- 根据用户名查询用户的数据
- 判断用户输入的密码是否正确
- 生成 JWT 的 Token 字符串
在 /middleware/constValue.js 定义并导出查询用户数据的 SQL 语句以及 JWT 秘钥、加密算法、无权限表达式:
// 查找用户信息
const selectInfo = `select id, username, nickname, email, user_pic from users where id = ?`;
// JWT 秘钥
const jwtSecretKey = 'JWTSecretKeyabcABC123!@#JWTSecretKey';
// JWT 加密算法
const jwtAlgorithms = 'HS256';
// JWT 无需权限的接口 表达式
const jwtUnlessPath = /^\/api\//;
运行如下的命令,安装生成 Token 字符串的包、解析 Token 的中间件的包:
npm i jsonwebtoke express-jwt
核心注意点: 在生成 Token 字符串的时候,一定要剔除 密码 和 头像 的值 通过 ES6 的高级语法,快速剔除 密码 和 头像 的值,将用户信息对象加密成 Token 字符串:
// 通过 ES6 的高级语法,快速剔除用户敏感信息后的数据作为 token 的加密数据
const user = { ...result[0], password: '', user_pic: '' }
// token 有效期为 10 天
const token = jwt.sign(user, constValue.jwtSecretKey, { expiresIn: '10d' });
在 app.js 中注册路由之前,配置解析 Token 的中间件:
// 配置 解析 Token 的中间件
var { expressjwt } = require('express-jwt');
const constValue = require('./middleware/constValue');
app.use(expressjwt({secret: constValue.jwtSecretKey, algorithms: [constValue.jwtAlgorithms]
}).unless({path: [constValue.jwtUnlessPath]
}));
在 app.js 中的 错误级别中间件里面,捕获并处理 Token 认证失败后的错误:
app.use(function(err, req, res, next) {// console.log(err);if (err instanceof joi.ValidationError) {// 处理数据校验错误} else if (err.name === 'UnauthorizedError') {// 处理身份认证失败的错误return res.cc('身份验证失败!');} else {// 处理其他未知错误}return res.cc(err);
});
修改 /routerHandler/user.js 中的代码,实现最终完整的登录逻辑,如下:
const jwt = require('jsonwebtoken');
const expressJwt = require('express-jwt');
const { use } = require('../router/user');
const login = (req, res) => {const info = req.body;// 查询 username 是否存在db.query(constValue.selectUN, info.username, function(err, result) {if (err) {res.cc(err);} else if (result.length === 1) {// 判断密码是否正确if (bcrypt.compareSync(info.password, result[0].password)) {// 剔除用户敏感信息后的数据作为 token 的加密数据const user = { ...result[0], password: '', user_pic: '' }console.log(user);const token = jwt.sign(user, constValue.jwtSecretKey, { expiresIn: '10d' });res.send({status: 0,token: 'Bearer ' + token,message: '登录成功!'});} else {res.cc('密码错误!');}} else {res.cc('登录失败!');}});
};
个人中心
获取用户的基本信息
- 初始化 路由 模块
- 初始化 路由处理函数 模块
- 获取用户的基本信息
创建 /router/userinfo.js 路由模块,并初始化如下的代码结构:
const express = require('express');
const router = express.Router();
const handler = require('../routerHandler/userinfo');// 获取用户信息
router.get('/userinfo', handler.userinfo);module.exports = router;
在 app.js 中导入并使用个人中心的路由模块:
const userinfo = require('./router/userinfo');
app.use('/my', userinfo);
在 /middleware/constValue.js 定义并导出查询用户信息的 SQL 语句:
// 查找用户信息
const selectInfo = `select id, username, nickname, email, user_pic from users where id = ?`;
创建 /routerHandler/userinfo.js 文件,实现最终完整的逻辑,如下:
const db = require('../db/index');
const constValue = require('../middleware/constValue');const userinfo = function(req, res) {console.log(req.auth);db.query(constValue.selectInfo, req.auth.id, function(err, result) {if (err) {res.cc(err);} else if (result.length === 1) {res.send({status: 0,data: result[0],message: '用户信息获取成功!'});} else {res.cc('用户信息获取失败!');}});
};module.exports = {userinfo
};
更新用户的基本信息
- 定义路由和处理函数
- 验证表单数据
- 实现更新用户基本信息的功能
具体实现见代码……
重置密码
- 定义路由和处理函数
- 验证表单数据
- 实现重置密码的功能
在 /schema/user.js 验证规则模块中,定义 newPassword 的验证规则并使用 exports 向外共享如下的验证规则对象:
const password = joi.string().required().pattern(/^[\S]{6,16}$/);
// 1. joi.ref('oldPassword') 表示 newPassword 的值必须和 oldPassword 的值保持一致
// 2. joi.not(joi.ref('oldPwd')) 表示 newPwd 的值不能等于 oldPwd 的值
// 3. .concat() 用于合并 joi.not(joi.ref('oldPwd')) 和 password 这两条验证规则
const newPassword = joi.not(joi.ref('oldPassword')).concat(password);exports.schema = {updatePassword: {body: {oldPassword: password,newPassword}}
}
其他具体实现见代码……
更新用户头像
- 定义路由和处理函数
- 验证表单数据
- 实现更新用户头像的功能
在 /schema/user.js 验证规则模块中,定义 user_pic 的验证规则并使用 exports 向外共享如下的验证规则对象:
// dataUri() 指的是如下格式的字符串数据:
// data:image/png;base64,VE9PTUFOWVNFQ1JFVFM=
const user_pic = joi.string().dataUri().required();exports.schema = {updateAvatar: {body: {user_pic}}
}
在 /router/userinfo.js 模块中,导入需要的验证规则对象, 修改 更新用户头像 的路由:
const expressJoi = require('../middleware/expressJoi');
const { schema } = require('../schema/user');
// 更换头像
router.post('/updateAvatar', expressJoi(schema.updateAvatar), handler.updateAvatar);
在 /middleware/constValue.js 定义并导出 更新用户头像 的 SQL 语句:
// 修改头像
const updateAvatar = `update users set user_pic = ? where id = ?`;
处理 /routerHandler/userinfo.js 中的代码,实现最终完整的登录逻辑,如下:
// 更换头像
const updateAvatar = (req, res) => {console.log('------------');console.log(req.body);db.query(constValue.updateAvatar, [req.body.user_pic, req.auth.id], (err, result) => {if (err) {res.cc(err);} else if (result.affectedRows === 1) {res.cc('更换头像成功!', 0);} else {res.cc('更换头像失败!');}})
}
文章分类管理
具体实现见代码……
文章管理
新建 articles 表如下:
发布新文章
- 初始化路由模块
- 初始化路由处理函数模块
- 使用 multer 解析表单数据
- 验证表单数据
- 实现发布文章的功能
创建 /routerHandler/article.js 路由处理函数模块,并初始化如下的代码结构:
// 发布新文章的处理函数
exports.addArticle = (req, res) => {res.send('ok')
}
创建 /router/article.js 路由模块,并初始化如下的代码结构:
// 导入 express
const express = require('express')
// 创建路由对象
const router = express.Router()// 导入文章的路由处理函数模块
const handler = require('../routerHandler/article');// 发布新文章
routor.post('/addArticle', handler.addArticle);// 向外共享路由对象
module.exports = router
在 app.js 中导入并使用文章的路由模块:
const article = require('./router/article');
app.use('/my', article);
使用 multer 解析表单数据
注意: 使用 express.urlencoded() 中间件无法解析 multipart/form-data 格式的请求体 数据。
推荐使用 multer 来解析 multipart/form-data 格式的表单数据。运行如下的终端命令,在项目中安装 multer :
npm i multer
在 /router/article.js 模块中导入并配置 multer :
// 导入解析 formdata 格式表单数据的包
const multer = require('multer')
// 导入处理路径的核心模块
const path = require('path')
// 创建 multer 的实例对象,通过 dest 属性指定文件的存放路径
const upload = multer({ dest: path.join(__dirname, '../uploads') })// 发布新文章的路由
// upload.single() 是一个局部生效的中间件,用来解析 FormData 格式的表单数据
// 将文件类型的数据,解析并挂载到 req.file 属性中
// 将文本类型的数据,解析并挂载到 req.body 属性中
routor.post('/addArticle', uploads.single('cover_img'), handler.addArticle);
实现发布文章的功能
通过 express-joi 自动验证 req.body 中的文本数据;通过 if 判断手动验证 req.file 中的 文件数据。
在 /schema/user.js 验证规则模块中,定义数据验证规则并使用 exports 向外共享如下的 验证规则对象:
const id = joi.number().integer().min(1).required();
const title = joi.string().required();
const content = joi.string().required().allow('');
const state = joi.string().valid('已发布', '草稿').required();exports.schema = {addArticle: {body: {title,content,cate_id: id,state}}
}
在 /router/article.js 模块中,导入需要的验证规则对象,并在路由中使用。
在 /middleware/constValue.js 定义并导出 发布文章 的 SQL 语句:
// 插入文章
const insertArticle = `insert into articles set ?`;
修改 /routerHandler/article.js 中的代码,实现最终完整的登录逻辑,如下:
const addArticle = (req, res) => {console.log(req.body);console.log(req.file);if (req.file && req.file.fieldname === 'cover_img') {const article = {...req.body,// 文章封面在服务器端的存放路径cover_img: `/uploads/${req.file.filename}`,pub_date: new Date(),author_id: req.auth.id};db.query(constValue.insertArticle, article, (err, result) => {if (err) {res.cc(err);} else if (result.affectedRows === 1) {res.cc('文章添加成功!', 0);} else {res.cc('文章添加失败!');}});} else {res.cc('cover_img 是必选参数!');}
}
相关文章:
前后端交互—开发一个完整的服务器
代码下载 初始化 新建 apiServer 文件夹作为项目根目录,并在项目根目录中运行如下的命令,初始化包管理配置文件: npm init -y运行如下的命令,安装 express、cors: npm i express cors在项目根目录中新建 app.js 作为整个项目的入口文件&a…...
前端框架的虚拟DOM(Virtual DOM)
聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…...
什么是http状态码?
什么是http状态码? 当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含 HTTP 状态码的信息头(server header)用以响应浏览器的请求。 ht…...
linux/CentOS 7安装Nginx
Nginx 是 C语言 开发,建议在 Linux 上运行,当然,也可以安装 Windows 版本,本篇则使用 CentOS 7 作为安装环境。 Nginx一般使用非root账号安装,如果还没有非root账号,先创建账号 创建账号 创建组…...
软件工程期末复习+数据仓库ETL
一、软件工程 请用基本路径测试方法为下列程序设计测试用例,并写明中间过程: 第1步:画出流程图 1.菱形用于条件判断。用在有分支的地方。 2.矩形表示一个基本操作。 3.圆形是连接点 第2步:计算程序环路复杂性 流图G的环路复杂…...
学习C语言——体会计算机中的0和1
/* 把hello隐写入一个整型数组,这个小程序可以考察是否清楚数据在内存中存储的具体细节。 具体的说,int类型在小端机器上的存储方式是高位在高地址,低位在低地址,从视觉习惯上和我们的日常书写习惯相反; char类型占用…...
PyTorch官网demo解读——第一个神经网络(1)
神经网络如此神奇,feel the magic 今天分享一下学习PyTorch官网demo的心得,原来实现一个神经网络可以如此简单/简洁/高效,同时也感慨PyTorch如此强大。 这个demo的目的是训练一个识别手写数字的模型! 先上源码: fr…...
升华 RabbitMQ:解锁一致性哈希交换机的奥秘【RabbitMQ 十】
欢迎来到我的博客,代码的世界里,每一行都是一个故事 升华 RabbitMQ:解锁一致性哈希交换机的奥秘【RabbitMQ 十】 前言第一:该插件需求为什么需要一种更智能的消息路由方式?一致性哈希的基本概念: 第二&…...
vue3 element-plus 日期选择器 el-date-picker 汉化
vue3 项目中,element-plus 的日期选择器 el-date-picker 默认是英文版的,如下: 页面引入: //引入汉化语言包 import locale from "element-plus/lib/locale/lang/zh-cn" import { ElDatePicker, ElButton, ElConfigP…...
剑指 Offer(第2版)面试题 35:复杂链表的复制
剑指 Offer(第2版)面试题 35:复杂链表的复制 剑指 Offer(第2版)面试题 35:复杂链表的复制解法1:模拟 剑指 Offer(第2版)面试题 35:复杂链表的复制 题目来源&…...
自定义指令Custom Directives
<script setup langts> import { ref } from "vue"const state ref(false)/*** Implement the custom directive* Make sure the input element focuses/blurs when the state is toggled* */ // 以v开头的驼峰式命名的变量都可以作为一个自定义指令 const VF…...
预测性维护对制造企业设备管理的作用
制造企业设备管理和维护对于生产效率和成本控制至关重要。然而,传统的维护方法往往无法准确预测设备故障,导致生产中断和高额维修费用。为了应对这一挑战,越来越多的制造企业开始采用预测性维护技术。 预测性维护是通过传感器数据、机器学习和…...
华为、新华三、锐捷常用命令总结
华为、新华三、锐捷常用命令总结 一、华为交换机基础配置命令二、H3C交换机的基本配置三、锐捷交换机基础命令配置 一、华为交换机基础配置命令 1、创建vlan: <Quidway> //用户视图,也就是在Quidway模式下运行命令。 <Quidway>system-view…...
链路追踪详解(四):分布式链路追踪的事实标准 OpenTelemetry 概述
目录 OpenTelemetry 是什么? OpenTelemetry 的起源和目标 OpenTelemetry 主要特点和功能 OpenTelemetry 的核心组件 OpenTelemetry 的工作原理 OpenTelemetry 的特点 OpenTelemetry 的应用场景 小结 OpenTelemetry 是什么? OpenTelemetry 是一个…...
Node.js 工作线程与子进程:应该使用哪一个
Node.js 工作线程与子进程:应该使用哪一个 并行处理在计算密集型应用程序中起着至关重要的作用。例如,考虑一个确定给定数字是否为素数的应用程序。如果我们熟悉素数,我们就会知道必须从 1 遍历到该数的平方根才能确定它是否是素数ÿ…...
python matplotlib 三维图形添加文字且不随图形变动而变动
要在三维图形中添加文字并使其不随图形变动而变动,可以使用 annotate() 方法。这个方法可以在三维图形中添加文字,并且可以指定文字的位置、对齐方式和字体大小等属性。 下面是一个示例代码,演示如何在三维图形中添加文字: impo…...
Ubuntu设置kubelet启动脚本关闭swap分区
查看swap分区 swapon -s打开swap分区 swapon -a查看/etc/fstab下所有固化的swap分区,注释 vi /etc/fstab修改kubelet.conf文件 vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf添加 ExecStartPre/sbin/swapoff -a生效 systemctl daemon-reload sys…...
MySQL数据库存储
MySQL数据库存储 MySQL数据库简介MySQL开发环境MySQL安装图形化界面工具Navicat使用 表的操作表的概念3.2 创建表3.3 修改表 数据的操作-增删改查4.1 增加数据4.2 删除数据4.3 修改数据4.4 查询数据4.4.1 基础查询4.4.2 分组查询和聚合函数4.4.4 having语句4.4.5 排序4.5 多表联…...
verilog语法进阶,时钟原语
概述: 内容 1. 时钟缓冲 2. 输入时钟缓冲 3. ODDR2作为输出时钟缓冲 1. 输入时钟缓冲 BUFGP verilog c代码,clk作为触发器的边沿触发,会自动将clk综合成时钟信号。 module primitive1(input clk,input a,output reg y); always (posed…...
案例069:基于微信小程序的计算机实验室排课与查询系统
文末获取源码 开发语言:Java 框架:SSM JDK版本:JDK1.8 数据库:mysql 5.7 开发软件:eclipse/myeclipse/idea Maven包:Maven3.5.4 小程序框架:uniapp 小程序开发软件:HBuilder X 小程序…...
C语言:将三个数从大到小输出
#include<stdio.h> int main() {int a 0;int b 0;int c 0;printf("请输入abc的值:");scanf_s("%d%d%d", &a, &b, &c);if (b > a){int tmp a;a b;b tmp;}if (c > a){int tmp a;a c;c tmp;}if (b < c){int t…...
基于Hadoop的铁路货运大数据平台设计与应用
完整下载:基于Hadoop的铁路货运大数据平台设计与应用 基于Hadoop的铁路货运大数据平台设计与应用 Design and Application of Railway Freight Big Data Platform based on Hadoop 目录 目录 2 摘要 3 关键词 4 第一章 绪论 4 1.1 研究背景 4 1.2 研究目的与意义 5 …...
Java基础题2:类和对象
1.下面代码的运行结果是() public static void main(String[] args){String s;System.out.println("s"s);}A.代码编程成功,并输出”s” B.代码编译成功,并输出”snull” C.由于String s没有初始化,代码不能…...
冒泡排序学习
冒泡排序(Bubble Sort)是一种简单的排序算法,它通过重复地交换相邻的元素来排序。具体实现如下: 1. 从待排序的数组中的第一个元素开始,依次比较相邻的两个元素。 2. 如果前一个元素大于后一个元素,则交换…...
LeetCode(65)LRU 缓存【链表】【中等】
目录 1.题目2.答案3.提交结果截图 链接: LRU 缓存 1.题目 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 k…...
网站提示“不安全”
当你在浏览网站时,有时可能会遇到浏览器提示网站不安全的情况。这通常是由于网站缺乏SSL证书所致。那么,从SSL证书的角度出发,我们应该如何解决这个问题呢? 首先,让我们简单了解一下SSL证书。SSL证书是一种用于保护网站…...
【Linux】驱动
驱动 驱动程序过程 系统调用 用户空间 内核空间 添加驱动和调用驱动 驱动程序是如何调用设备硬件 驱动 在计算机领域,驱动(Driver)是一种软件,它充当硬件设备与操作系统之间的桥梁,允许它们进行通信和协同工作。驱动程…...
Java研学-HTML
HTML 1 介绍 HTML(Hypertext Markup Language) 超文本标记语言。静态网页,用于在浏览器上显示数据 超文本: 指页面内可以包含图片、链接,甚至音乐、程序等非文字元素。 标记语言: 使用 < > 括起来的语言 超文本标记语言的结构, 包括“头”部分&am…...
SpringBoot之响应的详细解析
2. 响应 前面我们学习过HTTL协议的交互方式:请求响应模式(有请求就有响应) 那么Controller程序呢,除了接收请求外,还可以进行响应。 2.1 ResponseBody 在我们前面所编写的controller方法中,都已经设置了…...
redis:四、双写一致性的原理和解决方案(延时双删、分布式锁、异步通知MQ/canal)、面试回答模板
双写一致性 场景导入 如果现在有个数据要更新,是先删除缓存,还是先操作数据库呢?当多个线程同时进行访问数据的操作,又是什么情况呢? 以先删除缓存,再操作数据库为例 多个线程运行的正常的流程应该如下…...
wordpress 目录扫描/自己创建网页
一、环境搭建 1、安装nodejs node - v :查看版本 npm -v :查看npm 的版本 2、安装cnpm 疑问:npm和cnpm 都是什么? npm(node package manager):nodejs的包管理器,用于node插件管理(包括安装、卸载、管…...
企业网站建设方案.doc/响应式网站 乐云seo品牌
elites alliance allies revert stewardship fringe orthodoxy creak incitement repudiate wrangle democrat credence filibuster petition disbar purge tumor ductal oncogenic mutant inflammation progenitor...
广州自助网站推广制作/企业官网seo
内联汇编使用“__asm”(C)和“asm”(C和C)关键字声明,语法格式如下所示,内联汇编支持大部分的ARM指令,但不支持带状态转移的跳转指令,如BX和BLX 指令 __asm("instruction[;instruction]&q…...
网站想建设子站/可以推广赚钱的软件
熟悉C的童鞋都知道,为了避免“野指针”(即指针在首次使用之前没有进行初始化)的出现,我们声明一个指针后最好马上对其进行初始化操作。如果暂时不明确该指针指向哪个变量,则需要赋予NULL值。除了NULL之外,C…...
优秀网站建设方案/nba最新新闻新浪
因为txt默认的选项是ANSI,即GBK编码。GBK和GB2312都是中文编码,先解释一下两者的区别。总体说来,GBK包括所有的汉字,包括简体和繁体。而gb2312则只包括简体汉字。GBK编码是中国大陆制订的、等同于UCS的新的中文编码扩展国家标准。…...
wordpress 鲜果/深圳高端网站建设公司
第一种方法,你可以使用xlsread函数来读取excel中的数据第二种方法,就是把字符转化为数字,使用函数str2numxlsread的使用EXAMPLES:1. Default operation:NUMERIC xlsread(FILE);[NUMERIC,TXT]xlsread(FILE);[NUMERIC,TXT,RAW]xlsread(FILE);2…...