7-node.js


7-1 node.js基础及模块

浏览器是 JavaScript 的前端运行环境。

Node.js 是 JavaScript 的后端运行环境,无法调用 DOM 和 BOM 等浏览器内置 API。

1.fs文件系统模块

fs.readFile() 方法,用来读取指定文件中的内容
fs.writeFile() 方法,用来向指定的文件中写入内容

在JavaScript中使用fs模块进行文件操作,则需要使用如下的方式先导入它:

const fs=require('fs');

1.1读取指定文件内容

fs.readFile(path,[options],callback);
//例
const fs=require('fs');
fs.readFile('./a.txt',utf8,function(err,dataStr){
    
    
   
    if(err)
         console.log('读失败');
    else
        console.log('内容是'+dataStr);
})

path:字符串,文件路径

options:编码格式

callback:回调函数

1.2向指定文件写入内容

fs.writeFile(path,data,[options],callback);

//例
const fs =require('fs');
fs.writeFile('./a.txt','Hello',function(err){
    
    
    if(err)
        console.log('失败');
})

path:指定文件路径

data:写入内容

options:编码格式,默认utf8

callback:回调函数

在使用 fs 模块操作文件时,如果提供的操作路径是以 ./ 或 …/ 开头的相对路径时,很容易出现路径动态拼接错误的问题。 原因:代码在运行的时候,会以执行 node 命令时所处的目录,动态拼接出被操作文件的完整路径。 解决方案:在使用 fs 模块操作文件时,直接提供完整的路径,不要提供 ./ 或 …/ 开头的相对路径,从而防止路径动态拼接的问题。

2.path路径模块

path 模块是 Node.js 官方提供的、用来处理路径的模块。它提供了一系列的方法和属性,用来满足用户对路径的处需求。

2.1路径拼接

path.join([...paths]);
...paths <string> 路径片段的序列
返回值: <string>
    
//例
const pathStr=path.join('/a','/b/c','./d','e');
返回值=\a\b\d\e

const pathStr=path.join(_dirname,'./files/a.txt');
当前目录\files\a.txt

涉及到路径拼接的操作,都要使用 path.join() 方法进行处理。不要直接使用 + 进行字符串的拼接。

2.2获取路径中的文件名

path.basename(path,[ext]);

const fpath='/a/b/c/index.html'
var fullName=path.basename(fpath)//index.html
var fullName=path.basename(fpath,'.html')//index

path:路径

ext:文件扩展名

返回:路径中的最后一部分

2.3获取路径中的文件扩展名

path.extname(path)

const fext=path.extname(fpath)//.html

path:路径

返回:得到的扩展名(字符串)

fs.writeFile() 方法只能用来创建文件,不能用来创建路径 ② 重复调用 fs.writeFile() 写入同一个文件,新写入的内容会覆盖之前的旧内容

3.http模块

http 模块是 Node.js 官方提供的、用来创建 web 服务器的模块。通过 http 模块提供的 http.createServer() 方法,就 能方便的把一台普通的电脑,变成一台 Web 服务器,从而对外提供 Web 资源服务。

如果要希望使用 http 模块创建 Web 服务器,则需要先导入它

const http=require('http')

3.1创建最基本的web服务器

①导入 http 模块

const http=require('http');

② 创建 web 服务器实例

const server=http.createServer();

③ 为服务器实例绑定 request 事件,监听客户端的请求

//绑定事件,可以用on方法
server.on('request',function(req,res){
    
    
	console.log('访问服务器');
})
  • req 请求对象

    访问与客户端相关的数据或属性

    seq.url 请求的url地址
    seq.method 请求类型
    
  • res 响应对象

    访问与服务器相关的数据或属性

    res.end() 向客户端发送指定内容,并结束这次请求的处理过程
    
    如果发送的是中文,会乱码,解决方法:
    setHeader('const-Type','text/html:charset=utf-8');
    

④ 启动服务器

server.listen(80,function(){
    
    //端口号,回调函数
	console.log('启动服务器');
})

7-2 模块化

1.基本概念

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

优点:① 提高了代码的复用性 ② 提高了代码的可维护性 ③ 可以实现按需加载

2.node.js中的模块化

分类:

  • 内置模块(内置模块是由 Node.js 官方提供的,例如 fs、path、http 等)
  • 自定义模块(用户创建的每个 .js 文件,都是自定义模块)
  • 第三方模块(由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要先下载)

2.1加载模块

//加载内置的模块
const fs=require('fs')
//加载用户自定义模块
const custom=require('./custom.js')
//加载第三方模块
const =moment=require('moment')

2.2模块作用域

和函数作用域类似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块作用域。

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

  • module对象

    每个.js自定义模块中都有一个module对象,存储了和当前模块有关的信息。

  • module.exports对象

    在自定义模块中,可以使用 module.exports 对象,将模块内的成员共享出去,供外界使用。 外界用 require() 方法导入自定义模块时,得到的就是 module.exports 所指向的对象。

    使用 require() 方法导入模块时,导入的结果,永远以 module.exports 指向的对象为准。

    在被导入的js中,用module.exports.username='hello’来定义

    module.exports={
          
          
    	name:'abc',
    	Sayhi(){
          
          
    	console.log('hi');
    	}
    }//以最新的为准
    
  • exports 对象(=module.exports)

    由于 module.exports 单词写起来比较复杂,为了简化向外共享成员的代码,Node 提供了 exports 对象。默认情况 下,exports 和 module.exports 指向同一个对象。最终共享的结果,还是以 module.exports 指向的对象为准,即module.exports的优先级较高。

3.npm和包

Node.js 中的第三方模块又叫做包。

npm i 包名//安装某包
npm i //安装所有依赖包,package.json中的dependencies节点

7-3 Express

Express 的作用和 Node.js 内置的 http 模块类似,是专门用来创建 Web 服务器的。

1.express基础

1.1创建基本的web服务器

const express=require('express')
const app=express()
app.listen(80,function(){
    
    //端口号,回调函数
	console.log('启动服务器');
})

1.2监听GET请求

app.get('请求URL',function(req,res){
    
    
	/*处理函数*/
})

1.3监听POST请求

app.post('请求URL',function(req,res){
    
    
	/*处理函数*/
})

1.4把内容响应给客户端

通过 res.send() 方法,可以把处理好的内容,发送给客户端:

app.get('/user',funciton(req,res){
    
    
	res.send({
    
    name:'zhangsan',age:20})
})

app.post('/user',funciton(req,res){
    
    
	res.send('请求成功')
})

1.5获取 URL 中携带的查询参数

req.query//用于get请求中
//例如
req.query.name
req.query.age

默认是空对象
?name=zs&age=20 查询字符串形式,可以发送到服务器并赋值。
req.body//用于post请求中

1.6获取 URL 中的动态参数

通过 req.params 对象,可以访问到 URL 中,通过 : 匹配到的动态参数:

app.get('/user/:id',function(req,res){
    
    
	console.log(req.params);
})
//req.params默认是空对象,里面存放着通过:动态匹配到的参数值
//:id是一个动态参数

//例:
http://127.0.0.1/user/1
req.params=1

1.7托管静态资源

express.static(),可以创建一个静态资源服务器。

例如,通过如下代码就可以将 public 目录下的图片、CSS 文件、JavaScript 文件对外开放访问了:

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

//例
app.use(express.static('./files'))

现在就可以访问public目录中所有文件了

image-20230305234655606

访问静态资源文件时,express.static() 函数会根据目录的添加顺序查找所需的文件,多个目录多次添加即可。

如果希望在托管的静态资源访问路径之前,挂载路径前缀,则可以使用如下的方式:

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

image-20230305235202816

2.express路由

Express 中的路由分 3 部分组成,分别是请求的类型、请求的 URL 地址、处理函数,格式如下:

app.method(path,handler)

每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数。 在匹配时,会按照路由的顺序进行匹配,如果请求类型和请求的 URL 同时匹配成功,则 Express 会将这次请求,转交给对应的 function 函数进行处理。

路由匹配的注意点:

① 按照定义的先后顺序进行匹配

② 请求类型和请求的URL同时匹配成功, 才会调用对应的处理函数

将路由抽离为单独的模块:

//创建路由模块 router.js
var express =require('express')
var router express.Router()
router.get……
module.exports = router//向外导出路由对象

//导入路由模块
const userRouter =require(./router.js)
app.use(userRouter)
//添加前缀版  app.use('/api',userRouter)
app.listen(80,function...)

3.express中间件

3.1全局中间件

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

//定义中间件函数
const mw=function(req,res,next){
    
    
	console.log('中间件函数');
	//当中间件的业务处理完毕后必须调用next函数
	next();
}
//全局生效的中间件
//客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件
app.use(mw);
//不使用app.use()就是局部生效的中间件,只在当前路由中生效

//简化形式,定义多个会多个依次执行
app.use(function(req,res,next){
    
    
    	console('简化');
    	next();
})

① 一定要在路由之前注册中间件

② 客户端发送过来的请求,可以连续调用多个中间件进行处理

③ 执行完中间件的业务代码之后,不要忘记调用 next() 函数

④ 为了防止代码逻辑混乱,调用 next() 函数后不要再写额外的代码

⑤ 连续调用多个中间件时,多个中间件之间,共享 req 和 res 对象

3.2局部生效的中间件

app.get('/',mw1,mw2,function(req,res){
    
    
	res.send('Home page')
})

3.3中间件的分类

  • 应用级别的中间件

    应用级别中间件是绑定到 app 实例上

    //全局
    app.use(function(req,res,next){
          
          
    	next()
    })
    //局部
    app.get('/',mw1,function(req,res,next){
          
          
    	res.send('Home Page')
    })
    
  • 路由级别的中间件

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

    //创建路由模块 router.js
    var express =require('express')
    var router express.Router()
    router.use(function(req,res,next){
          
          
        console.log('Time',Date.now())
        next()
    })
    
    app.use('/',router)
    
  • 错误级别的中间件

    app.get( 'i', function (req,res) {
          
          		// 1.路由
    	throw new Error( '服务器内部发生了错误! ')// 1.1抛出一个自定义的错误
        res.send( 'Home Page. ')
    })
    app.use( function (err,req,res,next) {
          
           		// 2.错误级别的中间件
        console.log( '发生了错误:' +err.message)	// 2.1在服务器打印错误消息
    res.send( 'Error ! ' + err.message)			// 2.2向客户端响应错误相关的内容))
    

    注意:错误级别的中间件, 必须注册在所有路由之后!

  • express内置的中间件

    ① express.static 快速托管静态资源的内置中间件,例如: HTML 文件、图片、CSS 样式等(无兼容性)

    ② express.json 解析 JSON 格式的请求体数据(有兼容性,仅在 4.16.0+ 版本中可用)

    ③ express.urlencoded 解析 URL-encoded 格式的请求体数据(有兼容性,仅在 4.16.0+ 版本中可用)

  • 第三方中间件

  • 自定义中间件

4.使用express写接口

apirouter.js

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

router.get('/get',function(req,res){
    
    
    const query=req.query//获取客户端通过查询字符串发送到服务器的数据
    res.send({
    
    
        status:0,
        msg:'GET请求成功',
        data:query//需要响应给客户端的数据
    })
})

router.post('/post',function(req,res){
    
    
    const body=req.body
    res.send({
    
    
        status:0,
        msg:'请求成功',
        data:body
    })
})


module.exports=router

app.js

const express=require('express')//导入模块
const app=express() //创建服务器
app.use(express.urlencoded({
    
     extended: false }))


const router = require('./apirouter')

app.use('/api',router)

app.listen(80,function(){
    
    
    console.log('running')
})

5.CORS中间件解决跨域问题

npm i cors//安装
const cors=require('cors')//导入
app.use(cors())//路由之前调用,配置中间件

5.1CORS 响应头部 - Access-Control-Allow-Origin

 Access-Control-Allow-Origin :<origin>|*

其中,origin 参数的值指定了允许访问该资源的外域 URL

例如,下面的字段值将只允许来自 http://itcast.cn 的请求:

res.setHeader('Access-Control-Allow-Origin',' http://itcast.cn')
如果指定了 Access-Control-Allow-Origin 字段的值为通配符 *,表示允许来自任何域的请求,示例代码如下:

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

7-4 数据库与身份认证

1.在项目中操作MySQL

//导入mysql模块
const mysql =require('mysql')
const db=mysql.createPool({
    
    
    host:'127.0.0.1',
    user:'root',
    password:'admin123',
    database:'db1'//指定操作哪个数据库
})

//查询
db.query('select * from users',function(err,results){
    
    
    if(err)
        return console.log(err.message)
    console.log(results)
})


//插入
const user={
    
    username:'dsc',password:'123123'}
const sqlStr='insert into users(username,password) values(?,?)'
//?表示占位符
db.query(sqlStr,[user.username,user.password],function(err,results){
    
    
    if(err)
        return console.log(err.message)
    if(results.affectedRows===1)return console.log('成功')
})
//如果数据对象的每个属性和数据表的字段一一对应
db.query(sqlStr,user,function(err,results){
    
    
    if(err)
        return console.log(err.message)
    if(results.affectedRows===1)return console.log('成功')
})


//更新
const user ={
    
    id:1,username:'aaa',password:123123}
const sqlStr='update users set ? where id=?'
db.query(sqlStr,[user,user.id],function(err,results){
    
    
    if(err) return console.log(err.message)
    if(results.affectedRows===1)console.log('更新成功')
})


//删除
const sqlStr ='delete from users where?'
const user={
    
    id:1}
db.query(sqlStr,user,function(err,results){
    
    
    if(err) return console.log(err.message)
    if(results.affectedRows===1) console.log('删除成功')
})

2.前后端的身份认证

2.1 cokkie

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

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

Cookie的几大特性:

① 自动发送

② 域名独立

③ 过期时限

④ 4KB 限制

Cookie 不具有安全性。

由于 Cookie 是存储在浏览器中的,而且浏览器也提供了读写 Cookie 的 API,因此 Cookie 很容易被伪造,不具有安全 性。因此不建议服务器将重要的隐私数据(身份信息、密码等),通过 Cookie 的形式发送给浏览器。

2.2 Session认证机制

工作原理:

image-20230307205830260

  1. 安装express-session中间件

    npm i express-session
    
  2. 配置中间件

    //导入中间件
    var session =require('express-session')
    //配置中间件
    app.use(session({
          
          
    	secret:'keyboard cat',//任意字符串
    	resave:false,//固定写法
    	saveUninitialized:true//固定写法
    }))
    
  3. 向session中存数据

    app.get('/api/login',function(req,res){
          
          
        if(req.body.username!=='admin'||req.body.password!==='admin123'){
          
          
            return res.send({
          
          status:1,msg:'登录失败'})
        }
      req.session.user=req.body//将用户的信息存储到session中
      req.session.islogin=true
      
      res.send({
          
          status:0,msg:'登录成功'})
    })
    
  4. 向session中取数据

    app.get('/api/username',function(req,res){
          
          
    	if(!req.session.islogin)//没登录
    		return res.send({
          
          status:1,msg:'failure'})
    	res.send({
          
          status:0,msg:'success',username:req,session.user.username})
    })
    
  5. 清空session

    app.post('/api/logout',function(req,res){
          
          
        req.session.destroy()
        res.send({
          
          
            status:0,
            msg:'退出登录成功'
        })
    })
    

2.3JWT认证机制

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

image-20230307211328421

  1. 安装JWT相关包

    npm i jsonwebtoken express-jwt
    

    其中:

    jsonwebtoken 用于生成 JWT 字符串

    express-jwt 用于将 JWT 字符串解析还原成 JSON 对象

  2. 导入 JWT 相关的包

    const jwt =require('jsonwebtoken')
    const expressjwt=require('express-jwt')
    
  3. 定义secret密钥

    为了保证 JWT 字符串的安全性,防止 JWT 字符串在网络传输过程中被别人破解,我们需要专门定义一个用于加密和解密 的 secret 密钥:

    ① 当生成 JWT 字符串的时候,需要使用 secret 密钥对用户的信息进行加密,最终得到加密好的 JWT 字符串

    ② 当把 JWT 字符串解析还原成 JSON 对象的时候,需要使用 secret 密钥进行解密

    const secretkey='miyao'//密钥的本质就是一个字符串
    
  4. 在登录成功后生成 JWT 字符串

    app.post('/api/login',function(req,res){
          
          
    	//if(err).....
    	res.send({
          
          
    	status:200,
    	msg:'登录成功',
    	token:jwt.sign({
          
          username:userinfo.username,secretkey,{
          
          expiresin:'30s'}})
    	})
    })
    
  5. 将 JWT 字符串还原为 JSON 对象

    使用app.use()注册中间件

    expressJWT({secret:secretKey})是用来解析token的中间件

    .unless({path:[/^/api//]})用来指定哪些接口不需要访问权限

    app.use(expressJWT({
          
          secret:secretKey}).unless({
          
          path:[/^\/api\//]}))
    
  6. 使用 req.user 获取用户信息

    app.get('/admin/getinfo',function(req,res){
          
          
    	console.log(req.user)
    	res.send({
          
          
    	status:200,
    	message:'获取用户信息成功'
    	data:req.user
    	})
    })
    
  7. 捕获解析 JWT 失败后产生的错误

    当使用 express-jwt 解析 Token 字符串时,如果客户端发送过来的 Token 字符串过期或不合法,会产生一个解析失败 的错误,影响项目的正常运行。我们可以通过 Express 的错误中间件,捕获这个错误并进行相关的处理,示例代码如下:

    image-20230307212928819

猜你喜欢

转载自blog.csdn.net/qq_51444138/article/details/129392230