fs模块
fs常用方法
const fs = require('fs')
// 读取文件
// fs.readFile(path[, options], callback)
// options:表示以什么编码格式读取文件(可选),不选择时输出为一个Buffer对象
// callback:文件读取完成后通过回调函数拿到读取的结果
fs.readFile('./files/1.txt', 'utf8', (err, dataStr) => {
if(err) {
return console.log(err) // 若读取成功 err = null,失败则输出一个错误对象
}
console.log(dataStr) // 若读取失败则为 undefined
})
// 写入内容
// fs.writeFile(file, data[, options], callback)
// file:文件路径字符串
// data:写入的内容
// options:表示以什么编码格式写入文件(可选),不选择时输出为一个Buffer对象
// callback:文件写入完成后的回调函数
// 注意点:
// 1. 写入文件时文件夹必须存在,文件可不存在,文件不存在时则新建文件
// 2. 重复调用时新写入的内容会覆盖之前的旧内容
fs.writeFile('./files/1.txt', 'Go!', 'utf8', (err) => {
if(err) {
return console.log(err) // 若写入成功 err = null,失败则输出一个错误对象
}
fs.readFile('./files/1.txt', 'utf8', (err, dataStr) => {
if(err) {
return console.log(err)
}
console.log('写入的数据为:' + dataStr)
})
})
fs路径动态拼接问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kehzyTDh-1641905820826)(D:\typora\image-20220108171708425.png)]
// __dirname 表示当前文件所处的目录
const fs = require('fs')
fs.readFile(__dirname + '/files/1.txt', 'utf8', (err, dataStr) => {
if(err) {
return console.log(err)
}
console.log(dataStr)
})
path路径模块
const path = require('path')
// 拼接完整路径字符串
// path.join()
const pathStr = path.join('/a', '/b/c', '../', '/d', 'e')
console.log(pathStr) // 输出 \a\b\d\e,其中'../'会抵消一层路径
const pathStr2 = path.join(__dirname, '/files/1.txt')
console.log(pathStr2) // 输出当前路径下所处目录的\files\1.txt
// 从路径字符串中解析文件名
// path.basename(path[, ext])
// ext为文件扩展名
const fpath = path.join(__dirname, '/files/2.txt')
let fullName = path.basename(fpath)
console.log(fullName) // 输出 2.txt
let nameWithoutExt = path.basename(fpath, '.txt')
console.log(nameWithoutExt) // 输出 2
// 获取路径中文件的扩展名
// path.extname(path)
let ext = path.extname(fpath)
console.log(ext) // 输出 .txt
http模块
URL中的80端口可以被省略,每个端口号不能被多个web服务占用
// 创建web服务器
const http = require('http')
// 创建web服务器实例
const server = http.createServer()
// 为服务器实例绑定request事件,访问此网页即触发此事件
// req请求对象:在事件处理函数中访问与客户端相关的数据或属性,如req.url 请求url地址(/后面的内容,包括'/') req.method 请求类型
// res响应对象:在事件处理函数中访问与服务器相关的数据或属性
server.on('request', (req, res) => {
console.log('Someone visit our web server!')
const str = `Your request url is ${req.url == '/'? '根目录': req.url}, and request method is ${req.method}.`
// 防止响应乱码
res.setHeader('Content-Type', 'text/html; charset=utf-8')
// 向客户端响应内容
res.end(str)
})
// 启动服务器
server.listen(80, () => {
console.log('http server running at http://127.0.0.1')
})
http动态响应内容
// 创建web服务器
const http = require('http')
const fs = require('fs')
// 创建web服务器实例
const server = http.createServer()
// 为服务器实例绑定request事件,访问此网页即触发此事件
server.on('request', (req, res) => {
const url = req.url
// 优化资源请求路径
let fpath = ''
if(url == '/'){
fpath = path.join(__dirname, './clock/index.html')
} else {
fpath = path.join(__dirname, './clock', url)
}
fs.readFile(fpath, 'utf8', (err, dataStr) => {
if (err) return res.end('<h1>404 Not found!</h1>')
res.end(dataStr)
})
})
// 启动服务器
server.listen(80, () => {
console.log('http server running at http://127.0.0.1')
})
Node.js模块
Node.js中的模块作用域
自定义模块中定义的变量、方法等成员只能在当前模块被访问,这种模块级别的访问限制称为模块作用域。
向外共享模块作用域中的成员需要使用module.exports对象
const username = 'wj'
function makeMoney() {
console.log('30w')
}
// 外界使用 require() 方法导入自定义模块时得到的就是 module.exports 所指向的对象
// 其中,使用 require() 方法加载模块属于 CommonJS 规范
module.exports = {
username,
makeMoney
}
module.exports.income = '30w'
module.exports.go = () => {
console.log('go!')
}
// 以上 module.exports 均可用 exports 替换,默认情况下两者指向同一个对象
// 重点:
// 最终共享的结果还是以 module.exports 指向的对象为准
// 注意点:
// 1.module.exports = {} 不能使用 exports 代替,因为如果使用 exports = {} 即将exports指向另外一个新的对象
// 2.如需混用最好只使用 module.exports.new1 和 exports.new2 的形式
包的语义化版本规范
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gym6xjzc-1641905820828)(D:\typora\image-20220110161005639.png)]
npm切换下包镜像源
npm config get registry
npm config set registry=https://registry.npm.taobao.org/
i5ting_toc将md文档转换为html页面的小工具
i5ting_toc -f '要转换的md文件路径' -o
模块的加载机制
模块在第一次加载后被毁缓存,多次调用 require() 不会导致模块的代码被执行多次。
内置模块的加载优先级最高。
使用 require() 加载自定义模块时必须指定与 ./ 或 …/ 开头的路径标识符,若没有 node 会把它当作内置模块或第三方模块及逆行加载。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LklMRlBx-1641905820828)(D:\typora\image-20220110194741040.png)]
express基础
创建web服务器
const express = require('express')
// 创建web服务器
const app = express()
// 启动web服务器
app.listen(80, () => {
console.log('express server running at http://127.0.0.1')
})
监听GET请求和响应res.send()
/* app.get('请求URL', (req,res) => {
// 处理函数
}) */
app.get('/user', (req,res) => {
res.send({name:'wj', income:'30w'})
})
app.post('/user', (req,res) => {
res.send('请求成功!')
})
获取URL中携带的查询参数 req.query
app.get('/', (req,res) => {
// req.query 查询的是客户端使用 ?name=wj&income=30w 这种字符串形式,默认情况下为 {}
res.send(req.query)
})
获取URL中携带的动态参数 req.params
app.get('/:id/:name', (req,res) => {
// req.params: URL地址中通过 :参数名 的形式,匹配动态参数值
// :id 中的 id 即为 req.params 对象的属性名
// 请求时直接写 http://http://localhost/256/wj
res.send(req.params) // 输出JSON对象 {"id":"256","name":"wj"}
})
托管静态资源 express.static()
// 创建一个静态资源服务器
// 访问时不用加 public 这一级,且文件夹可以不叫 public
// 也可有多个文件夹被托管,会根据目录的添加顺序查找所需要的文件
app.use(express.static('public'))
app.use(express.static('clock'))
// 可以在静态资源访问路径之前挂载路径前缀
// 之后如果需要访问 public 文件夹内的文件时必须添加 /public 前缀,前缀可以任意,一般使用目录名
app.use('/public', express.static('public')) // http://http://localhost/public/clock/index.html 才能访问
express路由
路由概念
express路由指的是客户端的请求与服务器处理函数之间的映射关系。
// app.METHOD(PATH, HANDLER)
app.get('/user', (req,res) => {
res.send({name:'wj', income:'30w'})
})
app.post('/user', (req,res) => {
res.send('请求成功!')
})
// 以上皆为路由
// 匹配时按照路由的顺序进行匹配
模块化路由
// 路由抽离为单独模块的步骤,以下为模块.js
// 创建路由模块对应的 .js 文件
const express = require('express')
// 调用 express.Router() 函数创建路由对象
const router = express.Router()
// 向路由对象上挂载具体的路由
router.get('/user/list', (req,res) => {
res.send({name:'wj', income:'30w'})
})
// 使用 moudule.exports 向外共享路由对象
router.post('/user/add', (req,res) => {
res.send('请求成功!')
})
// 使用 app.use() 函数注册路由模块
module.exports = router
// 调用
const express = require('express')
const app = express()
const userRouter = require('./router/router1')
app.listen(80, () => {
console.log('express server running at http://127.0.0.1')
})
// app.use()函数注册全局中间件
// 同时可添加路由前缀,访问时必须加上路由前缀
app.use('/api', userRouter)
// 请求时触发 userRouter 内的 get 和 post 方法
express中间件
中间件的作用:**多个中间件之间共享一份 req 和 res。**基于这样的特性可在上游的中间件中统一为 req 或 res 对象添加自定义的属性和方法,供下游的中间件或路由进行使用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cxh8Cla3-1641905820829)(D:\typora\image-20220110214414703.png)]
中间件本质:function处理函数
// 中间件函数的形参列表中必须包含next函数,而路由处理函数中只包含 req 和 res
app.get('/user', (req, res, next) => {
next()
})
// next() 表示流转关系转交给下一个中间件或路由
// 定义中间件函数
// 常量 mv 所指向的就是一个中间件函数
const mw = function(req, res, next){
// 中间件函数必须调用next()函数,表示把流转关系转交给下一个中间件或路由
next()
}
全局中间件
// 客户端发起任何请求,到达服务器之后都会触发的中间件叫做全局中间件
// 通过调用 app.use(中间件函数) 即可定义一个全局中间件
const mw = function(req, res, next){
// 中间件函数必须调用next()函数,表示把流转关系转交给下一个中间件或路由
next()
}
app.use(mw)
// 流转关系:经过一个中间件后再转交给下一个中间件,若无中间件则转交给路由
// 中间件之间按定义的先后顺序流转
// 简化写法
app.use((req, res, next) => {
next()
})
局部中间件
// 直接定义在路由中的中间件为局部中间件,只在当前路由中生效
app.get('/about', (req, res, next) => {
next()
},(req, res) => {
res.send('About Page')
})
// 可一次性定义多个局部中间件,执行顺序为定义先后顺序
app.get('/', mw1, mw2, (req, res) => {})
app.get('/', [mw1, mw2], (req, res) => {})
中间件的5个注意事项
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9LjP3Rfl-1641905820829)(D:\typora\image-20220111100358561.png)]
express中间件分类
除了错误级别的中间件其他均需在路由之间注册中间件!!!
应用级别的中间件
// 通过 app.use() 或 app.get() 或 app.post(),绑定到app实例上的中间件都叫做应用级别的中间件
app.get('/about', (req, res, next) => {
next()
},(req, res) => {
res.send('About Page')
})
app.use((req, res, next) => {
next()
})
路由级别的中间件
// 绑定到 express.Router() 实例上的中间件叫做路由级别的中间件,express.Router() 为路由实例
const express = require('express')
const app = express()
const router = express.Router()
router.use((req, res, next) => {
next()
})
app.listen(80, () => {
console.log('http://127.0.0.1')
})
错误级别的中间件
注意!!!错误级别中间件必须注册在所有路由之后!!!
// 专门捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题
// 且错误级别中间件必须包含四个形参
app.get('/', (req, res) => {
throw new Error('Throw a error!')
res.send('Home page.')
})
app.use((err, req, res, next) => {
res.send('Error! ' + err.message)
})
express内置中间件
// 1.express.static 快速托管静态资源的内置中间件,无兼容性问题
app.use(express.static('public'))
// 2.express.json 解析JSON格式的请求体数据,4.16.0+ 版本才可使用
app.use(express.json())
app.post('/user', (req, res) => {
// 在服务器端可以使用 req.body 这个属性来接收客户端发送过来的请求体数据
// 默认情况下不加此中间件默认 req.body = undefined
console.log(req.body)
res.send('ok')
})
// 3.express.urlencoded 解析 URL-encoded 格式的请求体数据,4.16.0+ 版本才可使用
// URL-encoded 是一种键值对形式的表单格式
app.use(express.urlencoded({ extended: false }))
app.post('/book', (req, res) => {
// 默认情况下不加此中间件默认 req.body = {}
console.log(req.body)
res.send('ok')
})
注:
使用postman发送JSON格式请求体数据:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8BMbjPjT-1641905820830)(D:\typora\image-20220111110129048.png)]
使用postman发送URL-encoded 格式请求体数据:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TwyFb0Hi-1641905820830)(D:\typora\image-20220111110100607.png)]
第三方中间件
在 express@4.16.0 以前使用 body-parser 第三方中间件解析请求体数据。
const parser = require('body-parser')
// 解析 URL-encoded 格式的请求体数据
app.use(parser.urlencoded({ extended: false }))
自定义中间件
// 模块化 --> 中间件.js
// node.js 内置了 querystring 模块,parse() 专门用来查询字符串。
const qs = require('querystring')
function bodyParser(req, res, next) {
// 监听 req 对象的 data 时间,来获取客户端发送到服务器的数据
// 若数据量较大无法一次性发完,则客户端会将数据切割后分批发送到服务器端,所以 data 事件可能会触发多次
// 每次触发获取到的数据只是完整数据的一部分,需要手动对接收到的数据进行拼接
let str = ''
req.on('data', (chunk) => {
// 拼接请求体数据,隐式转换为字符串
str += chunk
})
// 当请求体数据接收完毕后会自动触发 req 的 end 事件
req.on('end', () => {
// 此时 str 为完整请求体数据
const body = qs.parse(str)
req.body = body
next()
})
}
module.exports = {
bodyParser
}
/*-----------------------------------------------------*/
// 调用 --> main.js
const express = require('express')
const app = express()
const myBodyParser = require('./diymw')
app.use(myBodyParser.bodyParser)
app.post('/user', (req, res) => {
res.send(req.body)
})
app.listen(80, () => {
console.log('http://127.0.0.1')
})
使用express写接口
GET接口
// 模块 --> apiRouter.js
const express = require('express')
const apiRouter = express.Router()
apiRouter.get('/get', (req, res) => {
const query = req.query
res.send({
status: 0, // 状态,0表示成功,1表示失败
msg: 'GET请求成功', // 状态描述
data: query // 需要相应给客户端的具体数据
})
})
module.exports = apiRouter
POST接口
const express = require('express')
const apiRouterPost = express.Router()
apiRouterPost.post('/post', (req, res) => {
const body = req.body
res.send({
status: 0, // 状态,0表示成功,1表示失败
msg: 'POST请求成功', // 状态描述
data: body // 需要响应给客户端的具体数据
})
})
module.exports = apiRouterPost
调用接口
const express = require('express')
const app = express()
const apiRouterGet = require('./apiRouter/apiRouterGet.js')
const apiRouterPost = require('./apiRouter/apiRouterPost.js')
// 注意:获取请求体数据时需要使用中间件对其解析
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.use('/api', apiRouterGet)
app.use('/api', apiRouterPost)
app.listen(80, () => {
console.log('http://127.0.0.1')
})
解决跨域问题
// 协议、域名或端口号任一项不同就存在跨域问题
// 解决方案
// 1.CORS(跨域资源共享)中间件,一定在路由前注册
// CORS本质就是一些列HTTP响应头
// CORS具有兼容性问题,只有支持 XMLHttpRequest Level2 的浏览器才能正常访问开启了 CORS 的服务端接口
const cors = require('cors')
app.use(cors())
/*
扩展:
1.CORS 响应头部 Access-Control-Allow-Origin
Access-Control-Allow-Origin: <origin> | *
其中,origin 参数的值指定了允许访问该资源的外域URL
res.setHeader('Access-Control-Allow-Origin', 'http://www.baidu.com')
2.CORS 响应头部 Access-Control-Allow-Header
默认情况下,CORS 仅支持客户端向服务器发送如下的 9 个请求头:
Accept, Accept-Language, Content-Language, DPR, Downlink, Sava-Data, Viewport-Width, Width, Content-Type
且值仅限于 text/plain, multipart/form-data, application/x-www-form-urlencoded 三者之一
如果客户端向服务器发送额外的请求头信息,则需要在服务器端通过 Access-Control-Allow-Header 对额外的请求头进行声明,否则此次请求失效
res.setHeader('Access-Control-Allow-Header', 'Content-Type, X-Custom-Header')
3.CORS 响应头部 Access-Control-Allow-Methods
默认情况下 CORS 仅支持客户端发起 GET、POST、HEAD 请求
如果需要通过 PUT、DELETE 等方式请求服务器的资源则需要通过 Access-Control-Allow-Methods来指明允许使用的 HTTP 方法
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, DElETE, HEAD')
res.setHeader('Access-Control-Allow-Methods', '*')
4.CORS分类
简单请求:GET、POST、HEAD三者之一,且头部信息不超过以下几种并无自定义头部字段
Accept, Accept-Language, Content-Language, DPR, Downlink, Sava-Data, Viewport-Width, Width, Content-Type
预检请求:
(1) GET、POST、HEAD之外的请求
(2) 请求头中包含自定义头部字段
(3) 向服务器发送了 application/json 格式的数据
在浏览器与服务器正式通信之前,浏览器会先发送OPTION请求进行预检,以获知服务器是否允许该实际请求,所以这一次的 OPTION 请求称为“预检请求”。
服务器成功响应预检请求后才会发送真正的请求,并且携带真实数据。
*/
// 2.JSONP(只支持 GET 请求)
// 概念:浏览器端通过 <script> 标签的 src 属性请求服务器上的数据,同时,服务器返回一个函数的调用,这种方式称为JSONP
// jsonp 接口
app.get('/api/jsonp', (req, res) => {
// 获取客户端发送过来的回调函数名字
const funcName = req.query.callback
// 得到要通过 JSONP 形式发送给客户端的数据
const data = { name: 'wj', age: 25 }
// 根据前两步得到的数据,拼接处一个函数调用的字符串
const scriptStr = `${funcName}(${JSON.stringify(data)})`
// 把上一步拼接得到的字符串响应给客户端的 <script> 标签进行解析执行
res.send(scriptStr)
})
/*---------------------------------------------------*/
// jQuery 中 JSONP 请求
$('#btnJSONP').on('click', function () {
$.ajax({
method: 'GET',
url: 'http://127.0.0.1/api/jsonp',
dataType: 'jsonp',
success(res) {
console.log(res)
}
})
})
MySQL数据类型和字段
DataType数据类型:
(1)int 整形
(2)varchar(len) 字符串
(3)tinyint(1) 字符串
字段的特殊表示:
(1)PK(Primary Key)主键、唯一标识
(2)NN(Not Null)值不允许为空
(3)UQ(Unique)值唯一
(4)AI(Auto Increment)值自动增长
SQL语句
SELECT
-- 从 FROM 指定的【表中】,查询出【所有】的数据
-- SELECT * FROM 表名称
SELECT * FROM users;
-- 从 FROM 指定的【表中】,查询出指定【字段】的数据
-- SELECT 列名称 FROM 表名称
SELECT username, password FROM users;
-- 使用 WHERE 查询
-- SELECT 列名称 FROM 表名称 WHERE 列 运算符 值
SELECT * FROM users WHERE id <> 4;
-- 使用 AND 和 OR 运算符
SELECT * FROM users WHERE id <> 2 AND username = 'wj';
SELECT * FROM users WHERE id > 2 OR id < 2;
-- SQL语句中关键字(SELECT、FROM等)对大小写不敏感
INSERT INTO
-- 向指定的表中插入如下几列数据,列的值通过 values 一一指定
-- 列和值要一一对应,多个列和多个值之间使用英文逗号隔开
-- INSERT INTO table_name(列1, 列2, ...) VALUES (值1, 值2, ...)
INSERT INTO user (username, password) VALUES ('lmj', '7856');
UPDATE
-- 指定要更新哪个表中的数据
-- 用 SET 指定列对应的新值
-- 用 WHERE 指定更新的条件
-- UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值
UPDATE users SET password = '123789', status = 1 WHERE username = 'lmj01';
DELETE
-- 从指定表中根据 WHERE 条件删除对应的数据行
-- DELETE FROM 表名称 WHERE 列名称 = 值
DELETE FROM users WHERE username = 'temp';
ORDER BY
-- ORDER BY 默认进行升序排序
-- ASC 关键字代表升序排序
SELECT * FROM users ORDER BY status;
SELECT * FROM users ORDER BY status ASC;
-- DESC 降序排序
SELECT * FROM users ORDER BY status DESC;
-- 多重排序
-- 先按照 status 进行降序排序,再按照 username 的字母排序进行升序排序
SELECT * FROM users ORDER BY status DESC, username ASC;
COUNT(*)
-- 查询 users 表中 status 为 0 的总数据条数
SELECT COUNT(*) FROM users WHERE status = 0;
AS
-- 将列名称从 COUNT(*) 修改为 total
SELECT COUNT(*) AS total FROM users WHERE status = 0;
-- 普通的列也可以
SELECT username AS uname, password as pwd FROM users WHERE status = 0;