对于后端开发了解并不多或者只会用js开发页面效果的小伙伴来说,要开发一款真正属于自己的网站或者APP不用Node来开发服务器实在是太可惜啦,因为Nodejs能够在很短的时间内开发出网站必要的服务端接口API。使用Nodejs开发最先就是要安装Nodejs, 这里提供Nodejs官网:Node.jsnodejs.org
一 、Nodejs介绍
Nodejs是一个javascript运行环境,运行在服务端作为web server, 通常前端开发人员都知道的ECMAScript是js官网的标准, 写js和nodejs都必须要遵守,这里面包括了变量定义,循环,判断,函数,但是ECMAScript不能够操作DOM,不能监听事件,不能发送ajax请求,不能处理http请求,也不能操作文件等等。
前端js ECMAScript + WebAPI (DOM操作 BOM操作 事件绑定 Ajax等)
Nodejs ECMAScript + NodeAPI (处理http,处理文件等server端的操作)
二、server端开发事项
1、服务稳定性和安全
server端可能会遭受各种恶意攻击和误操作,比如说防止sql注入引起mysql表发生问题,xss攻击客户端,或者后端代码本身的问题,如果是单个客户端可以意外挂掉,但是服务器不能,所以可以使用pm2做进程守护(进程挂掉会自动重启)
2、考虑CPU和内存(优化,扩展)
客户端独占一个浏览器,内存和CPU都不是问题, 而server端要承载很多请求,CPU和内存都是很稀缺的资源,要考虑到使用stream写日志和redis存session。
3、日志记录
前端也会参加写日志,但只是日志的发起方并不关心后面的问题,而server端要记录日志,存储日志,分析日志,前端并不用太关心过程。
4、集群和服务拆分
这个要结合产品的发展方向和用户量的多少来决定,服务器需要多少流量,需要多少机器来拆分流量的问题要具体看软件开发公司的业务需求来定夺,但不管怎么样也是服务端需要考虑的开发问题之一.
三、了解http请求的全过程(从url输入到接口返回步骤)
首先需要了解的是TCP三次握手: 1.客户端DNS解析获取IP地址,是否可用。 2.服务端告知客户端可用。 3.客户端再次告知服务端ok,会访问。
通过三次握手客户端才会发送http请求到服务端,服务端接收到http请求处理并返回结果,客户端接收到返回数据,并且处理数据(渲染页面并执行js)
了解到这些基本的http请求后,我们就可以自己搭建个最普通的http请求啦
const http = require('http');
const server = http.createServer((req, res) => {
res.setHeader('content-type','application/json');
res.end(JSON.stringify({
errno: 0,
data: [1,2,3]
}));
});
server.listen(8000)
三、nodejs http请求获取方式
这里主要介绍GET和POST请求获取客户端参数的方式,首先是GET方法获取参数方式
const querystring = require('querystring')
const server = http.createServer((req, res) => {
const data = [];
res.setHeader('content-type','application/json');
// 获取 path
const url = req.url
req.path = url.split('?')[0]
// 解析 query
// http://localhost:8000/api?a=1&b=2
req.query = querystring.parse(url.split('?')[1])
if (req.method==='GET' && req.path === '/api') {
res.end(JSON.stringify({
errno: 0,
data: req.query // { a:1, b:2 }
}));
}
});
server.listen(8000)
其次是POST方法获取参数方式,由于nodejs中获取POST方法是异步的,所以要先执行获取POST参数,就必须先执行该方法,于是就引入了ES6的Promise对象来定义。
// 用于处理 post data 之后有这个函数的引入
const getPostData = req => {
const promise = new Promise((resolve, reject) => {
//后面的操作能够保证req.body是一个空对象
if (req.method !== 'POST') {
resolve({})
return
}
if (req.headers['content-type'] !== 'application/json') {
resolve({})
return
}
let postData = ''
//异步方法
req.on('data', chunk => {
postData += chunk.toString()
})
//异步方法
req.on('end', () => {
if (!postData) {
resolve({})
return
}
resolve(
JSON.parse(postData)
)
})
})
return promise
}
四、cookie使用方法和session的引入
客户端访问服务端的时候,服务端要首先保证客户端的访问者是登录状态下进行的操作,所以在访问的时候都需要进行登录校验以及登录信息的存储,此时我们就能使用cookie来解决这个问题。
何为cookie呢?cookie是存储在浏览器的一段字符串,通常是以键值对形式存在(最大为5kb), cookie拥有跨域不共享的机制(如果做到了跨域共享,那么对于其他网站来说,内部信息泄露对网站安全受影响很大,浏览器肯定屏蔽这种漏洞存在),通常客户端整个cookie传到服务端格式结构如下:k1=v1;k2=v2;k3=v3,每次发送http请求,会将请求域的cookie一起发送给server, server可以修改cookie并返回给浏览器, 浏览器也可以通过js修改cookie(有限制,服务端可以设置httpOnly来限制客户端的修改)
cookie的Nodejs获取办法
const cookieStr = req.headers.cookie;
req.cookie = {}
if (cookieStr) {
cookieStr.split(';').forEach(item => {
const arr = item.split('=')
const key = arr[0].trim()
const value = arr[1].trim()
req.cookie[key] = value
});
}
我们通过上述代码方式将客户端的cookie存入req.cookie的对象当中,如果cookie的值需要修改或者新增,依据如下代码便能够实现:
// 获取 cookie 的过期时间
const getCookieExpires = () => {
const d = new Date()
d.setTime(d.getTime() + (24 * 60 * 60 * 1000))
return d.toGMTString()
}
//代码默认cookie存储时间为一天
res.setHeader('Set-Cookie', `username=${username};path=/;httpOnly;expires=${getExpireData()}`)
不知道很多小伙伴看到这段代码后,是不是发现虽然cookie能够实现cookie客户端和服务端正常的交互功能了,但是username这个信息对于开发各类网站的开发人员来说,太过敏感了,很多客户是不希望暴露自己的用户名给所有人看,而且从安全的角度来说非常危险,通常网站登录注册只要用户名密码,等于一半的信息被别人掌握了,所以要避免这个问题就引入了session这种方式来作为服务端判断用户登录校验的一种方式。
let SESSION_LOGIN = {}
let sessionId= req.cookie.sessionId;
if (sessionId) {
if (!SESSION_LOGIN[sessionId]) {
SESSION_LOGIN[sessionId] = {}
}
} else {
needSetCookie = true
userId = `${Date.now()}_${Math.random()}`
SESSION_LOGIN[sessionId] = {}
}
req.session = SESSION_LOGIN[sessionId]
这种方式看似好像实现了服务端存储session的方式,也同时避免了客户端直接传递username的困扰,但是一个服务器进程下,一个SESSION_LOGIN对象存储所有用户的cookie操作是非常不现实的,一方面服务端SESSION_LOGIN对象内存会溢出,无法存储过多数据(一个进程内分配的内存有限),而且在生产环境中基本都是采用多进程的环境,而多进程环境下客户端每次访问的服务端进程又不确定(负载均衡的问题,所以面对这样的问题一个对象明显不能作为我们存储用户信息的方式,此时我们想到了使用redis内存数据库的方式。
redis作为web server最常用的缓存数据库,数据放在内存中,相比于mysql,访问速度快(内存和硬盘不是一个数量级的),但是成本更高,因为内存存储成本高于硬盘,并且可存储的数据量也会更小(内存的硬伤), 将web server和redis拆分成两个单独的服务双方都是独立的,都是可扩展的(例如可以扩展成一个集群)。
这是关于redis如何存储服务器的部分实现代码
let sessionId = req.cookie.sessionId;
let needSetCookie = false;
if (!sessionId ) {
needSetCookie = true;
sessionId = `${Date.now()}_${Math.random()}`
//将sessionId 设置到redis数据库中
set(sessionId , {})
}
req.sessionId = sessionId;
//从redis中通过sessionId作为redis的key获取value
get(req.sessionId).then(sessionData => {
if (sessionData == null) {
//将sessionId 设置到redis数据库中
set(sessionId , {})
req.session = {}
} else {
//存入req对象中可以为之后登录验证判断做准备
//登录验证从redis获取的内容信息sessionData={ username:'xxx', realname:'xxx' }
req.session = sessionData
}
//返回获取post参数的promise对象进行下一步操作
//刚才的post参数获取方式
return getPostData(req)
})
//之后可以继续执行关于存入postData的操作
.then(postData => {
req.body = postData;
....
}
session适合使用redis的原因主要有几点:
1.session访问繁忙,对性能要求极高。
2.session可不考虑断点丢失数据的问题(内存的硬伤)
3.session数据量一般不会太大(相比于mysql来说太小了)
目前个人博客更新至此,后面内容尽情期待