【nodejs】知识点整理
获取GET传过来的参数
const http = require('http')
const querystring = require('querystring')
const server = http.createServer((req, res) => {
// 通过get传递的参数
let url = req.url
// 请求方式
let method = req.method
// 利用querystring内置参数,获取get请求的参数----返回对象格式
let parameter = querystring.parse(url.split('?')[1])
//打印返回
res.end(JSON.stringify(parameter))
})
server.listen(3000, () => {
console.log('运行在3000端口')
})
获取POST传过来的参数
因为post接受参数不是一次就可以完全接受到的,所以要利用事件进行反复接受,接受完了也有对应的事件触发
const http = require('http')
const server = http.createServer((req, res) => {
// 判断接收数据的类型
console.log('content-type:', req.headers['content-type'])
// 判断是否是post请求
if (req.method === "POST") {
// 一个用来接收数据的字符串
let strData = "";
// 每次post发送数据都会触发
req.on('data', str => {
strData += str.toString()
})
// 参数接收完之后触发
req.on('end', () => {
res.end(strData)
})
}
})
server.listen(3000, () => {
console.log('运行在3000端口')
})
登录之cooike理解
cooike具体项的作用
- 获取前端传过来的cooike
因为从前端接受到的cooike是字符串形式的,所以要进行拆分。然后组成cooike
//接受前端传过来的cooike并保存成json格式
req.cooike={}
var cooikeStr=req.headers.cookie || ''
cooikeStr.split(';').forEach(v=>{
if (!v){
return
}else {
var item=v.split('=')
//这里的去空格是因为。每个两个cooike数据之间有一个空格
var key=item[0].trim()
var value=item[1].trim()
req.cooike[key]=value
}
})
- 后端设置cooike
期中数组中的每一项都是一个cooike。
数据格式:key=value
。httpOnly是指:在前端不能修改cooike的值。path=/是指:当前路径下的所有路径都可以使用。 expires是指:cooike的过期时间
//设置cookike的expires时间格式
const getCookieExpires=()=>{
var d=new Date()
d.setTime(d.getTime()+(24*60*60*1000))
return d.toGMTString()
}
//设置cooike
res.writeHead(200, {'set-cookie': [`username=${result.username};httpOnly;path=/;expires=${getCookieExpires()}`]})
登录之session理解
- 首先为什么要使用session?
- cooike端存重要数据不安全。
- cooike端有大小容量和长度限制。
- session端都可以解决以上的问题。
- 解决方法:
- cooike端传一个与session端真实数据相对应的数据
- 具体步骤:
- 因为把用户的用户名等。重要的信息存储在cooike端是很不安全的。所以我们在cooike端只存储一个与后端session端相对应的userid号就可以了。等到了session端,只把cooike穿过来的id与session中的数据对应的数据那出来就可以了
图解:
session到redis的转变
- 什么是redis
- 他是一个内存数据库,所以访问速度快
- 相比于mysql等(硬件数据库)访问速度快
- 单成本高,可存储的数据量较少,但是可以扩展。建立集群
- session为什么要用redis?
- 目前session的数据放在node的进程中,每个进程的存储是有限制的
- 正式上线后运行时多进程,多个进程之间数据不能共享
在项目中使用redis
首先要明确session表存的是cooike传过来的id与其id对应真实值的映射表
- 步骤:
- 开启redis服务
- 接受cooike传过来的id从redis映射真实的数据
- 最后根据真实的数据进行操作
//解析session
var userid=req.cooike.userId
var needSetCooike=false
if(userid){
//传入的cooike有userid
get(userid).then(result=>{
if(result==null){
//有userid但是没有对应的值,就赋值为空
set(userid,{})
}
})
}else {
//传入的cooike没有有userid
needSetCooike=true
userid=`${Date.now()}_${Math.random()}`
set(userid,{})
}
//把id对应的真实值,传递给req.session
get(userid).then(result=>{
req.session=result
})
//修改的时候就可以req.session最后再把他的值赋值给redis
//最后等登陆之后把修改的数据从新赋值给redis
set(userid,req.session)
日志
日志是服务端比较特有的一个属性,他能记录用户访问api的各种信息
-
类型
- 访问日志 access log (server 端最重要的日志)
- 自定义日志(包括自定义事件、错误信息)
-
日志操作的步骤
- nodejs文件操作(nodejs stream)
- 日志的开发与使用
- 日志文件拆分,日志内容分析
-
流的概念
流就相当于把资源一点一点的流给你而不是直接把资源全部给你,例如:在爱奇艺线上观看视频,在线直播等等,都用到了流的概念
流操作
- 写入流
以下的案例中创建的读取流就相当于第一个发送数据的桶,而写入流就相当于下面接受数据的桶
- 用流复制文件
复制文件
const fs=require('fs')
const path=require('path')
const fileName1=path.join(__dirname,"test1.txt")
const fileName2=path.join(__dirname,"test2.txt")
//创建读取流
const readStream=fs.createReadStream(fileName1)
//创建写入流
const writeStream=fs.createWriteStream(fileName2)
//把读取的流内容写入到写入流中
readStream.pipe(writeStream)
//copy 完成后就会触发次效果
readStream.on('end',()=>{
console.log('copy over');
})
- 读取get请求并发送用流读取到的文件的内容(网络IO和文件IO的结合)
const http=require('http')
const fs=require('fs')
const path=require('path')
const fileName1=path.join(__dirname,"test1.txt")
//创建读取流
const readStream=fs.createReadStream(fileName1)
const server=http.createServer((req,res)=>{
if (req.method=='GET'){
readStream.pipe(res)
}
})
server.listen(8000,()=>{
console.log('服务器运行在8000端口')
})
- 写log的操作
writeStream(事先创建好的写入流).write(logs(log的内容)+'\n')
- 分析log文件
原生的node提供了一个用在逐行分析日志的内置模块
readline
我们就是利用readline
进行分析log文件的
- 创建一个读取log文件的读取流对象
- 根据读取流对象再生成一个readline对象
- 根据读取流对象的
line
和close
事件来进行事物分析
const fs=require('fs')
const path=require('path')
const readLine=require('readline')
//总条数
var num=0
//chrome访问所占的数量
var Chrome=0
const fileName=path.join(__dirname,'../','logs','access.log')
const readStream=fs.createReadStream(fileName)
var rl=readLine.createInterface({
input:readStream
})
rl.on('line',data=>{
if (!data){
return
}
//总行数增加
num++
if (data.split(' -- ')[2]&& data.split(' -- ')[2].indexOf('Chrome')>0){
//增加chrome的数量
Chrome++
}
})
rl.on('close',()=>{
console.log('Chrome的占比为',Chrome/num)
})
安全
sql注入,xss攻击,密码加密,DDOS
防止sql注入
所谓sql注入就是利用前端输入的带有攻击性的sql内容来进行sql注入
例如:在登录输入用户名时,在用户名处写入用户名' --
就可以不用输密码就能登录了。因为--
在mysql中等于注释,所以你在拼接时就把密码的部分给注释了所以自然就可以知道用户名就可以登陆了
而预防的方式就是使用mysql自定义的escape
函数就可以避免sql注入了,如果一旦用了此函数,注意在拼接时就不能用再用双单引号拼接了
const { writeSql,escape } = require('../db/db')
const userLogin = (username, password) => {
//注意:此函数写入之后在拼接sql是就不用加引号了
username=escape(username)
password=escape(password)
var sql=`select username,realname from user where username=${username} and password=${password}`
return writeSql(sql).then(result=>{
return result[0]||{}
})
}
防止xss攻击
所谓xss攻击就是指前端输入了一些可以恶意操作的js代码而形成的攻击
而后端预防的方式就是调用xss
函数模块把特殊符号进行转移,让其无法形成可以使用的js代码
密码加密
密码加密运用crypto
模块进行加密
const crypto=require('crypto')
//密匙
var secretKey='Wfji13_#'
//生成md5函数
function md5(content) {
let md5=crypto.createHash('md5')
return md5.update(content).digest('hex')
}
//供外界使用的函数
function genPassword(password) {
const str=`password=${password}&key=${secretKey}`
return md5(str)
}
module.exports={
genPassword
}