nodejs学习笔记(慕课网nodejs从零开发web Server博客项目)
码农天地 -二、node的简介
node是一个运行js的环境。浏览器也是运行js的环境。
两者的差异在于:
浏览器会有window和document的调用。但是node没有node有许多模块的api,例如文件系统访问功能node可以切换运行环境(切换版本?)node不用bable转换node使用的是commonJS,要使用require2-2 nodejs与js的区别nodejs = nodejsapi + ecmascript
处理http,处理文件等。
module.export()
require
2-4,5 debuger之inspect协议--inspect==9229
chrome://inspect/#devices
四、开发博客项目之接口4-1 http 概述从url到浏览器的过程?
首先dns解析,解析完了发送请求,通过http协议建立tcp链接,然后传输文件。浏览器拿到数据然后建立dom树,建立css树。合并成render树,然后渲染,注入js。
DNS解析,建立TCP连接,发送http请求
server接收到http请求,处理并返回。
客户端接收到返回数据,处理数据,(如渲染页面,执行js)
Remote address 就是dns查询的结果
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,实现登录验证、
domin:cookie生效的域名
path:是cookie生效路径
有了localStorage ,本地修改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 sessioncookie 中 会暴露username,很危险userid
、sid
、conentid
,对应server端username.
redis 解决多进程
不能够互通session
的问题(挤爆,多进程之间无法数据共享。),是服务端常用的缓存工具。
web server
最常用的缓存数据库,数据存放在内存中,相比mysql 读写速度快,但是比较昂贵,断电丢失。
session 对性能的要求极高
,因为每次都会发送请求。断电丢失没关系,重新登录即可。所以不能够直接存在mysql中。
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 stopsudo 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,代码里面就是配一配。