предисловие
В мгновение ока сентябрь снова прошел. В последнее время я мало вел блог, потому что слишком много всего. Я чувствую, что мое сердце всегда в пути, и у меня никогда нет времени остановиться и присесть. С момента выпуска и до настоящего времени, как только я присоединился к компании, меня окружало множество деловых потребностей. Когда я увидел, что расписание уже расписано на следующий год, я задумался и стал фантазировать о том, чтобы использовать свободное время для того, чтобы заняться любимым делом. Постепенно я обнаружил, что мое слабое тело вообще не могло его выдержать. Я читал «node.js простыми словами», который я купил очень рано, и я читал его очень мало раз, Всякий раз, когда я думаю об этом, на моем лице всегда появляется след меланхолии. Пользуясь Национальным днем, у меня есть возможность читать книги на узле. На бумаге я нашел это поверхностным, и мне не терпелось узнать о базовых знаниях об узле. В конце концов, я стиснул зубы и взял еду на неделю, чтобы купить Учителя Шуанъюэ:Обязательные курсы для продвижения фронтенда до full-stack инженеров Node.js разрабатывает проект блога веб-сервера с нуля. На следующей неделе будет голоден, когда смотрю на видео, так что знания, чтобы наполнить свой живот. Курс объясняет очень вдумчивый, достойный рекомендации. Но для того, чтобы дать всем, как мне деньги, чтобы поесть. Поэтому я приносим вам подробный шаг за шагом снова, я верю, что вам понравится полный урожай. Если вы богаты красивый, белый фу-мей, потом покупайте опыт покупки ощущение было бы еще лучше.
Что вы можете узнать из этой статьи
- Интерфейсы, включая обработку http-запросов node.js, построение среды разработки, обработку маршрутов и разработку различных интерфейсов.
- Хранение данных, создание базы данных и таблицы MySql, Node, связывающий MySql, стыковка интерфейса Mysql
- Логин, куки и сеансы, используйте Redis для хранения сеансов
- Конфигурация Nginx, совместная отладка интерфейсной и серверной частей
- Журналы, файловые операции Node.js, потоки потоков, разработка функций логов, разбиение лог-файлов, анализ логов
- Безопасность, предотвращение атак SQL, XSS
- Экспресс-каркас, принцип промежуточного программного обеспечения, разработка интерфейса API и общие плагины
- фреймворк коа2,
- Онлайн-развертывание, введение и настройка PM2, многопроцессная модель PM2
1. Интерфейс блог-проекта
Для разработки серверной части блог-проекта необходимо сначала внедрить каждый API в дизайн технического решения. В этой главе в основном объясняется, как использовать http-запрос, обработанный собственными узлами, включая анализ маршрута и возврат данных, а затем код демонстрирует разработку каждого API. Но эта глава еще не подключена к базе данных, поэтому API возвращает поддельные данные.
1. Подготовка инструмента
- Используйте nodemon для обнаружения изменений файлов и автоматического перезапуска узла
- Используйте cross-env для установки переменных среды, совместимых с Mac Linux и Windows.
npm install nodemon cress-env -d --save
Создайте новый файл с именем проекта node-blog и инициализируйте проект с помощью npm init -y. И настраиваем скрипт в package.json, используем npm run dev Запускаем наш проект
"dev": "cross-env NODE_ENV=dev nodemon ./bin/api.js",
"prd": "cross-env NODE_ENV=production nodemon ./bin/api.js"
2. Модульность - введение в каталог
.node-blog
├── bin // 项目启动文件
├── node_modules
├── src
| ├── conf // 数据库配置
| └── controller // 接口api
| └── db // 数据库链接
| └── model // 输出格式
| └──router // 路由
├── app.js
├── package.json
3. Разработка проекта
👉Создайте новый api.js под bin в качестве модуля для узла, чтобы запустить службу.
const http = require('http')
const serverHandle = require('../app')
const PORT = 8000
const server = http.createServer(serverHandle)
server.listen(PORT)
👉Создайте нашу конфигурацию службы конфигурации в app.js:
const serverHandle = (req, res) => {
// 设置返回格式 JSON
res.setHeader('Content-type', 'application/json')
}
module.exports = serverHandle
👉 Создаем в роутере файлы blog.js и user.js, где blog.js используется как маршрут блога.Вот пример интерфейса для получения списка блогов.Остальные интерфейсы просто меняем на имя:
const handleBlogRouter = (req, res) => {
const method = req.method // GET POST
const url = req.url
const path = url.split('?')[0]
// 获取博客列表
if (method === 'GET' && req.path === '/api/blog/list') {
return {
msg: '这是获取博客列表的接口'
}
}
}
module.exports = handleBlogRouter
👉 Ссылка созданный маршрут в App.js
const handleBlogRouter = require('./src/router/blog')
const handelUserRouter = require('./src/router/user')
const serverHandle = (req, res) => {
// 设置返回格式 JSON
res.setHeader('Content-type', 'application/json')
// 处理 blog 路由
const blogData = handleBlogRouter(req, res) => {
if (blogData) {
res.end(JSON.stringify(blogData)
return
}
}
// 处理 user 路由
const userData = handleUserRouter(req, res) => {
if (userData) {
res.end(JSON.stringify(userData)
return
}
}
// 404
res.writeHead(404, {"Content-type": "text/plain"})
res.write("404 Not Found\n")
res.end()
}
module.exports = serverHandle
Запустите наш сервис здесь, введите соответствующий интерфейс API, и вы сможете получить поддельные данные, которые мы вернули. Про детальную разработку интерфейса. Создайте новую обработку blog.js и user.js в контроллере, который здесь не будет раскрываться. Подробности можно посмотретьРазработка интерфейса
2. Хранение данных блог-проектов
После реализации API необходимо подключиться к базе данных, чтобы реализовать реальное хранение данных и запрос, и больше не использовать поддельные данные. В этой главе в основном объясняется установка и использование mysql, а также подключение mysql к nodejs и, наконец, применение mysql к каждому разработанному API.
Чтобы сократить объем этой статьи, мы не будем здесь рассказывать, как установить mysql. На самом деле, шаги очень просты, и это не является основной темой этой статьи. Вот хорошее руководство по установке для справки:Учебник по установке MySQL, Кроме того, те, кто не привык работать с консолью, могут сами создать графический интерфейс. Я использую MySql Workbench.
1. Создайте базу данных и таблицу данных
- Используйте MySql Workbench для создания базы данных myBlog и создайте две таблицы данных, блоги и пользователи, в этой базе данных для хранения данных входа блога и пользователя соответственно.
- Таблица блогов создается с самоувеличивающимся идентификатором в качестве первичного ключа, и всего имеется пять полей: заголовок, содержание, время создания и автор; таблица пользователей также создается с самоувеличивающимся идентификатором в качестве первичного ключа, и там четыре поля в имени пользователя, пароле и реальном имени.На следующем рисунке показаны блоги и данные о пользователях Структура каждого поля таблицы:
2. Узел подключается к базе данных
- Установите mysql в проект. > npm установить mysql
- Создайте новый файл mysql.js в каталоге db. для подключения к mysql
const mysql = require('mysql')
// 创建链接对象
const con = mysql.createConnection(
{
host: 'localhost',
user: 'root',
password: '123456',
port: '3306',
database: 'myblog'
}
)
// 开始链接
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
}
module.exports = {
exec
}
3. Интерфейс стыковки с MySQL
Поддельные данные, упомянутые в предыдущей главе, заменяются реальными данными в базе данных. Блог и пользователь в каталоге контроллера представляют только что созданный mysql.js. Заполните оператор mysql на каждом интерфейсе, чтобы завершить подключение интерфейса к mysql. Все интерфейсы обрабатываются одинаково, но исполняемые sql операторы разные.Подробности можно посмотретьКаждый интерфейс подключен к Mysql, вот пример интерфейса для получения списка данных:
const getList = (author, keyworld) => {
let sql = `select * from blogs where 1=1 `
if (author) {
sql += `and author = '${author}' `
}
if (keyworld) {
sql += `and title like '%${keyworld}%' `
}
sql += `order by createtime desc;`
return exec(sql)
}
3. Вход в блог проекта
1. Файлы cookie ограничены
Установите файл cookie имени пользователя, где getCookieExpires — время истечения срока действия файла cookie, path=/ устанавливает все маршруты, а httpOnly не позволяет внешнему интерфейсу изменять файл cookie.
res.setHeader('Set-Cookie',
username=${username}; path=/; httpOnly; expires=${getCookieExpires()}
)
Шаги: войдите в систему, передайте имя пользователя и пароль, подтвердите вход, запишите информацию о пользователе в файл cookie, чтобы вернуться к внешнему интерфейсу. Решение определяется тестом cookie.
// 解析cookie
req.cookie = {}
const cookiestr = req.headers.cookie || ''
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 для реализации ведения журнала, анализа содержимого журнала и разделения файла журнала. Он включает в себя основные точки знаний, такие как поток readline и crontab.
1. Введение и использование потока
Что такое стрим? 👉официальное объяснение
Stream — это абстрактный интерфейс для обработки потоковых данных в Node.js. Модуль потока используется для создания объектов, реализующих интерфейс потока. Потоки могут быть доступны для чтения (Readable), для записи (Writable) или для чтения и записи (Duplex). Абстрактное понимание состоит в том, что два ведра соединены водопроводной трубой, и вода из одного ведра полностью перетекает в другое ведро.
Что может сделать ручье? 👉 IO (Network IO и File IO) Указания производительности, как повысить операционную эффективность IO в рамках ограниченных аппаратных ресурсов.
демонстрация кода потокового (потокового) копирования:
const fs = require('fs')
const path= require('path')
// 两个文件名
const fileName1 = path.resolve(__dirname, 'data.txt')
const fileName2 = path.resolve(__dirname, 'data-bak.txt')
// 读取文件的 stream 对象
const readStream = fs.createReadStream(fileName1)
// 写入文件的 stream 对象
const writeStream = fs.createWriteStream(fileName2)
// 执行拷贝,通过pipe
readStream.pipe(writeStream)
// 监听每次拷贝的内容
readStream.on('data', chunk => {
console.log(chunk.toString())
})
// 数据读取完成,即拷贝完成
readStream.on('end', () => {
console.log('copy done')
})
2. Напишите журнал
Создайте новую папку журналов в каталоге узла блога и создайте в ней файлы access.log, error.log и event.log. И создайте новый utils> log.js под src. Вот пример доступа. Код:
const fs = require('fs')
const path = require('path')
// 写日志
function writeLog (writeStream, log) {
writeStream.write(log + '\n')
}
// 生成 write stream
function createWriteStream (fileName) {
const fullFileName = path.join(__dirname, '../', '../', 'logs', fileName)
const writeStream = fs.createWriteStream(fullFileName, {
flags: 'a'
})
return writeStream
}
// 写访问日志
const accessWriteStream = createWriteStream('access.log')
function access (log) {
writeLog(accessWriteStream, log)
}
module.exports = {
access
}
Введите метод доступа в файл log.js, только что написанный в app.js, и запишите журнал доступа в методе serverHandle. Когда наш интерфейс будет выполнен, он запишет информацию об интерфейсе и так далее.
access(
${req.method} -- ${req.url} -- ${req.headers['user-agent']} -- ${Date.now()}
)
3. Разделение журнала
- Разделите файлы журналов по времени, например 2019-09-18.access.log.
- Метод реализации: команда linux crontab, то есть задачи по расписанию
Создайте новый utils > copy.sh в src и напишите команду sh. Выполните sh copy.sh, журналы, созданные в предыдущем разделе, будут иметь дополнительный файл с именем 2019-09-17.access.log Имя файла завершит разделение журнала. Вот команда ш:
!/bin/sh
cd /Users/wusimin7/Documents/jd_code/node-blog/blog-node/logs
cp access.log $(date +%Y-%m-%d).access.log
echo "" > access.log
Выполните crontab -e под нашим общим проектом, чтобы создать запланированное задание. Введите следующее. wq! После сохранения используйте crontab -l для просмотра только что созданной команды crontab.
*0 * * * sh /Users/wusimin7/Documents/jd_code/node-blog/blog-node/src/utils/copy.sh
5. Безопасность блог-проектов
Безопасность является ключевым содержимым, которое необходимо учитывать на стороне сервера.В этой главе в основном объясняется, как nodejs предотвращает инъекции sql, атаки xss и шифрование паролей базы данных, чтобы хакеры не могли получить пароли в виде открытого текста.
1. SQL-инъекция
- Самая примитивная и простая атака
- Метод атаки: введите фрагмент SQL и, наконец, сращенные в кусок кода атаки
- Предотвращение ошибок: используйте escape-функцию mysql для обработки входного содержимого (рассматривается со стороны сервера)
Демонстрация метода атаки: 👉 Введите в наш sql
select username, realname from users where username='zhangsan'-- and password="123";
Имя пользователя «zhangsan» можно найти в этом операторе sql, и пароль не будет отображаться, что аннотировано --. Используйте это для выполнения атак SQL-инъекций
Вы обнаружите, что вы можете войти в систему, только введя свое имя пользователя. Это простая атака с использованием SQL-инъекций. Конечно, это связано с оператором sql вашего запроса на вход.Предотвращение функции escape 👉 Используйте функцию escape в mysql, чтобы обернуть наше имя пользователя и пароль
// db文件夹下导出escape函数。在user.js中引用
escape: mysql.escape
username = escape(username)
password = escape(password)
2. xss-атака
- Метод атаки: добавление кода js в содержимое, отображаемое на странице, для получения информации о веб-странице.
- Превентивные меры: преобразовать специальные символы, которые генерируют js, (npm install xss -d --save)
Демонстрация метода атаки: 👉 Когда вы создаете новый блог, введите в заголовок следующее содержимое, чтобы просмотреть файлы cookie этого веб-сайта.
<script>alert(document.cookie)</script>
3. Шифрование пароля
Зашифруйте файл в utils > crpy.js.
const crypto = require('crypto')
// 密匙
const SECRET_KEY = 'WJiol_8776#'
// md5 加密
function md5(content) {
let md5 = crypto.createHash('md5')
return md5.update(content).digest('hex')
}
// 加密函数
function genPassword(password) {
const str = `password=${password}&key=${SECRET_KEY}`
return md5(str)
}
module.exports = {
genPassword
}
Введите метод genPassword в контроллер > user.js для шифрования введенного пароля.
password = genPassword(password)
6. Рефакторинг блог-проектов с экспрессом
1. Экспресс-монтаж (с помощью экспресс-генератора лесов)
npm install express-generator -g
Создайте проект с помощью команды экспресс-экспресс-тест. npm install для загрузки зависимостей, запустите npm start для доступа к localhist:3000. Переустановите модификацию файла мониторинга
npm install nodemon cross-env --save-dev
Добавьте команду скриптов в package.json:
"dev": "cross-env NODE_ENV=dev nodemon ./bin/www"
2. Знакомство с app.js в экспрессе
- http-ошибки обрабатывают 404
- cookie-parser анализирует куки
- morgan автоматически генерирует журналы
- app.use (express.json ()); постобработка данных
- App.use ({Expense: false}); Другой формат, совместимый с публикацией
3. Принцип и реализация экспресс промежуточного ПО
Принципиальный анализ промежуточного ПО:
- app.use используется для регистрации промежуточного программного обеспечения, первого звонка.
- При обнаружении http-запроса определите, какие из них следует запускать, исходя из пути и метода.
- Реализовать следующий механизм, то есть предыдущий запускает следующий через следующий
const http = require('http')
const slice = Array.prototype.slice
class LikeExpress {
constructor() {
// 存放中间件列表
this.routes = {
all: [],
gte: [],
post: []
}
}
register(path) {
const info = {}
// 分析第一个参数是否为路由
if (typeof path === 'string') {
info.path = path
// 从第二个参数开始,转换成数组,存入 stack
info.stack = slice.call(arguments, 1) // 数组
} else {
info.path = '/'
// 从第一个参数开始,转换成数组,存入 stack
info.stack = slice.call(arguments, 0) // 数组
}
return info
}
use() {
const info = this.register.apply(this, arguments)
this.routes.all.push(info)
}
get() {
const info = this.register.apply(this, arguments)
this.routes.get.push(info)
}
post() {
const info = this.register.apply(this, arguments)
this.routes.post.push(info)
}
match(method, url) {
let stack = []
if (url === '/favico.ico') {
return stack
}
// 获取 routes
let curRoutes = []
curRoutes = curRoutes.concat(this.routes.all)
curRoutes = curRoutes.concat(this.routes[method])
curRoutes.forEach(routerInfo => {
if (url.indexOf(routerInfo.path) === 0) {
stack = stack.concat(routerInfo.stack)
}
})
return stack
}
// 核心的next机制
handle(req, res, stack) {
const next = () => {
// 拿到第一个匹配的中间件
const middleware = stack.shift()
if (middleware) {
// 执行中间件函数
middleware(req, res, next)
}
}
next()
}
callback() {
return (req, res) => {
res.json = (data) => {
res.setHeader('Content-type', 'application/json')
res.end(JSON.stringify(data))
}
const url = req.url
const method = req.method.toLowerCase()
const resultList = this.match(method, url)
this.handle(req, res, resultList)
}
}
listen(...args) {
const server = htpp.createServer(this.callback())
server.listen(...args)
}
}
module.exports = () => {
return new LikeExpress()
}
Семь, используйте Koa2 для рефакторинга проекта блога
1. установка koa2 (используя koa-генератор скаффолдинга)
npm install koa-generator -g
Создайте проект с помощью команды koa2 express-koa2. npm install для загрузки зависимостей, запустите npm start для доступа к localhist:3000. Переустановите модификацию файла мониторинга
npm install cross-env --save-dev
Добавьте команду скриптов в package.json:
"dev": "cross-env NODE_ENV=dev nodemon ./bin/www"
2. интерфейс разработки koa2
- Реализовать сеанс входа в систему на основе koa-generic-session и koa-redis.
- Развитие маршрутизации в основном заключается в повторном использовании экспресс-доставки.
3. Принцип промежуточного программного обеспечения koa2 и его реализация
- app.use используется для регистрации промежуточного программного обеспечения, сначала соберите его
- Реализовать следующий механизм, то есть предыдущий следующий запускает следующий
const http = require('http')
// 组合中间件
function compose(middlewareList) {
return function (ctx) {
// 中间件调用
function dispatch(i) {
const fn = middlewareList[i]
try {
return Promise.resolve(fn(ctx, dispatch.bind(null, i+1)))
} catch(err) {
return Promise.reject(err)
}
}
return dispatch(0)
}
}
class LikeKoa2 {
constructor() {
this.middlewareList = []
}
use(fn) {
this.middlewareList.push(fn)
return this
}
createCtx(req, res) {
const ctx = {
req,
res
}
return ctx
}
handleRequest(ctx, fn) {
return fn(ctx)
}
callback() {
const fn = compose(this.middlewareList)
return (req, res) => {
const ctx = this.createCtx(req, res)
return this.handleRequest(ctx, fn)
}
}
lsiten(...args) {
const server = http.createServer(this.callback())
server.listen(...args)
}
}
module.exports = {
LikeKoa2
}
Восемь, онлайн и конфигурация (pm2)
После разработки кода его необходимо запустить онлайн, при этом будет обеспечена стабильность работы сервиса, а также будет использован инструмент PM2. В этой главе объясняется использование конфигурации и процесс-демон PM2, а также многопроцессорная модель PM2. Наконец, также представлены соответствующие методы эксплуатации и обслуживания сервера.
1. Введение в pm2
- Демон процесса, автоматический перезапуск после сбоя системы В узлах app.js и nodemon app.js происходит сбой процесса, доступ к которому недоступен. Когда pm2 обнаруживает сбой, он автоматически перезапускается.
- Запустите несколько процессов, чтобы полностью использовать процессор и память
- Встроенная функция ведения журнала
2. Конфигурация pm2 и многопроцессорность
преимущество: Память одного процесса ограничена, и операционная система ограничивает максимально доступную память процесса. Невозможно использовать все преимущества многоядерного процессора.
недостаток: Между несколькими процессами память не может быть разделена; несколько процессов обращаются к Redis для совместного использования данных.
{
"apps": {
"name": "pm2-test-server",
"script": "app.js",
"watch": true, // 实时监听
"ignore_watch": [
"node_modules",
"logs"
],
"instances": 4, // 进程数
"error_file": "logs/err.log",
"out_file": "logs/out.log",
"log_date_format": "YYYY-MM-DD HH:mm:ss"
}
}
9. Резюме
Видя, что здесь почти все закончено, я лишь вкратце описал следующие шаги. Узнайте подробности 👉Местоположение источника, Заинтересованные студенты могут пойти учиться на МООК.Это не реклама, и курс все равно очень хороший. На бумаге это немного поверхностно. Я собираюсь рассказать о том, чему я научился, и применить это. Затем я придумаю полную имитацию Nuggets. Если вам интересно, вы можете присоединиться и научиться со мной счастливо. Сейчас проект реализован более чем наполовину.