Node 学习

1、fs文件模块

  • fs.readFile()方法,用来读取指定文件中的内容
fs.readFile(path[,option],callback)
参数1:必选参数,表示文件路径
参数2:可选参数,表示声明编码格式来读取文件
参数3:必选参数,文件读取完成后,通过回调函数拿到读取的结果

例子

const fs = require('fs')

fs.readFile('./1.txt','utf8',function(err,dataStr){
	console.log(err)
	console.log('------------')
	console.log(dataStr)

})
  • fs.writeFile()方法,用来读取指定文件中的内容
const fs = require('fs')
fs.writeFile('./2.txt','abcd',function(err){
	console.log(err)
})
小红=23   小红:23
//将成绩的数组,按照空格进行分割
const arrOld = dataStr.split('')
//循环分割后的数组,对每一项数据,进行字符串的替换操作
const arrNew = []
arrOld.forEach(item => {
    arrNew.push(item.replace('=',':'))
})
//把新数组中的每一项,进行合并换行,得到一个新的字符串
const newStr = arrNew.join('\r\n')
  • 路径动态拼接

__dirname表示当前文件所处的目录

const fs = require('fs')

fs.readFile(__dirname+'/files/1.txt','utf8',function(err,dataStr){
	console.log(err)
	console.log('------------')
	console.log(dataStr)

})

2、path路径模块

  • path.join()方法,用来将多个路径片段拼接成一个完整的路径字符串

    const path = require('path')
    const pathStr = path.join('/a','/b/c','../','./d','e')
    console.log(pathStr)  //输出\a\b\d\e    ..\会抵消前一个路径
    
    
    fs.readFile(path.join(__dirname,'/files/1.txt'),'utf8',function(err,dataStr){
        
    })
    
  • path.basename()方法,用来从路径字符串中,将文件名解析出来

const path = require('path')

//定义文件的存放路径
const fpath = '/a/b/c/index.html'

const fullName = path.basename(fpath)
console.log(fullName)   //index.html

const fullName = path.basename(fpath,'html')
console.log(fullName)   //index
  • path.extname()获取文件扩展名
const path = require('path')

//定义文件的存放路径
const fpath = '/a/b/c/index.html'
const fext = path.extname(path)
console.log(fext)  //.html

3、综合案例

const fs = require('fs')
const path = require('path')
//匹配<style></style>标签的正则表达式
//   \s表示空白字符串   \S表示非空白字符  *表示匹配任意次
const regStyle = /<style>[\s\S]*<\/style>
const regScript = /<script>[\s\S]*<\/script>

//读取需要被处理的HTML文件
fs.readFile(path.join(__dirname,'./index.hrml'),'utf8',(err,dataStr) = >{
    if(err){
        return console.log('读取文件失败'+err.message)
    }
    resolveCSS(dataStr)
	resolveJS(dataStr)
	resolveHTML(dataStr)
})

//定义处理css样式的方法
function resolveCSS(htmlStr){
    //使用正则提取需要的内容
    const r1 = regStyle.exec(htmlStr)
    
    //将提取出来的样式字符串,进行字符串的replace操作
    const newCSS = r1[0].replace('<style>','').replace('</style>','')
    
    //调用fs.writeFile()方法,将提取的样式,写入到clock目录中 index.css 的文件里面
    
    fs.writeFile(path.join(__dirname,'./clock/index.css'),newCSS,function(err){
        if(err)return console.log('写入样式失败'+err.message)
        console.log('成功')
    })
}

4、http模块

//导入http模块
const http = require('http')
//创建web服务器实例
const server = http.createServer()
//为服务器实例,绑定request事件,监听客户端的请求
//req是请求对象,它包含了与客户端相关的数据和属性
//req.url 是客户端请求的URL地址
//req.methods是客户端的method请求类型
server.on('request',function(req,res){
    console.log('somone visit our web server')
})
//请求服务器
server.listen(8080,function(){
    console.log('server running at http://127.0.0.1:8080')
})

解决中文乱码问题

res.setHeader('Content-Type','text/html;charset=utf-8')

5、模块化

5.1 模块化好处

模块化:是指解决一个复杂问题时,自顶逐层的把系统划分成若干模块的过程,对于整个系统来说,模块时可组合、分解和更换的单元。

复用性,可维护性,按需加载方便调用

5.2模块化规范

5.3 Node中的模块化

  • 内置模块
  • 自定义模块
  • 第三方模块

1、加载模块

const fs = require('fs')

2、模块作用域

只能在当前模块被访问。防止全局变量污染问题

3、向外共享模块作用域中的成员

module对象

module.exports ={
    name:'zf',
    
}

4、模块化规范。CommonJS模块化规范。

  • 每个模块内部,module变量代表当前模块 。
  • module 变量是一个对象,它的exports 属性(module.exports)是对外的接口
  • 加载某个模块,其实是加载该模块的module.exports属性。require()方法用于加载模块

5.4 npm 与 包

6、Expres

1.创建基本的Web服务器

const express = require('express')

const app = express()

app.listen(80,()=>{
    console.log('服务器已经启动!')
})

2、监听GET,POST请求

app.get('请求URL',(req,res)=>{
	
})

app.post('请求URL',(req,res)=>{
	
})

3、把内容响应给客户端 res.send(),可以把处理好的内容,发送给客户端。

app.get('/user',(req,res)=>{
	//向客户端发送 JSON 对象
    res.send({name:'zs',age:20})
})

app.post('/user',(req,res)=>{
	//向客户端发送 JSON 对象
    res.send({name:'zs',age:20})
})

4、获取URL中携带的查询参数

通过req.query对象,可以访问到客户端通过查询字符串的形式,发送到服务器的参数。

app.get('/user'()=>{
    //req.query默认是一个空对象
    //客户端使用  ?name=zs&age=20 这种查询字符串形式,发送到服务器的参数
    //可以通过 req.query 对象访问到
    //req.query.name   req.query.age
    console.log(req.query)
})

5、获取URL的动态参数

req.params对象 **: **匹配到的动态参数

app.get('/user/:id',()=>{
   console.log(req.params)
   res.send(req.params)
})

请求127.0.0.1/user/1
打印 {id:'1'}

6.1 express.static()快速 对外提供静态资源

app.use(express.static('./public'))

挂载路径前缀
app.use('public',express.static('./public'))

6.2 nodemon

npm i -g nodemon


nodemon app.js  代码保存后,会自动更新服务器

6.3 路由

客户端的请求与服务器处理函数之间的映射关系

app.METHOD(PATH,HANDLER)

请求类型    请求的URL地址  处理函数

6.4 路由模块

  • 创建路由模块对应的 .js 文件

  • 调用 express.Router() 函数创建路由

  • 向路由对象上挂载具体的路由

  • 使用 module.exports 向外共享路由对象

  • 使用 app.use() 函数注册路由模块

var express = require('express')  //导入express

var router = express.Router()     //创建路由对象

router.get('/user/list',(req,res)=>{   //挂载获取用户列表的路由
    res.send('得到用户列表')
})

router.post('/user/add',(req,res)=>{    //挂载添加用户的路由
    res.send('添加新用户')
})

modules.exports = router    //向外导出路由对象

使用 导入

const router = require('./router')
app.use('/api',router)

6.5 中间件

//中间件函数
app.get('/',(req,res,next) => {
    next()
})

全局生效中间件

app.use((req,res,next)=>{
    console.log('简单中间件')
    next()
})

中间件作用,共享一份req,res

app.use((req,res,next) => {
  //获取到请求到达服务器的时间
  const time = Date.now()
  
  //为req对象,挂载自定义属性,从而把时间共享给后面的所有路由
  req.startTime = time
  
  next()
})

局部生效的中间件

const mw1 = (req,res,next) => {
    console.log('这是中间件函数')
    next()
}

//mw1 这个中间件只在当前路由中生效,这种用法属于局部生效的中间件

app.get('/',mw1,(req,res) => {
    res.send('home page')
})

//mw1 这个中间件不会影响下面这个路由
app.get('/user',(req,res) => {
    res.send('user page')
})
  • 路由之前注册中间件
  • 客户端发送过来的请求,可以连续调用多个中间件进行处理
  • 执行完中间件的业务代码之后,不要忘记调用next()函数
  • 为了防止代码逻辑混乱,调用next()函数后不要写额外的代码
  • 连续调用,多个中间件时,多个中间件,共享req,res对象

6.6 中间件的分类

1、应用级别的中间件

通过app.use() 或 app.get() 或 app.post() ,绑定到app实例上面的中间件,叫做应用级别的中间件。

2、路由级别的中间件

绑定 express.Router() 实例上面的中间件,叫做路由级别的中间件。

var router = express.Router()

3、错误级别的中间件

专门捕获整个项目中发生的异常错误,防止项目异常崩溃。错误级别中间件必须注册在所有路由之后

app.get('/',(req,res)=>{             //1.路由
    throw new Error('服务器内部发生错误')//2.抛出一个自定义的错误
    res.send('home page')
})

app.use((err,req,res,next)=>{//3.错误级别的中间件
    console.log('发生了错误:'+ err.message)//4.在服务器打印错误消息
    res.send('Error!'+ err.message)//5.向客户端响应错误相关的内容
})

4、内置中间件 4.16.0版本+

  1. expres.static:快速托管静态资源的内置中间件
  2. express.json:解析JSON格式的请求数据----兼容性
  3. express.urlencodeed:解析URL-encoded格式的请求体数据----兼容性
app.use(express.json())

//req.body 来获取 JSON 格式的表单数据和url-encoded格式的数据

app.use(express.urlencodeed({extended:false}))

5、第三方的中间件

6.7 自定义中间件

在中间件中,需要监听req对象的data事件,来获取客户端发送到服务器的数据

数据量大,则客户端会把数据切割后,分批发送到服务器。

所以data事件可能会触发多次,每一次触发data事件,获取到数据只是完整数据的一部分,需要手动对接收到的数据进行拼接。

const express = require('express')

const app = express()

const qs = require('querystring')

app.use((req,res,next) => {
    let str = '' //定义一个字符串,专门存储客户端发送过来的请求体数据
    req.on('data',(chunk) => {     //每次触发data都会把,数据存储在str中。
        //拼接请求体数据
        str += chunk
    })
    
    req.end('end',()=>{      //监听end事件,结束后打印全部数据
        //把字符串格式的请求体数据,解析成对象格式。
        const body = qs.parse(str)
        req.body = body
        //console.log(body)
        next()
    })
})

app.post('/user',(req,res)=>{
    res.end('req.body')
})

将自定义中间件封装为模块------创建一个 .js文件

const qs = require('querystring')

const  bodyParser = (req,res,next) => {
    let str = '' //定义一个字符串,专门存储客户端发送过来的请求体数据
    req.on('data',(chunk) => {     //每次触发data都会把,数据存储在str中。
        //拼接请求体数据
        str += chunk
    })
    
    req.end('end',()=>{      //监听end事件,结束后打印全部数据
        //把字符串格式的请求体数据,解析成对象格式。
        const body = qs.parse(str)
        req.body = body
        //console.log(body)
        next()
    })
}

module.exports = bodyParser



//使用:导入自己封装的中间件函数,注册为全局可用的中间件
cosnt customBodyParser = require(./xx.js)

app.use(customBodyParser)

6.8 使用Express写接口 GET-POST

1、创建app.js文件

const express = require('express')
const app =express()

//导入路由模块
const router = require('./apiRouter')
//把路由模块,注册到app上
app.use('/api',router)


app.listen(80,()=>{
    console.log('服务器已经启动')
})

2、创建API路由模块-----apiRouter.js

const express = require('express')
const router = express.Router


//挂载路由处
router.get('/get',(req,res) => {
    //通过req.query获取客户端通过查询字符串,发送到服务器的数据
    const query = req.query
    //调用res.send方法,向客户端响应处理的结果
    res.send({
        status:0,//0表示处理成功 ,1表示处理失败
        msg:'GET请求成功',  .//状态描述
        data:query  //响应给客户端的数据
    })
})

router.post('/post',(req,res) => {
    //通过req.body获取客户端通过请求体,发送到服务器的URL-encoded数据,配置中间app.use(express.urlencoded({extended:fasle}))
    const body = req.body
    //调用res.send方法,向客户端响应处理的结果
    res.send({
        status:0,//0表示处理成功 ,1表示处理失败
        msg:'POST请求成功',  .//状态描述
        data:body  //响应给客户端的数据
    })
})

module.exports = router

1、跨域问题---cors

  • npm i coars
  • const cors = require('cors')
  • 在路由前调用app.use(cors())
<html>
    <head>
        <script>
        </script>
    </head>
   <body>
       <button id="btnGET">
           GET
       </button>
        <button id="btnPOST">
           POST
       </button>
	   <script>
       		$(function () {
                $('#btnGET').on('click',function (){
                    $ajax({
                        type:'GET',
                        url:"http://127.0.0.1/api/get",
                        data:{name:'zd',age:20},
                        sucess: function (res) {
                            console.log(res)
                        }
                    })
                })
            })
           
           $(function () {
                $('#btnPOST').on('click',function (){
                    $ajax({
                        type:'POST',
                        url:"http://127.0.0.1/api/post",
                        data:{bookname:'谁属中国哑巴',age:23},
                        sucess: function (res) {
                            console.log(res)
                        }
                    })
                })
            })
       </script>
    </body>
</html>
  • Access-Control-Allow-Origin字段的值为通配符*,表示允许来自任何域的请求。

res.setHeader('Access-Control-Allow-Origin','*')

  • Access-Control-Allow-Headers,只支持客户端向服务器发送九个请求头

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

  • Access-Control-Allow-Methods,如果希望通过PUT,DELETE等方式请求服务器的资源,则需要在服务器端,通过这个来指明实际请求所允许使用的HTTP方法

``````res.setHeader('Access-Control-Allow-Methods','*')```

7、数据库

id username password status
1 张三 123456 0
2 李四 341125 1
3 托尼史塔克 7758258 1

1、查

select 列名 from 表名

select * from users   /*查所有*/

select id from users

2、增

insert into 表名 (列1,列2,列3) values (值1,值2,值3)

insert into users (username,password) values ('托尼史塔克','20202020')

3、改

update 表名称 set 列名称 = 新值  where 列名称 = 某值     where更新的条件

update users set username='美国队长' where id=1

update users set username='雷神',password='7758258' where id=2

4、删

delete from 表名称 where 列名称 = 值

delete from users where id=3

5、where子句:限定选择的标准

select * from users where id>1

6、and 和 or 运算符 相当于 || 和 &&

select * from users where id>1 and status=1

7、order by 语句,更具指定的列,进行排序,默认升序排序,asc关键字代表升序排序

升序排序:select * from users order by status asc

降序排序:select * from users order by id desc

多重排序:select * from users order by status desc, username asc

8、count(*)函数,总数据条数

```select count(*) from users where status=1``

返回值 count=2

9、使用as为列设置别名

select count(*) as total from users where staus=1

8、使用 mysql

//安装 npm imysql
//导入
const mysql = require('mysql')

cosnt db = mysql.createPool({
    host:'127.0.0.1',
    user:'root',
    password:'admin2022!',
    database:'test'
})

module.exports = db

1、查询数据

const str1 = 'select * from users'
//查询表中数据
db.query(str1,(err,results) => {
    
    if(err) return console.log(err.message)
    
    console.log(results)
})

2、插入数据

//要插入到users表中的数据对象
const user = {username:'Doctor Strange',password:'123456'}

//待执行的SQL语句,其中英文的 ? 表示占位符
const sqlStr = 'insert into users (username,password) values (?,?)'

//使用数组的形式,依次为 ? 占位符指定的具体的值
db.query(sqlStr,[user.username,user.password],(err,results) => {
    if(err) return console.log(err.message)
    
    if(result.affectedRows ===1){  //判断数据是否插入成功
        console.log('插入数据成功')
    }
})

快速插入

//要插入到users表中的数据对象
const user = {username:'Doctor Strange',password:'123456'}

//待执行的SQL语句,其中英文的 ? 表示占位符
const sqlStr = 'insert into users set ?'

//使用数组的形式,依次为 ? 占位符指定的具体的值
db.query(sqlStr,user,(err,results) => {
    if(err) return console.log(err.message)
    
    if(result.affectedRows ===1){  //判断数据是否插入成功
        console.log('插入数据成功')
    }
})

3、更新数据

const user = {id:4,username:'Doctor Strange',password:'123456'}

const sqlStr = 'update users set username=?,password=? where id=?'

db.query(sqlStr,[user.username,user.password,user.id],(err,results) => {
    if(err) return console.log(err.message)
    
    if(result.affectedRows ===1){  //判断数据是否插入成功
        console.log('更新数据成功')
    }
})

便捷方式

const user = {id:4,username:'Doctor Strange',password:'123456'}

const sqlStr = 'update users set ? where id=?'

db.query(sqlStr,[user,user.id],(err,results) => {
    
    if(err) return console.log(err.message)
    
    if(result.affectedRows ===1){  //判断数据是否插入成功
        console.log('更新数据成功')
    }
})

4、删除数据

const sqlStr = 'delete from users where id=?'

db.query(sqlStr,2,(err,results) => {
    
    if(err) return console.log(err.message)
    
    if(result.affectedRows ===1){  //判断数据是否插入成功
        console.log('删除数据成功')
    }
})

标记删除

const sqlStr = 'update users set status=? where id=?'

8、身份认证

cookie:存储在用户浏览器中的不超过4kb的字符串。

自动发送,域名独立,过期时限。

图解

浏览器(客户端) ------------>>>>>>登录请求--------------->>>>>服务器

浏览器(客户端)---------------<<<<<<通过响应头,发送Cookie给浏览器------<<<<---服务器

浏览器(将身份认证的cookie保存到浏览器中)--->>>通过请求头,自动将cookie发送给服务器----->>服务器(服务器根据请求头中cookie,验明用户的身份)------>>>>响应当前用户对应的内容

Session的工作原理

在浏览器中进行登录----提交账号和密码到服务器-----服务器验证账号和密码-----将登录成功后的用户信息存储在服务器内存中,同时生成对应的cookie字符串------将生成的cookie响应给客户端-----浏览器自动把cookie存储在当前域名下----客户端再次发送请求时,通过请求头自动把当前域名下所有可用的cookie发送给服务器----服务器根据请求头中携带的cookie,从内存中查找对应的用户信息------用户的身份信息认证成功之后,服务器针对当前用户生成特定的响应内容------把当前用户对应的页面内容响应给浏览器。

JWT认证机制

1、jwt工作原理

在浏览器中进行登录----提交账号和密码到服务器-----服务器验证账号和密码-----验证通过后将用户信息加密之后生成对应的Token字符串------将生成的Token响应给客户端-----将Token存储到LocalStorage或SessionStorage----客户端再次发送请求时,通过请求头的Authorization字段,将Token发送给服务器----服务器把Token字符串还原成用户的信息对象------用户的身份信息认证成功之后,服务器针对当前用户生成特定的响应内容------把当前用户对应的页面内容响应给浏览器。

2、jwt组成部分,逗号分割

Header.Payload.Signature

Payload:才是真正的用户信息

Header、Signature保证Token的安全性

3、使用方式

Authorization:Bearer <token>

在express中使用

npm i jsonwebtoken express-jwt
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()

// TODO_01:安装并导入 JWT 相关的两个包,分别是 jsonwebtoken 和 express-jwt
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
// 允许跨域资源共享
const cors = require('cors')
app.use(cors())

// 解析 post 表单数据的中间件
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: false }))

// TODO_02:定义 secret 密钥,建议将密钥命名为 secretKey
const secretKey = 'zhangfan520'

// TODO_04:注册将 JWT 字符串解析还原成 JSON 对象的中间件
// 注意:只要配置成功了express-jwt这个中间件,就可以把解析出来的用户信息,挂载到req.user属性上
app.use(expressJWT({secret: secretKey }).unless({ path: [/^\/api\//] }))

// 登录接口
app.post('/api/login', function (req, res) {
  // 将 req.body 请求体中的数据,转存为 userinfo 常量
  const userinfo = req.body
  // 登录失败
  if (userinfo.username !== 'admin' || userinfo.password !== '000000') {
    return res.send({
      status: 400,
      message: '登录失败!'
    })
  }
  // 登录成功
  const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '1h' })
  // TODO_03:在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并通过 token 属性发送给客户端
  res.send({
    status: 200,
    message: '登录成功!',
    token: tokenStr // 要发送给客户端的 token 字符串
  })
})

// 这是一个有权限的 API 接口
app.get('/admin/getinfo', function (req, res) {
  // TODO_05:使用 req.user 获取用户信息,并使用 data 属性将用户信息发送给客户端
  console.log(req.user)
  res.send({
    status: 200,
    message: '获取用户信息成功!',
    data: req.user // 要发送给客户端的用户信息
  })
})

// TODO_06:使用全局错误处理中间件,捕获解析 JWT 失败后产生的错误
app.use((err,req,res,next)=>{
  if(err.name === 'UnauthorizedError'){
    return res.send({
      status:401,
      message:'无效的Token'
    })
  }
  res.send({
    status:500,
    message:'未知的错误'
  })
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(8888, function () {
  console.log('Express server running at http://127.0.0.1:8888')
})

posted @ 2023-03-01 15:35  孤舟蓑衣客  阅读(24)  评论(0)    收藏  举报