二、node的简介
node是一个运行js的环境。浏览器也是运行js的环境。
两者的差异在于:
- 浏览器会有window和document的调用。但是node没有
- node有许多模块的api,例如文件系统访问功能
- node可以切换运行环境(切换版本?)
- node不用bable转换
- node使用的是commonJS,要使用require
2-2 nodejs与js的区别
nodejs = nodejsapi + ecmascript
处理http,处理文件等。
2-3 commonjs
module.export()
require
2-4,5 debuger之inspect协议
--inspect==9229
四、开发博客项目之接口
4-1 http 概述
从url到浏览器的过程?
首先dns解析,解析完了发送请求,通过http协议建立tcp链接,然后传输文件。
浏览器拿到数据然后建立dom树,建立css树。合并成render树,然后渲染,注入js。DNS解析,建立TCP连接,发送http请求
server接收到http请求,处理并返回。
客户端接收到返回数据,处理数据,(如渲染页面,执行js)
Remote address 就是dns查询的结果
4-4 处理http请求的综合示例
const http = require("http");
const port = 3000
const server = http.createServer((req, res) => {
res.setHeader("Content-Type", "application/json")
const url = req.url
const method = req.method
res.end(`${url}${method}`);
})
server.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}/`)
})
console.log("ok")
4-5 搭建开发环境
- 使用nodemon检测文件变化,自动重启node。
- corss-env设置环境变量
npm i nodemon cross-env --save-dev
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "cross-env NODE_ENV=dev nodemon ./bin/www.js"
},
4-6 初始化路由
hostname:nodejs.cn
pathname:/api/blog/list
query:?name=zhangsan&keyword=123
const method = res.method // post get
const url = res.url // url
const path = res.url.split("?")[0] //
4-7 开发路由
创建SuccessModel
、ErrorModel
。
class BaseModel {
constructor(data, message) {
if (typeof data === "string") {
this.message = data;
message = null;
data = null
}
if (data) {
this.data = data;
}
if (message) {
this.message = message;
}
}
}
class SuccessModel extends BaseModel {
constructor(data, message) {
super(data, message);
this.error = 0
}
}
class ErrorModel extends BaseModel {
constructor(data, message) {
super(data, message);
this.error = -1
}
}
module.exports = {
SuccessModel,
ErrorModel
}
4-8 开发路由(博客详情路由)
const fs = require('fs');
const path = require('path');
function getFileContent(fileName) {
const fullFileName = path.resolve(__dirname, 'file', fileName)
const promise = new Promise((resolve, reject) => {
fs.readFile(fullFileName, (err, data) => {
if (err) {
reject(err);
}
resolve(JSON.parse(data));
})
})
return promise;
}
getFileContent('a.json').then(aDate => {
console.log(aDate);
return getFileContent(aDate.next);
}).then((bData) => {
console.log(bData);
return getFileContent(bData.next);
}).then((cData) => {
console.log(cData);
});
4-9 开发路由(处理POSTData)
// 用于处理post data
const getPostData = (req) => {
const promise = new Promise(() => {
if (req.method !== 'POST') {
resolve({})
return
}
if (req.headers['content-type'] !== "application/json") {
resolve({})
return
}
})
// 监听流数据
let postData = ''
res.on('data', chunk => {
postData += chunk.toString()
})
res.on('end', () => {
if (!postData) {
resolve({})
return
}
resolve(
JSON.parse(postData)
)
})
return promise
}
更新和删除数据等....
5 数据库
MySql
use myblog;
-- 查表
-- show tables;
-- insert into blogs(title,content,createtime,author)values("市民胡女士2的标题","市民胡女士2的内容",1585645673144,"mjmjmj");
SELECT * FROM myblog.users LIMIT 0,1000;
-- select username from users;
-- select * from users where password like '%1%' order by id desc;
-- select * from blogs;
-- ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password' PASSWORD EXPIRE NEVER;
-- ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '11111111';
-- flush privileges
-- insert into blogs (title, content, createtime, author)values ('hhhhhh', 'jjhhhhh', 1585646800912, 'mjmjmj');
-- select username, realname from users where username='mjmjmj' and password='321654a.'
-- 更新
-- SET SQL_SAFE_UPDATES = 0;
-- update users set state = 0;
-- update users set username ='huhuhu' where id = '2';
-- 删除
-- delete from blogs where id='1';
5.4 nodejs 操作 数据库
const mysql = require('mysql');
const con = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '11111111',
port: '3306',
database: 'myblog',
})
// 开始连接
con.connect()
// 执行sql
const sql = 'select * from users;';
con.query(sql, (err, result) => {
if (err) {
console.log(err);
return;
}
console.log(result);
})
con.end();
const mysql = require('mysql');
const { MYSQL_CONF } = require("../conf/db");
// 创建链接对象
const con = mysql.createConnection(MYSQL_CONF);
// 建立连接
con.connect();
// 统一执行sql 的函数
function exec(sql) {
const promise = new Promise((resolve, reject) => {
con.query(sql, (err, result) => {
if (err) {
reject(err);
return
}
resolve(result)
})
})
return promise;
}
con.end();
module.exports = {
exec
}
6.2 Cookie 介绍
什么是cookie?
js如何操作cookie,如何在浏览器中查看cookie?
server端操作cookie,实现登录验证、
特点:
- 最大5kb
- 跨域不共享,
- 可保存结构化数据
- 每次发送http请求,会将请求域的cookoie 一起发送给cookie
- server 端可以修改cookie,并返回给客户端
- 客户端也可以通过js修改cookie,并返回给server端(有限制)
- 看请求的哪个域,请求哪个域,带哪个cookie
请求头内部的cookie
本地的缓存cookie
domin:cookie生效的域名
path:是cookie生效路径
修改cookie
有了localStorage ,本地修改cookie的情况不多
nodejs修改cookie
// 解析 cookie
req.cookie = {}
const cookieStr = req.headers.cookie || '' // k1=v1;k2=v2;k3=v3
cookieStr.split(';').forEach(item => {
if (!item) {
return
}
const arr = item.split('=')
const key = arr[0].trim()
const val = arr[1].trim()
req.cookie[key] = val
})
nodejs setCookie
// 登录
if (method === 'POST' && req.path === '/api/user/login') {
const { username, password } = req?.body
const result = login(username, password)
return result.then(data => {
if (data.username) {
// 操作cookie
res.setHeader('SetCookie', `username=${data.username}`)
return new SuccessModel()
}
return new ErrorModel('登录失败')
})
}
6.4 cookie 做限制
设置httpOnly
res.setHeader('SetCookie', `username=${data.username}; path=/; httpOnly`)
// 获取 cookie 的过期时间
const getCookieExpires = () => {
const d = new Date()
d.setTime(d.getTime() + (24 * 60 * 60 * 1000))
console.log('d.toGMTString() is ', d.toGMTString())
return d.toGMTString()
}
res.setHeader('Set-Cookie', `userid=${userId}; path=/; httpOnly; expires=${getCookieExpires()}`)
6.6 session
cookie 中 会暴露username,很危险userid
、sid
、conentid
,对应server端username.
- session直接是js变量,放在nodejs进程内存中;
- 内存过大,会把进程的空间挤爆
- 多进程之间,数据无法共享
6.8 redis
redis 解决多进程
不能够互通session
的问题(挤爆,多进程之间无法数据共享。),是服务端常用的缓存工具。
web server
最常用的缓存数据库,数据存放在内存中,相比mysql 读写速度快,但是比较昂贵,断电丢失。
- 单独的服务,与nodejs之间只是调用关系。就像mysql一样,所以不存在被挤爆的情况。
- 多进程之间访问同一个redis。所以session会同步。
- redis 可拓展,就算用户量增加,那也可以增加机器,可以拓展成集群。
session 对性能的要求极高
,因为每次都会发送请求。断电丢失没关系,重新登录即可。所以不能够直接存在mysql中。
6.9
const redis = require('redis');
const { REDIS_CONF } = require('../conf/db');
// 创建客户端
const redisClient = redis.createClient(REDIS_CONF);
redisClient.on('error', (error) => {
console.log(error);
})
function set(key, val) {
if (typeof val === "object") {
val = JSON.stringify(val);
}
redisClient.set(key, val, redis.print);
}
function get(key) {
const promise = new Promise((resolve, reject) => {
redisClient.get(key, (err, val) => {
if (err) {
reject(err)
return
}
if (val == null) {
resolve(null);
return
}
try {
resolve(JSON.parse(val))
} catch (ex) {
resolve(val)
}
resolve(val);
})
});
return promise;
}
module.exports = {
set,
get
}
set myname mj
get myname // mj
get * // 获取所有key value
// 获取 session
req.sessionId = userId
get(req.sessionId).then(sessionData => {
if (sessionData == null) {
// 初始化 redis 中的 session 值
set(req.sessionId, {})
// 设置 session
req.session = {}
} else {
// 设置 session
req.session = sessionData
}
// console.log('req.session ', req.session)
// 处理 post data
return getPostData(req)
})
.then(postData => {
......
}
6.14 为什么使用nginx
因为前端起的一个服务和nodejs起的服务不是同一个端口,而cookie跨域不共享。
使用http-server 起一个前端的服务。端口设置为8001。
nodejs:8000 端口
大家都去8080端口。
6.15 高性能的web服务器nginx),开源,免费
假如请求/
根目录,请求html,那么,nginx直接返回返回html
假如请求/api/blog/...
先到nginx端口8000,然后再到node服务的端口,8001.
对客户端不可见的代理,称为反向代理
使用easyconnect连接,被称为正向代理
,客户端可以控制
- 测试配置文件格式是否正确:
ngnix -t
- 启动nginx 重启 nginx -s reload
- 停止 ngnix -s stop
sudo vi /usr/local/etc/nginx/nginx.conf
主要就是添加这些代码,然后把上面的一些注释掉。
location / {
proxy_pass <http://localhost:8001>;
}
location /api/{
proxy_pass <http://localhost:8000>;
proxy_set_header Host $host;
}
感觉搞了半天,就是看懂了什么是redis,什么是session,什么是ngnix,代码里面就是配一配。