当前位置:网站首页>nodejs学习笔记(慕课网nodejs从零开发web Server博客项目)

nodejs学习笔记(慕课网nodejs从零开发web Server博客项目)

2020-11-09 10:50:00 米姐胡扯扯

二、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

chrome://inspect/#devices

四、开发博客项目之接口

4-1 http 概述

从url到浏览器的过程?

首先dns解析,解析完了发送请求,通过http协议建立tcp链接,然后传输文件。
浏览器拿到数据然后建立dom树,建立css树。合并成render树,然后渲染,注入js。

DNS解析,建立TCP连接,发送http请求
server接收到http请求,处理并返回。
客户端接收到返回数据,处理数据,(如渲染页面,执行js)

image.png
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 初始化路由

image.png

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 开发路由

创建SuccessModelErrorModel

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,实现登录验证、

特点:

  1. 最大5kb
  2. 跨域不共享,
  3. 可保存结构化数据
  4. 每次发送http请求,会将请求域的cookoie 一起发送给cookie
  5. server 端可以修改cookie,并返回给客户端
  6. 客户端也可以通过js修改cookie,并返回给server端(有限制)
  7. 看请求的哪个域,请求哪个域,带哪个cookie
请求头内部的cookie

image.png

本地的缓存cookie

image.png

domin:cookie生效的域名
path:是cookie生效路径

修改cookie

image.png
有了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,很危险
useridsidconentid,对应server端username.

  • session直接是js变量,放在nodejs进程内存中;
  • 内存过大,会把进程的空间挤爆
  • 多进程之间,数据无法共享

6.8 redis

redis 解决多进程不能够互通session的问题(挤爆,多进程之间无法数据共享。),是服务端常用的缓存工具。

web server 最常用的缓存数据库,数据存放在内存中,相比mysql 读写速度快,但是比较昂贵,断电丢失。

  • 单独的服务,与nodejs之间只是调用关系。就像mysql一样,所以不存在被挤爆的情况。
  • 多进程之间访问同一个redis。所以session会同步。
  • redis 可拓展,就算用户量增加,那也可以增加机器,可以拓展成集群。

session 对性能的要求极高,因为每次都会发送请求。断电丢失没关系,重新登录即可。所以不能够直接存在mysql中。

image.png

image.png

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跨域不共享。
image.png

使用http-server 起一个前端的服务。端口设置为8001。
nodejs:8000 端口

大家都去8080端口。

6.15 高性能的web服务器nginx),开源,免费

假如请求/根目录,请求html,那么,nginx直接返回返回html

假如请求/api/blog/...先到nginx端口8000,然后再到node服务的端口,8001.

对客户端不可见的代理,称为反向代理

使用easyconnect连接,被称为正向代理,客户端可以控制
image.png

image.png

image.png

  • 测试配置文件格式是否正确: 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,代码里面就是配一配。

版权声明
本文为[米姐胡扯扯]所创,转载请带上原文链接,感谢
https://segmentfault.com/a/1190000037768086