初识NODE(从常用模块到小型web服务器构建)

303 阅读8分钟

同学们好,我是连颜少,一个菜鸟前端,都说前端要掌握一门后端语言,才能更好的理解前后端的交互,这里我就记录下我初学NODE的一些笔记,希望对同学们有点帮助~

fs模块:

//被[,]包裹起来的为可选参数
const fs = require('fs')
fs.readFile(path[,option],callback)
    path: //文件路径
    option://编码格式:默认为utf-8
    callback(err,dataStr)://文件读取完成后,通过回调函数读取的结果
        成功:err = null;
        失败: dataStr = undefined;
fs.writeFile(file,data[,option],callback)
    file://文件路径
    data://写入内容(覆盖写入)
    option://编码格式:默认为utf-8
    callback(err,dataStr)://文件读取完成后,通过回调函数读取的结果
        成功:err = null;
 .//   / 或者 ../ 相对路径会出现问题
 //解决:提供完整的文件路径,__dirname 当前所处的文件路径
     __dirname + 'files/1.txt'

path模块:

 path.join([...paths]): //将多个路径片段拼接
     paths://多个路径片段
     返回值:string
     path.join(__dirname,'files/1.txt')
     
 path.basename(path,'扩展名'): //从路径字符串中,将文件名解析出来
     path: //地址
     返回值://文件名.扩展名
 path.extname(path)://获取文件扩展名 

http模块:创建web服务器模块

 http.createServer()
     server.on('request',(req,res) => {})
         req //包含一系列内容
             req.url
             req.method
         res //包含一系列内容
             res.setHeader('Content-Type','text/html';'charset=utf-8'): //设置编码方式,否则中文乱码
             res.end(content) //方法结束本次请求,并返回内容
     server.listen(80,() => {}) //默认地址127.0.0.1 默认端口80
 
//模块内变量只能在模块内使用
每个js文件都有一个module变量,都有一个exports对象,当通过require导入的时候,返回的内容就是exports,exports最终指向也是module.exports
导入模块的时候,会执行模块
module.exports = {
    XXX : XXX,
    aaa : function() {}
}

npm安装小知识

npm i bao -D 开发依赖包,会被记录在devDependencies;
npm i bao  核心依赖包,记录在dependencies节点下;
require优先从缓存机制中加载
内置模块优先级最高,当第三方模块名和node内置模块名重复,默认加载node内置模块
加载自定义模块需要相对路径
    当没有后缀的时候,node默认按顺序尝试加载.js => .json => .node

express

  • express 的作用和nodejs内置的http模块相似,是专门用来创建web服务器的
  • express 的本质是一个npm第三方包,提供了快速创建web服务器的便捷方法
  • web网站服务器
    
  • api接口服务器
    

express创建服务器

const express = require('express')
const app = express()
app.listen(80,() => {
    console.log(express server running at 80 port)
})
app.get('/',function(req,res) {
    console.log('req.query',req.query) // 匹配?a=1示例参数
    console.log('req.param',req.params)//匹配:id示例动态参数
})

express静态资源入口

// express.static:创建一个静态资源服务器
    app.use(express.static('public')) //所有地址都可以访问
    app.use('/public',express.static('./files')) //只有当访问public开头的接口的时候,才能访问public下的文件
    // public不会出现在路径当中
    // 当挂载多个静态服务器,从先声明的文件中找,找到就返回

express中的路由挂载

//作用:处理客户端请求和服务端处理函数之间的映射关系
//模块路优化步骤:
//在router.js文件中
//1.导入express
    const express = require('express')
//2.创建路由对象
    const router = express.Router()
//3.挂在具体的路由
    router.get('/user/list',fuunction(req,res) {
        res.send('GET LIST SUCCESS')
    })
//4.向外导出路由对象:所有module对外导出都通过module.exports,导入文件才能接收到对象
    module.exports = router
//在服务器文件中
//1.导入router.js 自定义模块
    const router = require('./router.js')
//2.挂在到express服务器上
    app.use(router)
    app.use('/api',Router) //统一的路由前缀

中间件函数(五大类)

  • 1.中间件函数:需要定义在路由之前,从上到下执行
  • 2.通过next()结束该中间件函数

应用级别中间件:app.use / app.get / app.post绑定

// 1.全局中间件
app.get('/',function(req,res,next){
	next()
})

const mv = function(req,res,next) {
	next()
}
//使用全局中间件函数
app.use(mv)

//或者
app.use(function(req,res,next){
	next()
})

//2.局部中间件:在路由调用的时候,作为第二个参数传入 ,mv只在 /user 地址下生效
app.get('/user',mv,function(res,req) {
	
})
//3.多个局部中间件
app.get('/user',mv1,mv2,function(res,req) {
	
})
//或者
app.get('/user',[mv1,mv2],function(res,req) {
	
})
  • 1.中间件函数之间是共享req和res,对req和res的修改会影响到其他的中间件函数
  • 2.定义多个中间件,都用app.use()进行注册,并按照注册的顺序依次执行中间件

路由级别中间件:在router.js中,绑定在router下:router.use / router.get / router.post

错误级别中间件:放在所有路由之后

//抓门用来捕获整个项目发生的错误,防止项目崩溃
//必须四个形参(err,req,res,next) 顺序固定 	
//ps:认为制造报错方法:throw new Error('xxxxx')

app.use(function(err,res,req,next) {
        res.send('Error!' + err.message)
})

Express内置的中间件

express.static:访问文件
express.json:解析json格式请求体数据
        app.use(express.json())
express.urlencoded(extended:false)
        app.use(express.urlencoded({extended:false}))

第三方的中间件

//示例
const = qs = require('querystring')
app.use((req,res,next) => {
    // 监听数据传输,并拼接字符串
    let str = '';
    req.on('data',(chunk) => {
        str += chunk
    })
    // 当请求数据接受完毕,会自动处罚req的end事件
    req.on('end',() => {
        // 解析数据
        const body = qs.parse(str)
        req.body = body
        next()
    })
})

跨域问题CORS

使用cors中间件解决跨域问题:相当于配置了Access-Control-Allow-* 请求头

npm i cors  //安装中间件
const cors = require('cors')
app.use(cors())
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三者之一)

res.setHeader('Access-Control-Allow-Origin',<origin> | *)
默认只允许发送GET,POST,HEAD请求,希望发送其他请求需要配置
res.setHeader('Access-Control-Allow-Methods','POST,GET,DELETE,PUT,HEAD') 或者 *

预检请求:

只要复合以下的任何一请求,都需要进行预检请求:客户端和服务器之间会发生两次请求,OPTION预检请求成功之后,才会发起真正的请求

  • 1.请求方式为GET,POST,HEAD之外的Method类型
  • 2.请求头中包含自定义头部字段
  • 3.向服务器发送了application/json格式的数据

认证机制(两种)

服务端渲染推荐Session认证机制

~服务端渲染推荐Session认证机制:
        1.HTTP协议的无状态性:客户端每次HTTP请求都是独立的
        2.身份认证方式:cookie
                cookie:存储在浏览器中不超过4KB的字符串,由NAME,Value,Domain,path,expires,httpOnly等组成,控制cookie的有效期,安全性,使用范围
                当客户端发起请求,会自动,自动!!将当前域名下,所有未过期的cookie一起发送到服务器端
~客户端第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身份认证的cookie,客户端会自动将cookie保存至浏览器中
~之后,客户端浏览器每次请求服务器的时候,浏览器会自动将身份认证相关的cookie,通过请求头的形式发送给服务器,服务器即可验证客户端的身份
cookie不具有安全性,浏览器提供读写cookie的api,可以伪造cookie,也可以从cooike中读取隐私数据

前后端分离推荐使用JWT认证机制

Session认证

//安装express-session中间件
npm i express-session
//配置express-session
const session = require('express-session')

app.use(session({
	secret:'keyboard car', // secret属性值可以是任意字符串
	resave:false, //固定写法
	saveUninitialized:true //固定写法
}))

// 配置成功后,req就会多一个session对象,
// 请求登录后,存储登录用户信息及状态在session中,之后请求的接口中,req中会一直有之前保存的session对象
app.post('/api/login',(req,res) => {
	//判断用户提交的信息是否正确
	if (req.body.username !== 'admin' || req.body.password !== '123456') {
		return res.send({status:1,msg:'login fail'})
	}
	
	//保存信息到session中
	req.session.user = req.body
	res.session.islogin = true
	res.send({
		status:0
		msg:'login success'
	})
})

// 之后的接口中:
app.get('/api/username',(req,res) => {
	//从session中取值,判断是否登录,或者判断用户类型,返回对应内容
	if (!req.session.islogin) {
		return res.send({status:1,msg:'fail'})
	}
	res.send({status:0,msg:'success',user:req.session.user})
})

//退出登录后,清空session信息
app.post('/api/logout',(req,res) => {
	//清空当前客户端对应的session信息
	req.session.destory()
	res.send({
		status:0
		msg:'logout success'
	})
})

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

sequenceDiagram
客户端->>服务器: 客户端登录,提交账号及密码
服务器->>客户端: 服务器响应,验证密码,加密生成Token字符串返回给客户端
客户端->>服务器: 客户端将Token存储到localStorage或者SessionStorage,之后请求都通过请求体的Authorization字段,将Token返回
服务器->>客户端: 服服务器接受Token,解密认真身份,响应当前用户对应内容再返回给客户端

JWT的组成部分

1.Header(请求头).Payload(有效荷载).Signature(签名),用'.'隔开
2.Payload 部分才是真正的用户信息,通过加密后生成的字符串
3.把JWT放在HTTP的请求头Authorization中
4.Authorization: Bearer <token> (拼接)

在express中使用JWT


npm i jsowebtoken // 用于生成JWT字符串
npm i express-jwt // 将JWT字符串解析还原成JSON对象

const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')

//定义secret秘钥
//1.当生成JWT字符串的时候,需要使用secret秘钥对用户的信息进行加密,最终得到加密好的JWT字符串
//2.把JWT字符串解析还原成JSON对象的时候,需要使用同一个secret秘钥进行解密
const secretKey = 'lys No1~~'

app.post('/api/login',function(req,res) {
    //当登录成功的时候
    res.send({
        status:200,
        messgae:'login success',
        token:jwt.sign({username:userinfo.username},secretKey,{expiresIn:'30s'})
    })
})
//3.解析token
app.use(expressJWT({secret:secretKey}))
// unless({path:[正则]}) //规定哪些接口不需要验证token

//4.当配置成功express-jwt,接口的req参数中会多一个user变量,把解析出来的内容放在req.user里面

//捕捉JWT解析失败后产生的错误:在express错误中间件中,根据错误去判断
app.use((err,req,res,next) => {
    //token 错误解析
    if(err.name === 'UnauthorizedError') {
        return res.send({status:'401',message:'无效token'})
    }
    //其他错误原因
    res.send({})
})

其他精彩:

image.png