当前位置: 首页 > news >正文

Node.js后端学习笔记:Express+MySQL

目录

  • 一、前置知识:Node的内置模块
    • fs文件系统模块
    • path路径模块
    • http模块
  • 二、开发规范:模块化
    • Node.js中的模块分类(三类)
    • 模块加载
    • 模块导出
    • CommonJS模块化规范
    • npm包
  • 三、轻量级框架:Express
    • Express的基本使用
    • Express托管静态资源
    • Express路由
    • 模块化路由
    • 中间件
    • 中间件分类
    • 自定义中间件
    • 使用Express写接口
    • CORS跨域资源共享
    • 简单请求和预检请求
    • JSONP接口
  • 四、数据库:MySQL
    • 数据库类型
    • 传统型数据库的组织结构
    • 安装并配置MySQL
    • 在项目中操作MySQL
    • 对数据的标记删除
  • 五、开发模式:前后端的身份认证
    • Web开发模式
    • 身份认证
      • Session认证机制
      • JWT认证机制

一、前置知识:Node的内置模块

fs文件系统模块

// 1、导入模块
const fs = require('fs')// 2、读取文件内容
fs.readFile(path,[option],callback)
// option:可选参数,表示一扇门编码格式来读取文件
// 示例
fs.readFile('./files/11.txt','utf8',function(err,dataStr){})// 3、写入文件内容
fs.writeFile(file,data,[options],function(err){})// 4、__dirname表示当前文件所在的目录:用于动态拼接文件路径

path路径模块

// 1、导入模块
const path = require('path')// 2、路径拼接
path.join([...paths])
// 凡是涉及到路径拼接的操作都需要用join方法处理// 3、获取路径中的文件名
path.basename(path,[ext])
// 不指定ext之后会输出完整的文件名,指定之后只输出不带后缀的文件名// 4、获取路径中的文件扩展名
path.extname(path)

http模块

// 1、导入模块
const http = require('http')// 服务器上安装了web 服务器软件(如Apache)实现了服务器功能
// Node.js直接通过http模块就能创建一个服务器对外提供服务
// 2、创建web服务器
const server = http.createServer()// 3、绑定request事件,监听网络请求
server.on('server',(req,res) => {})// 4、启动服务器
server.listen(port,() => {})// 5、req对象包含与客户端相关的属性和数据,如:
// req.url是客户端请求的 URL 地址
// req.method是客户端的请求方法// 6、res对象包含与服务器相关的数据和属性
// res.end方法用于向客户端发送指定内容,并结束这次请求// 7、解决中文乱码问题
// 问题:服务端向客户端发送的中文内容时会出现乱码问题
// 解决:手动设置内容的编码格式(设置在响应操作之前)
res.setHeader('Content-Type','text/html;charset=utf-8')

二、开发规范:模块化

Node.js中的模块分类(三类)

  • 内置模块(详见第一章)
  • 自定义模块 - 用户创建的每个.js文件,都是自定义模块
  • 第三方模块

模块加载

  • 注意点:使用require方法加载模块时回执行被加载模块中的代码

模块导出

  • 每个.js自定义模块中都有一个module对象,内部存储了和当前模块有关的信息
  • 使用module.exports指定的对象为该模块最终导出的module对象

CommonJS模块化规范

  • 模块内module变量代表当前模块
  • module变量是一个对象,其exports属性是对外接口
  • 使用require方法加载模块时其实是加载module.exports属性

npm包

  • 从https://www.npmjs.com/网站上搜索自己所需要的包
  • 从https://registry.npmjs.org/服务器上下载自己需要的包
  • node_modules 文件夹用来存放所有已安装到项目中的包。require() 导入第三方包时,就是从这个目录中查找并加载包。
  • package-lock.json 配置文件用来记录 node_modules 目录下的每一个包的下载信息,例如包的名字、版本号、下载地址等。
  • i5ting_toc 是一个可以把 md 文档转为html 页面的小工具

三、轻量级框架:Express

Express的本质就是一个npm包,其内部封装了Node的内置http模块,用于快速创建Web服务器(包括网页资源和API接口)

Express的基本使用

const express = require('express')
// 1、创建 web 服务器
const app = express()// 2、启动服务器
app.listen(port, () => {})// 3、监听请求
app.get(url, (req,res) => {})
app.post(url, (req,res) => {})// 4、响应请求
res.send(...响应内容)// 5、获取参数
req.query // 查询参数  --  url后面通过 ? 拼接的参数
req.params // 动态参数  --  url后面通过 :拼接的参数

Express托管静态资源

// express提供了一个函数来创建一个静态资源服务器
app.use(express.static('public'))
// 此时通过http://localhost:3000/images/bf.jpg 可以访问public下的静态资源(路径无需添加public)
// 常见需要托管的静态资源包括图片、CSS文件、JS文件
// 多次调用可以同时托管多个静态资源目录// 如需挂载目录前缀
app.use('/public',express.static('public'))

Express路由

在Express中,路由指的是客户端请求与服务器处理函数之间的映射关系

路由匹配过程:当一个请求到达服务器之后,需要经过路由的匹配(按照定义的先后顺序),当请求类型和URL同时满足时就会调用对应的回调函数处理

  • 重点一:同时满足请求类型和URL相同
  • 重点二:按照先后顺序匹配

模块化路由

将路由以模块的拆分的话容易管理,具体步骤如下:

  • 创建单独的模块.js文件
  • 调用express.Router()函数创建路由对象
  • 向路由对象上挂载具体的路由
  • 使用module.exports向外共享路由对象
  • 使用app.use()注册路由模块
// 1.导入 express
var express = require('express')
// 2.创建路由对象
var router = express.Router()// 3.挂载获取用户列表的路由
router.get('/user/list'function(req,res){ res.send('Get user list.')
})// 4.挂载添加用户的路由
router.post('/user/add'function(req,res){ res.send('Add new user.')
})// 5.向外导出路由对象
module.exports = router
// 1、导入路由模块
const userRouter = require('./router/user.js')// 2、注册路由模块(并添加统一路径访问前缀)
app.use('/api', userRouter)

中间件

中间件本质就是一个函数,多个中间件之间共享一份req和res

  • 可以在上游的中间件中统一为req和res对象添加自定义的属性和方法

其调用流程:请求 - > 中间件1 - > 中间件2 - > 响应

调用过程必须使用next()函数将流转关系交给下一个中间件或路由!

// 全局中间件(通过调用app.use定义)
app.use((req,res,next) => {console.log('这是全局生效的中间件')next()
})// 局部生效的中间件(不使用app.use定义)
const mw1 = (req,res,next) => {console.log('这是局部中间件')next()
}
app.get('/', mw1, (req,res) => {})

中间件分类

// 1、应用级别的中间件(绑定到app实例上的中间件,包括全局和局部中间件)
app.use((res,res,next) => {next()
})-----------------------------------------------------------
// 2、路由级别的中间件(绑定到router实例上的中间件)
const router = express.router()
router.use((res,res,next) => {next()
})-----------------------------------------------------------
// 3、错误级别的中间件
// 作用:专门用来捕获整个项目中发生的异常错误,防止代码崩溃,形参包括(err,req,res,next)
app.use((err,res,res,next) => {console.log('服务器出错!')res.send('Error:' + err.message)
})
// 注意:错误级别的中间件必须注册在所有的路由之后-----------------------------------------------------------
// 4、Express内置的中间件(三个常用的,提升开发效率)
// 4.1、express.static 快速托管HTML文件、图片、CSS样式等静态资源
// 托管public目录下的静态资源(增加访问前缀)
app.use('/public',express.static('public'))// 4.2、express.json 解析JSON格式的请求体数据
// 配置解析 application/json 格式数据的内置中间件
app.use(express.json())// 4.3、express.urlencoded 解析Url-encoded格式的请求体数据(表单默认的提交格式)
// 如:username=%E5%BC%A0%E4%B8%89&email=zhangsan%40example.com&password=123456
// 解码后:username=张三&email=zhangsan@example.com&password=123456
// 配置解析 application/x-www-form-urlencoded 格式数据的内置中间件
app.use(express.urlencoded({ extended:false }))-----------------------------------------------------------
// 5、第三方中间件(需要安装,介绍常用的)
// body-parser  --  解析请求体数据
// 步骤一:使用 npm install body-parser 安装中间件
// 步骤二:使用 require 导入中间件
// 步骤三:调用 app.use() 注册中间件
// Express 内置的 express.urlencoded 中间件,就是基于body-parser 这个第三方中间件进一步封装出来的

自定义中间件

手动模拟一个类似于express.urlencoded这样的中间件,来解析POST 提交到服务器的表单数据(封装成模块)

// custom-body-parser.js 模块
const qs = require('querystring') // Node的内置模块,parse方法用于解析字符串为对象格式
function bodyParser(req,res,next){// 监听req的data事件(客服端发送数据就会触发,客户端可能会分割发送,data会多次触发,需拼接数据)let str = ''req.on('data', (chunk) => {str+ = chunk})// 请求体发送完毕会触发req的end事件req.on('end', () => {const body = qs.parse(str)req.body = body // 挂载到req.body,这样下游函数看到的就是对象格式的数据啦next()})
}
module.exports = bodyParser// -------------------------分割线-------------------------
// index.js
const myBodyParser = require('custom-body-parser')
app.use(myBodyParser)

使用Express写接口

// app.js
const express = require('express')
const cors = require('cors')
const apiRouter = require('./apiRouter')
const app = express()// 解决跨域问题:1、CORS中间件(记得安装);2、JSONP(仅支持GET请求)
app.use(cors())
app.use('/api', apiRouter)app.listen(80, () => {console.log('Express server running at localhost')
})// --------------------分割线----------------------------------
// apiRouter.js [路由模块]
const express = require('express')
const apiRouter = express.Router()apiRouter.get('/get', (req,res) => {const query = req.queryres.send({status: 0,  // 0成功,1失败msg: 'GET请求成功!',data: query})
})apiRouter.post('post', (req,res) => {const body = req.bodyres.send({status: 0,msg: 'POST请求成功!',data: body})
})module.exports = apiRouter

CORS跨域资源共享

Cross-Origin Resource Sharing 由一系列的HTTP响应头组成,这些响应头告诉浏览器不要阻止前端JS代码跨域获取资源(浏览器的同源安全策略会阻止网页跨域访问服务器资源)

  • Access-Control-Allow-Origin<origin> | * --> 指定允许访问该资源的外域URL,正常是通配符*

  • Access-Control-Allow-Header --> 默认CORS支持客户端向服务器发送如下九个请求头:

    Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width 、 Content-Type (请求头的值仅限于 text/plain、multipart/form-data、application/x-www-form-urlencoded 三者之一)
    如果需要发送额外的请求头信息,需要在这里配置!

  • Access-Control-Allow-Methods --> 默认CORS支持客户端向服务器发起GET、POST、HEAD 请求,如果客户端希望发起PUT、DELETE等请求,需要在这里配置

配置方法:

res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Headers', 'Content-Type,X-Custom-Header')
res.setHeader('Access-Control-Allow-Methods', '*')

注意:Axios 会根据你发送的 data 的类型,智能地设置一个最合适的 Content-Type。虽然 Axios 很智能,但为了代码的清晰和可预测性,显式地设置 headers 是一个好习惯,尤其是在发送 JSON 时。

关于Access-Control-Allow-Headers详细解释:

如上面所言,默认CORS支持客户端向服务器发送如下九个请求头,每个请求头的值仅限于三者之一
如果请求头的值位于三者之外,就需要在Access-Control-Allow-Headers中声明!

比如我们最常用的最常用的是 Content-Typeapplication/json,但是application/json并不是三者之一,所以需要显示在Access-Control-Allow-Headers带上Content-Type,表示该请求头允许任何值!

简单请求和预检请求

客户端在请求CORS接口时,同时满足以下情况为简单请求,否则为非简单请求:

  • 请求方式为GET、POST、HEAD三者之一
  • HTTP头部信息不超过以下字段:无自定义头部字段、AcceptAccept-LanguageContent-LanguageDPRDownlinkSave-DataViewport-WidthWidthContent-Type(且值只能是以下三个application/x-www-formurlencodedmultipart/form-datatext/plain

简单请求:客户端直接发送真实请求

预检请求:

一旦确定为非简单请求,浏览器就在正式请求之前向服务器发送OPTION预检请求,服务器响应时告知浏览器服务器允许的请求方法和请求头。如果响应成功的话浏览器才会发起正式请求!

JSONP接口

浏览器通过<script>标签的src属性,请求服务器上的数据,同时服务器返回一个函数的调用。这种请求数据的方式就是JSONP。其支持跨域请求数据,但是只支持GET请求方式!

前端通过JSONP请求的接口只能是后端声明的JSONP接口,为了避免该接口也被处理CORS接口,后端声明JSONP接口需要在注册CORS之前进行!

// 在CORS中间件注册之前声明JSONP接口
app.get('/api/jsonp', (req,res) => {// 1、获取客户端发送过来的回调函数的名字const funcName = req.query.callback// 2、得到要通过JSONP格式发送给客户端的数据const data = { name: 'xs', age: '20' }// 3、拼接除一个函数调用的字符串const scriptStr = `${funcName}(${JSON.stringify(data)})`// 4、响应回客户端res.send(scriptStr)
})// 然后再注册CORS中间件(后续接口都会被处理成CORS接口)
app.use(cors())
// 前端调用
// 1. 定义一个全局函数作为回调函数
function handleJsonpResponse(data) {console.log('收到来自JSONP接口的数据:', data);alert(`姓名:${data.name},年龄:${data.age}`);// 在这里你可以处理数据,例如更新DOM等
}function getData() {// 2. 动态创建script标签const script = document.createElement('script');// 3. 设置src属性,指定接口地址并传入回调函数名script.src = 'http://your-backend.com/api/jsonp?callback=handleJsonpResponse';// 4. 将script标签添加到body中,请求开始document.body.appendChild(script);// 5. (可选)请求完成后移除script标签,避免污染DOMscript.onload = function() {document.body.removeChild(script);};
}

四、数据库:MySQL

数据库类型

  • MySQL - 传统型数据库(又叫关系型数据库、SQL数据库),还包括Oracle、SQL Server
  • Mongodb:新型数据库(又叫非关系型数据库、NoSQL数据库)

传统型数据库的组织结构

  • 数据库(database)、数据表(table)、数据行(row)、字段(field)

安装并配置MySQL

  • 安装MySQL Server:专门用来提供数据存储和服务的软件
  • 安装MySQL Workbench:可视化的MySQL管理工具
  • 查看mysql服务是否正在运行
    • Win + R,输入 services.msc 打开“服务”窗口。
    • 在服务列表中找到以 MySQL 开头的服务(如 MySQL80)。查看其“状态”是否为“正在运行”

在项目中操作MySQL

使用MySQL数据库的第三方模块mysql连接MySQL数据库,同时可以通过SQL语句操作数据库

// 先安装:npm install mysql
// 1、导入mysql模块
const mysql = require('mysql')// 2、建立于MySQL数据库的连接
const db = mysql.createPool({host: '127.0.0.1',  // 数据库的IP地址user: 'root',  // 数据库账号password: 'admin123',  // 数据库密码database: 'my_db_01' // 指定要操作的数据库
})// 3、测试mysql是否可以工作
db.query('SELECT 1', (err,result) => {if(err){return console.log(err)}console.log(result) // 只要能打印出[ RowDataPacker {'1':1} ]的结果,说明连接正常
})
// ------------------------------------------------------------------// 4、查询数据
db.query('SELECT * FROM users', (err,result) => {// 查询失败if(err) return console.log(err.message)// 查询成功console.log(result)
})
// ------------------------------------------------------------------// 5、向user表中插入数据
const user = { username: 'lys', password: 'lys123'}
const sqlStr = 'INSERT INTO users (username,password) VALUES (?,?)'  // ? 表示占位符
// 简化写法:占位符(?,?)替换为? , [user.username,user.password] 替换为user对象
db.query(sqlStr, [user.username,user.password], (err,result) => {// 插入失败if(err) return console.log(err.message)// 插入成功if(results.affectedRows === 1){console.log('插入数据成功')}
})
// ------------------------------------------------------------------// 6、更新表数据
const user = { id: 1, username: 'lzc', password: '000'}
const sqlStr = 'UPDATE users SET username=?,password=? WHERE id=?'
// 数组数据顺序与SQL语句的?占位符需要一致
db.query(sqlStr, [user.username,user.password,user.id], (err,result) => {// 更新失败if(err) return console.log(err.message)// 更新成功if(results.affectedRows === 1){console.log('更新数据成功')}
})
// ------------------------------------------------------------------// 7、删除数据
const sqlStr = 'DELETE FROM users WHERE id=?'
db.query(sqlStr, 3, (err,result) => {// 删除失败if(err) return console.log(err.message)// 删除成功if(results.affectedRows === 1){console.log('删除数据成功')}
})

对数据的标记删除

使用DELETE语句会将数据真正的从表中删除。为了保险起见,推荐使用标记删除的形式,来模拟删除的动作!

所谓标记删除,就是在表中设置类似于status这样的状态字段,来标记当前这条数据已经删除!
当用户执行删除动作时,我们并没有执行DELETE语句把数据删除掉,而是执行了UPDATE语句,将这条数据对应的status字段标记为删除即可!

db.query('UPDATE USERS SET status=1 WHERE id=?', 3, (err,result) => {// 删除失败if(err) return console.log(err.message)// 删除成功if(results.affectedRows === 1){console.log('删除数据成功')}
))

五、开发模式:前后端的身份认证

Web开发模式

1、基于服务端渲染的Web开发模式:

服务端发送给客户端的页面,是在服务器通过字符串的拼接、动态生成的。因此客户端不需要使用AJAX这样的技术额外请求页面的数据

app.get('/index.html', (req,res) => {// 1、要渲染的数据const user = { name: 'xs', age: 20 }// 2、服务端通过字符串的拼接动态生成HTML内容const html = `<h1>姓名:${user.name},年龄:${user.age}</h1>`// 3、把生成好的页面内容响应给客户端。因此,客户端拿到的是带有真实数据的页面res.send(html)
})
  • 优点:前端只需要直接渲染页面,耗时少。爬虫更容易获取信息,有利于SEO
  • 缺点:服务端完成HTML页面的内容拼接,占用服务端资源。不利于前后端分离,开发效率低

2、前后端分离的Web开发模式

  • 优点:开发体验好、用户体验好(Ajax技术实现页面局部刷新)、减小服务器压力
  • 缺点:不利于SEO。完整的页面在客户端动态生成没所以爬虫无法爬取页面的有效信息(解决方案:利用Vue、React等前端框架的SSR(server side render)技术解决SEO问题)

身份认证

不同的认证方案

  • 服务端渲染推荐使用Session认证机制
  • 前后端分离推荐使用JWT认证机制

Session认证机制

1、HTTP协议的无状态性

HTTP协议的无状态性指的是客户端每次的HTTP请求都是独立的,连续多次请求之间都没有直接的关系,服务器不会主动保留每次HTTP请求的状态,为了突破HTTP协议无状态的限制,推荐使用Cookie存储用户信息

2、什么是Cookie

Cookie是存储在浏览器中一段不超过4KB的字符串。它由一个名称(name)、一个值(value)和其他几个用于控制Cookie有效期、安全性、使用范围的可选属性组成。

不同域名下的Cookie各自独立,每当客户端发起请求时,会自动把当前域名下的所有未过期的Cookie一同发送到服务器

Cookie的几大特性

  • 自动发送
  • 不同域名的Cookie各自独立
  • 存在过期时限
  • 4KB限制

3、Cookie在身份认证的作用

客户端第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身份认证的Cookie,客户端会自动将Cookie保存在浏览器当中。

随后,当客户端浏览器每次请求服务器的时候,浏览器会自动将身份认证相关的Cookie,通过请求头的形式发送给服务器,服务器即可验明客户端身份!

4、Cookie具有不安全性

Cookie存储在浏览器中,并且浏览器提供了读写Cookie的API,因此Cookie容易被伪造,不具有安全性。因此不建议将隐私数据存储在Cookie中

5、Session的工作原理
在这里插入图片描述

6、在Express使用Session认证

// 安装中间件:npm install express-session
const session = require('express-session')// 注册中间件
app.use(session({secret: 'Keyboard cat', // secret属性的值可以是任意字符串resave: false,  // 固定写法saveUninitialized: trye  // 固定写法
}))// 登录接口(访问session对象,存储信息)
app.post('/api/login', (req,res) => {if(req.body.username !== 'admin' || req.body.password !== '000000'){resturn res.send({ status: 1, msg: '登陆失败!' })}req.session.user = req.body   // 将用户信息存储到session中res.session.islogin = true    // 将用户的登录状态存储到session中res.send({ status: 0, msg: '登陆成功!' })
})// 获取用户信息接口
app.get('/api/username', (req,res) => {if(!req.session.islogin) {return res.send({ status: 1, msg: 'fail' })}res.send({ status: 0, msg: 'success', username: req.session.user.username })
})// 退出登录接口(清空session)
app.post('/api/logout', (req,res) => {res.session.destory()  // 清空sessionres.send({status: 0,msg: '退出登录成功!'})
})

JWT认证机制

Session认证机制需要配置Cookie实现,而由于Cookie默认不支持跨域访问。所以当涉及到前端跨域请求后端接口的时候,需要做很多额外的配置,才能实现Session认证!

JWT(JSON Web Token)是目前最流行的跨域认证解决方案!

1、JWT的工作原理
在这里插入图片描述

2、JWT的组成部分

JWT通常由三部分组成,分别是头部Header、有效载荷Payload、签名Signature。三者之间通过“ . ”隔开,格式如下:

Header.Payload.Signature

3、JWT的三个部分各自代表的含义

  • Payload部分是真正的用户信息,它是用户信息经过加密之后生成的字符串
  • Header和Signature是安全性相关的部分,只是为了保证Token的安全性

4、JWT的使用方式

客户端收到JWT之后,通常会将它存储在localStorage或者sessionStorage中。

此后,客户端每次与服务端通信,都要带上这个JWT的字符串,从而进行身份验证,推荐的做法是把JWT放在HTTP请求头的Authorization字符中,格式如下:

Authorization: Bearer <token>

5、在Express使用JWT

// 安装包 npm install jsonwebtoken express-jwt
// jsonwebtoken   用于生成JWT字符串
// express-jwt    用于将JWT字符串解析还原成JSON对象
// 1、导入
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')// 2、定义一个用于加密和解密的密钥secret(本质是一个字符串)
const srcretKey = 'shunhahaha NO1 ^_^'// 3、登陆成功之后加密
app.post('/api/login', (req,res) => {// ...// 登陆成功时响应res.send({ status: 0, msg: '登陆成功!',// sign方法生成jwt字符串,三个参数分别是:信息对象、加密密钥、配置对象token: jwt.sign({username: userInfo.username}, secretKey, { expire: '30s'})})
})// 4、调用有权限的接口时会自动解密
// 使用app.use()来注册中间件
// expressJWT({ secret: secretKey }) 就是用来解析Token的中间件
// .unless({ path:[/^\/api\//] }) 用来指定哪些接口不需要访问权限
app.use(expressJWT({ secret: secretKey }).unless({ path:[/^\/api\//] }))// 5、使用req.user获取用户信息
// 当配置express-jwt中间件之后,就可以在需要权限的接口通过req.user访问从JWT字符串中解析的信息了
app.get('/admin/getinfo', (req,res) => {// ...res.send({status: 0,msg: '获取用户信息成功!',date: res.user})
})// 6、捕获JWT解析失败的错误
// 当使用express-jwt解析Token时,如果Toeken不合法或者已经过期,会产生一个失败的错误
// 可以通过Express的错误中间件捕获错误并进行处理
app.use((err,req,res,next) => {// token解析失败错误if(err.name === 'UnauthorizationError'){return res.send({ status: 401, message: 'token无效!'})}// 其他错误res.send({ status: 500, message: '未知错误!' })
})
http://www.dtcms.com/a/393893.html

相关文章:

  • Ubuntu24.04 安装 禅道
  • StandardScaler,MinMaxScaler 学习
  • vscode+ssh连接server
  • 一文快速入门 HTTP 和 WebSocket 概念
  • Vue.js 项目创建指南
  • 核心策略、高级技巧、细节处理和心理
  • 算法优化的艺术:深入理解 Pow(x, n) 及其背后的思考
  • Projection Approximation Subspace Tracking PAST 算法
  • 容器化简单的 Java 应用程序
  • 【实证分析】上市公司并购数据dofile数据集(2005-2024年)
  • OceanBase备租户创建(三):通过带日志的物理备份恢复
  • OceanBase用户和权限管理
  • VMware Workstation Pro 虚拟机为 Ubuntu 18 配网教程
  • 城市自然资源资产离任审计试点DID
  • 算法日记---新动计划
  • Vue3水波纹指令:2025年Material Design交互新标准
  • Ansible-yum_repository模块
  • Java 单元测试(JUnit)与反射机制深度解析
  • Spring MVC 入门:构建 Web 应用的核心框架
  • C 语言核心关键字与数据结构:volatile、struct、union 详解
  • 【Elasticsearch面试精讲 Day 19】磁盘IO与存储优化
  • Linux信号机制详解
  • 【杂谈】-儿童友好型AI的未来之路
  • Docker+cpolar 实战:打造灵活可控的远程办公系统——容器化 RDP 远程桌面与多因子安全治理
  • docker远程主机启用TLS及其在JAVA工程的应用
  • docker 安装 Postgres 17.6
  • 【Linux命令从入门到精通系列指南】poweroff 命令详解:安全关机与强制断电实战指南
  • 【文件上传管理系统】实战详解 SpringBoot + Vue.js
  • 软考中级习题与解答——第八章_计算机网络(3)
  • 【每日一问】PFC电路有什么作用?