Показать результаты
github
моментальное управление адресом github
articles
Разговор о серии выпускных проектов --- введение проекта
Разговор о серии выпускных проектов --- внедрение системы
предисловие
В последней статье я в основном представил проект и сделал общее введение в системный анализ и системное проектирование. Затем в этой статье будет представлена реализация системы, в основном для выбора некоторых основных модулей или модулей, которыми можно поделиться с вами. Ладно, давайте к делу~~
MongoDB
На стороне сервера используется инфраструктура Express, база данных использует MongoDB, а для работы с базой данных используется модуль Mongoose. Основная цель здесь — дать представление о MongoDB.Конечно, если чиновник понимает, просто проведите пальцем вниз~~~~~~
Перед запуском проекта убедитесь, что на компьютере установлена mongoDB.загрузи меня,Визуализатор Robo 3T Click me, Пожалуйста, спросите Du Niang или Google о том, как настроить его после загрузки.Эта статья не будет представлять его. Примечание. После установки mongoDB вам необходимо открыть сервер mongod в каталоге lib при запуске проекта~~
MongoDB — это база данных, основанная на распределенном хранилище файлов.Это продукт с открытым исходным кодом между реляционными и нереляционными базами данных.Это самая многофункциональная нереляционная база данных и наиболее похожая на реляционные базы данных. Но в отличие от реляционных баз данных, в MongoDB нет концепции таблиц и строк, а есть集合、文档
база данных. Документ представляет собой пару "ключ-значение" с использованием BSON (формат двоичного сериализованного документа). BSON – это двоичный формат хранения, аналогичный JSON, а BSON имеет расширения для представления типов данных, поэтому поддерживаемые данные очень обширны. MongoDB имеет два очень важных типа данных:内嵌文档和数组
, и другие документы могут быть встроены в массив, так что одна запись может представлять очень сложные отношения.
Mongoose — это инструмент объектной модели для удобной работы MongoDB в асинхронной среде node.js, он может извлекать любую информацию из базы данных, а также может использовать объектно-ориентированный метод для чтения и записи данных, что делает его очень удобным в работе. База данных MongoDB. В Mongoose есть три очень важных понятия, а именно Schema (схема), Model (модель) и Entity (сущность).
- Схема: Скелет модели базы данных, хранящийся в виде файла. Он не имеет возможности оперировать базой данных. Процесс его создания аналогичен процессу построения таблицы в реляционной базе данных, а именно:
//Schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const UserSchema = new Schema({
token: String,
is_banned: {type: Boolean, default: false}, //是否禁言
enable: { type: Boolean, default: true }, //用户是否有效
is_actived: {type: Boolean, default: false}, //邮件激活
username: String,
password: String,
email: String, //email唯一性
code: String,
email_time: {type: Date},
phone: {type: String},
description: { type: String, default: "这个人很懒,什么都没有留下..." },
avatar: { type: String, default: "http://p89inamdb.bkt.clouddn.com/default_avatar.png" },
bg_url: { type: String, default: "http://p89inamdb.bkt.clouddn.com/FkagpurBWZjB98lDrpSrCL8zeaTU"},
ip: String,
ip_location: { type: Object },
agent: { type: String }, // 用户ua
last_login_time: { type: Date },
.....
});
- Модель: модель, созданная публикацией схемы, объект операции с базой данных с абстрактными свойствами и поведением.
//生成一个具体User的model并导出
const User = mongoose.model("User", UserSchema); //第一个参数是集合名,在数据库中会把Model名字字母全部变小写和在后面加复数s
//执行到这个时候你的数据库中就有了 users 这个集合
module.exports = User;
- Сущность: Сущность, созданная Моделью, ее работа также повлияет на базу данных, но ее способность управлять базой данных слабее, чем у Модели.
const newUser = new UserModel({ //UserModel 为导出来的 User
email: req.body.email,
code: getCode(),
email_time: Date.now()
});
В Mongoose есть одна вещь, которую я лично считаю очень важной, а именно:populate
, он может легко установить связь с другой коллекцией с помощью populate. Таким образом, пользовательская коллекция может быть связана с коллекцией статей и самой пользовательской коллекцией.В соответствии с характеристиками встроенных документов она может включать вложенные документы, и есть вложенные документы, которые могут быть встроены в вложенные документы, так что данные, которые он возвращает, будут чрезвычайно богатыми.
const user = await UserModel.findOne({_id: req.query._id, is_actived: true}, {password: 0}).populate({
path: 'image_article',
model: 'ImageArticle',
populate: {
path: 'author',
model: 'User'
}
}).populate({
path: 'collection_film_article',
model: 'FilmArticle',
}).populate({
path: 'following_user',
model: 'User',
}).populate({
path: 'follower_user',
model: 'User',
}).exec();
Сервер в основном управляет базой данных и выполняет такие операции, как добавление, удаление, изменение и запрос (CRUD) к базе данных. Интерфейсы в проекте, различные методы Mongoose здесь подробно описываться не будут, их можно посмотретьДокументация по мангусту.
Реализация аутентификации пользователя
представлять
Механизм аутентификации пользователей этой системы принимаетJSON Web Token(JWT)
, которая представляет собой облегченную спецификацию проверки подлинности, которая также используется для проверки подлинности интерфейса. Мы знаем, что протокол HTTP является протоколом без сохранения состояния, что означает, что каждый запрос независим.Когда пользователь предоставляет имя пользователя и пароль для аутентификации нашего приложения, то в следующем запросе пользователю необходимо выполнить только повторная аутентификация, потому что в соответствии с протоколом HTTP мы не можем знать, какой пользователь отправил запрос.Эта система использует механизм аутентификации токена. Этот токен должен передаваться на сервер в каждом запросе, и он должен храниться в заголовке запроса. Кроме того, сервер должен поддерживать стратегию CORS (Cross-Origin Resource Sharing). Как правило, мы можем сделать это на сервере. Доступ-Контроль-Разрешить-Происхождение: *.
Существует множество методов аутентификации пользователей, наиболее распространенными из которых являются cookie и сеанс. Так в чем разница между тремя из них?Вот две статьи, которые являются довольно всеобъемлющими.
- Правильно понимать Cookie, Session и Token в коротком HTTP-соединении
- Xiaobai должен прочитать: Сессия и токен в коротких HTTP-соединениях сплетен
Разница между токеном и сеансом заключается в том, что он отличается от традиционного механизма аутентификации сеанса, ему не нужно хранить информацию об аутентификации пользователя или информацию о сеансе на стороне сервера. Как только система станет относительно большой, для балансировки нагрузки будет использоваться кластер машин, для чего требуется несколько машин.Поскольку сессия хранится на стороне сервера, необходимо учитывать, на какой сервер входит пользователь.Это отличный груз.
Тогда некоторые люди хотят спросить, ваша система такая маленькая, почему бы не использовать традиционный механизм сеанса? Ха~Поскольку мои проекты раньше обычно использовали сеанс для входа в систему, я никогда не использовал токен, я хочу попытаться попасть в яму~~ха-ха-ха~
Реализовать идеи
Основные идеи реализации JWT следующие:
-
Когда пользователь успешно входит в систему, созданный токен сохраняется в базе данных и возвращается клиенту.
-
Каждый последующий запрос от клиента должен приносить токен, добавлять Authorization в заголовок запроса и добавлять токен.
-
Проверьте действительность токена на сервере, верните код состояния 200 в течение периода действия и верните код состояния 401 по истечении срока действия токена.
Как показано ниже:
График запросов JWTВ основном используется в узлеjsonwebtoken
Этот модуль используется для создания JWT, см. использование jsonwebtoken.документация jsonwebtoken. Промежуточное ПО createToken, которое создает токены в проекте, выглядит следующим образом.
/**
* createToken.js
*/
const jwt = require('jsonwebtoken'); // 引入jsonwebtoken模块
const secret = '我是密钥'
//登录时:核对用户名和密码成功后,应用将用户的id(user_id)作为JWT Payload的一个属性
module.exports = function(user_id){
const token = jwt.sign({
user_id: user_id
}, secret, { //密钥
expiresIn: '24h' //过期时间设置为24h。那么decode这个token的时候得到的过期时间为:创建token的时间+设置的值
});
return token;
};
Возвращенный токен похож наeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYWRtaW4iLCJpYXQiOjE1MzQ2ODQwNzAsImV4cCI6MTUzNDc3MDQ3MH0.Y3kaglqW9Fpe1YxF_uF7zwTV224W4W97MArU0aI0JgM
. Давайте внимательнее посмотрим на эту строку, которая разделена на три сегмента, разделенных знаком «.». Теперь мы base64 декодируем первые два сегмента следующим образом:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ===> {"alg":"HS256","typ":"JWT"} 其中 alg是加密算法名字,typ是类型
eyJ1c2VyX2lkIjoiYWRtaW4iLCJpYXQiOjE1MzQ2ODQwNzAsImV4cCI6MTUzNDc3MDQ3MH0 ===> {"user_id":"admin","iat":1534684070,"exp":1534770470} 其中 name是我们储存的内容,iat创建的时间戳,exp到期时间戳。
Y3kaglqW9Fpe1YxF_uF7zwTV224W4W97MArU0aI0JgM ===> 最后一段是由前面两段字符串,HS256加密后得到。所以前面的任何一个字段修改,都会导致加密后的字符串不匹配。
После того, как мы создадим и получим токен на основе id пользователя, нам нужно вернуть токен клиенту, а клиент сохраняет его локально (localStorage), каждый последующий запрос от клиента должен приносить токен и добавлять его в заголовок запроса .Авторизация и добавление токена, сервер проверяет действительность токена. Итак, как мы можем проверить действительность токена? Поэтому нам нужен checkToken промежуточного слоя, чтобы определить действительность токена.
/**
* checkToken
*/
const jwt = require('jsonwebtoken');
const secret = '我是密钥'
module.exports = async ( req, res, next ) => {
const authorization = req.get('Authorization');
if (!authorization) {
res.status(401).end(); //接口需要认证但是有没带上token,返回401未授权状态码
return
}
const token = authorization.split(' ')[1];
try {
let tokenContent = await jwt.verify(token, secret); //如果token过期或验证失败,将抛出错误
next(); //执行下一个中间件
} catch (err) {
console.log(err)
res.status(401).end(); //token过期或者验证失败返回401状态码
}
}
Итак, теперь нам нужно только добавить промежуточное программное обеспечение checkToken на интерфейс, который требует аутентификации пользователя перед операцией с данными, как показано ниже:
//更新用户信息
router.post('/updateUserInfo', checkToken, User.updateUserInfo)
//如果checkToken检测不成功,它便返回401状态码,不会对User.updateUserInfo做任何操作, 只有检测token成功,才能处理User.updateUserInfo
Как мы можем гарантировать, что каждый запрос может добавить авторизацию в заголовок запроса и добавить токен, что требует перехвата запроса Axios и его перехвата ответа, потому что после того, как сервер возвращает код состояния 401, он должен быть выполнен. хранения локального токена. Конкретный код выглядит следующим образом:
//request拦截器
instance.interceptors.request.use(
config => {
//每次发送请求之前检测本地是否存有token,都要放在请求头发送给服务器
if(localStorage.getItem('token')){
if (config.url.indexOf('upload-z0.qiniup.com/putb64') > -1){
config.headers.Authorization = config.headers['UpToken']; //加上七牛云上传token
}
else {
config.headers.Authorization = `token ${localStorage.getItem('token')}`.replace(/(^\")|(\"$)/g, ''); //加上系统接口token
}
}
console.log('config',config)
return config;
},
err => {
console.log('err',err)
return Promise.reject(err);
}
);
//response拦截器
instance.interceptors.response.use(
response => {
return response;
},
error => { //默认除了2XX之外的都是错误的,就会走这里
if(error.response){
switch(error.response.status){
case 401:
console.log(error.response)
store.dispatch('ADMIN_LOGINOUT'); //可能是token过期,清除它
router.replace({ //跳转到登录页面
path: '/login',
query: { redirect: '/dashboard' } // 将跳转的路由path作为参数,登录成功后跳转到该路由
});
}
}
return Promise.reject(error.response);
}
);
Если еще, потому что изображения, аудио и видео этой системы размещаются в Qiniuyun.При загрузке требуется, чтобы Qiniuyun загружал изображения base64, токен помещается в заголовок запроса, а обычная загрузка изображения не помещается в заголовок запроса, поэтому Это Отличие токенов, как получить доступ к облаку Qiniu, также будет представлено в следующих модулях.
Доступ к облаку Qiniu
Изображения, аудио и видео этой системы размещаются в облаке Qiniu, поэтому необходим доступ к облаку Qiniu. Qiniuyun делит это на две ситуации: загрузка обычных изображений и аудио и видео, а также загрузка изображений base64, потому что Qiniuyun загружает и то, и другое.Content-Type
а такжеdomain(域)
Это другое, Content-Type обычных изображений, аудио и видеоheaders: {'Content-Type':'multipart/form-data'}
доменdomain='https://upload-z0.qiniup.com'
, а загрузка изображения base64headers:{'Content-Type':'application/octet-stream'}
доменdomain='https://upload-z0.qiniup.com/putb64/-1'
, поэтому токен помещается в другое место при запросе, base64 помещается в заголовок запроса, как указано выше.Authorization
, в то время как нормальныйform-data
середина. Сервер получает токен загрузки Qiniuyun через запрос интерфейса, а клиент получает токен Qiniuyun и доставляет токен по различным схемам.
- Загрузка base64:
headers:{'Content-Type':'application/octet-stream'}
а такжеdomain='https://upload-z0.qiniup.com/putb64/-1'
, токен помещается в заголовок запросаAuthorization
середина. - Обычная загрузка изображений, аудио и видео:
headers: {'Content-Type':'multipart/form-data'}
а такжеdomain='https://upload-z0.qiniup.com'
, токен помещается вform-data
середина.
сервер черезqiniu
Этот модуль создает токен, а код сервера выглядит следующим образом:
/**
* 构建一个七牛云上传凭证类
* @class QN
*/
const qiniu = require('qiniu') //导入qiniu模块
const config = require('../config')
class QN {
/**
* Creates an instance of qn.
* @param {string} accessKey -七牛云AK
* @param {string} secretKey -七牛云SK
* @param {string} bucket -七牛云空间名称
* @param {string} origin -七牛云默认外链域名,(可选参数)
*/
constructor (accessKey, secretKey, bucket, origin) {
this.ak = accessKey
this.sk = secretKey
this.bucket = bucket
this.origin = origin
}
/**
* 获取七牛云文件上传凭证
* @param {number} time - 七牛云凭证过期时间,以秒为单位,如果为空,默认为7200,有效时间为2小时
*/
upToken (time) {
const mac = new qiniu.auth.digest.Mac(this.ak, this.sk)
const options = {
scope: this.bucket,
expires: time || 7200
}
const putPolicy = new qiniu.rs.PutPolicy(options)
const uploadToken = putPolicy.uploadToken(mac)
return uploadToken
}
}
exports.QN = QN;
exports.upToken = () => {
return new QN(config.qiniu.accessKey, config.qiniu.secretKey, config.qiniu.bucket, config.qiniu.origin).upToken() //每次调用都创建一个token
}
//获取七牛云token接口
const {upToken} = require('../utils/qiniu')
app.get('/api/uploadToken', (req, res, next) => {
const token = upToken()
res.send({
status: 1,
message: '上传凭证获取成功',
upToken: token,
})
})
Из-за загрузки обычных изображений и аудио и видео, а также загрузки изображений base64, потому что Qiniuyun загружает их обаContent-Type
а такжеdomain(域)
Другой, поэтому место, где хранится запрос токена, отличается, поэтому, чтобы отличить, клиент вызывает код загрузки следующим образом:
//根据获取到的上传凭证uploadToken上传文件到指定域
//正常图片和音视频的上传
uploadFile(formdata, domain='https://upload-z0.qiniup.com',config={headers:{'Content-Type':'multipart/form-data'}}){
console.log(domain)
console.log(formdata)
return instance.post(domain, formdata, config)
},
//base64图片的上传
//根据获取到的上传凭证uploadToken上传base64到指定域
uploadBase64File(base64, token, domain = 'https://upload-z0.qiniup.com/putb64/-1', config = {
headers: {
'Content-Type': 'application/octet-stream',
},
}){
const pic = base64.split(',')[1];
config.headers['UpToken'] = `UpToken ${token}`
return instance.post(domain, pic, config)
},
function upload(Vue, data, callbackSuccess, callbackFail) {
//获取上传token之后处理
Vue.prototype.axios.getUploadToken().then(res => {
if (typeof data === 'string'){ //如果是base64
const token = res.data.upToken
Vue.prototype.axios.uploadBase64File(data, token).then(res => {
if (res.status === 200){
callbackSuccess && callbackSuccess({
data: res.data,
result_url: `http://p89inamdb.bkt.clouddn.com/${res.data.key}`
})
}
}).catch((error) => {
callbackFail && callbackFail({
error
})
})
}
else if (data instanceof FormData){ //如果是FormData
data.append('token', res.data.upToken)
data.append('key', `moment${Date.now()}${Math.floor(Math.random() * 100)}`)
Vue.prototype.axios.uploadFile(data).then(res => {
if (res.status === 200){
callbackSuccess && callbackSuccess({
data: res.data,
result_url: `http://p89inamdb.bkt.clouddn.com/${res.data.key}`
})
}
}).catch((error) => {
callbackFail && callbackFail({
error
})
})
}
else {
const formdata = new FormData() //如果不是formData 就创建formData
formdata.append('token', res.data.upToken)
formdata.append('file', data.file || data)
formdata.append('key', `moment${Date.now()}${Math.floor(Math.random() * 100)}.${data.file.type.split('/')[1]}`)
// 获取到凭证之后再将文件上传到七牛云空间
console.log('formdata',formdata)
Vue.prototype.axios.uploadFile(formdata).then(res => {
console.log('res',res)
if (res.status === 200){
callbackSuccess && callbackSuccess({
data: res.data,
result_url: `http://p89inamdb.bkt.clouddn.com/${res.data.key}` //返回的图片链接
})
}
}).catch((error) => {
console.log(error)
callbackFail && callbackFail({
error
})
})
}
})
}
export default upload
Модуль разрешения маршрутизации
Фоновое управление системой предназначено для соавторов и администраторов с участием двух ролей, поэтому требуется управление разрешениями. Разные разрешения соответствуют разным маршрутам, а меню на боковой панели тоже нужно генерировать асинхронно по разным разрешениям, что отличается от предыдущего сервера, который напрямую возвращает таблицу маршрутизации, которая динамически генерируется фронтендом.Далее мы расскажет о методах проверки входа и разрешений.
-
Логин: после того, как пользователь введет номер учетной записи и пароль, сервер проверит, правильно ли они введены.После прохождения проверки сервер вернет токен.После получения токена внешний интерфейс вытащит интерфейс getAdminInfo в соответствии с токен для получения подробной информации о пользователе (например, разрешения пользователя, имя пользователя и т. д.).
-
Проверка разрешений: получите соответствующую роль пользователя с помощью токена, динамически вычислите соответствующие авторизованные маршруты в соответствии с ролью пользователя, выполните глобальную предварительную защиту с помощью beforeEach vue-router и динамически монтируйте эти маршруты с помощью router.addRoutes.
Кода много, так что просто поместите блок-схему здесь~~
Блок-схема маршрутизации разрешенийВ последнее время мне довелось работать над мидл и бэкенд проектами в компании.В мидл и бэкенд проектах компании таблица маршрутизации генерируется сервером, а фронтенд напрямую рендерится. , весь набор бизнеса компании является относительно зрелым. Однако мы будем думать о том, может ли таблица маршрутизации поддерживаться внешним интерфейсом, чтобы проект не нужно было повторять во время.Каждый раз, когда внешний интерфейс добавляет страницу, брат сервера должен настроить маршрутизацию и разрешения.Конечно, предпосылка может быть, когда проект относительно небольшой.
Модуль учетной записи
Модуль учетной записи является самым основным модулем в бизнесе и отвечает за все связанные с учетной записью функции всей системы. В системе реализованы функции регистрации пользователя, входа пользователя в систему, изменения пароля и восстановления пароля.
Модуль учетной записи системы использует почтовую службу.Для регистрации обычных пользователей почтовая служба используется для отправки кода подтверждения, а для изменения пароля и других операций используется почтовая служба. Nodemailer в основном используется в node.js. Nodemailer — это простой и удобный в использовании компонент отправки почты Node.js.прикоснись ко мне прикоснись ко мне прикоснись ко мне, отправлять электронные письма через этот модуль. Вы спросите, а почему бы не использовать SMS? Ха~ Потому что сервис SMS просит денег, хахаха
/*
* email 邮件模块
*/
const nodemailer = require('nodemailer');
const smtpTransport = require('nodemailer-smtp-transport');
const config = require('../config')
const transporter = nodemailer.createTransport(smtpTransport({
host: 'smtp.qq.com',
secure: true,
port: 465, // SMTP 端口
auth: {
user: config.email.account,
pass: config.email.password //这里密码不是qq密码,是你设置的smtp授权码
}
}));
let clientIsValid = false;
const verifyClient = () => {
transporter.verify((error, success) => {
if (error) {
clientIsValid = false;
console.warn('邮件客户端初始化连接失败,将在一小时后重试');
setTimeout(verifyClient, 1000 * 60 * 60);
} else {
clientIsValid = true;
console.log('邮件客户端初始化连接成功,随时可发送邮件');
}
});
};
verifyClient();
const sendMail = mailOptions => {
if (!clientIsValid) {
console.warn('由于未初始化成功,邮件客户端发送被拒绝');
return false;
}
mailOptions.from = '"ShineTomorrow" <admin@momentin.cn>'
transporter.sendMail(mailOptions, (error, info) => {
if (error) return console.warn('邮件发送失败', error);
console.log('邮件发送成功', info.messageId, info.response);
});
};
exports.sendMail = sendMail;
Для регистрации учетной записи сначала заполните адрес электронной почты. После заполнения электронного письма через Nodemailer будет отправлено электронное письмо с кодом подтверждения с датой истечения срока действия. После этого введите код подтверждения, псевдоним и пароль для завершения регистрации. Из соображений безопасности , для пароля используется безопасный хеш-алгоритм (Secure Hash Algorithm) для шифрования. Вход в учетную запись основан на номере учетной записи или электронной почты плюс пароль, а упомянутый выше механизм аутентификации JSON Web Token (JWT) используется для реализации соответствия между пользователем и данными о статусе входа пользователя.
Моя электронная почта выглядит так 👆 (вы можете написать свой собственный шаблон электронной почты)отправка сообщения в реальном времени
Когда за пользователем следят другие, на комментарии отвечают другие, лайкают и другие социальные операции, после завершения хранения данных сервер должен вовремя отправлять сообщения пользователю, чтобы напомнить ему. Модуль push-сообщений принимаетSocket.io
Для достижения этой цели socket.io инкапсулирует веб-сокет, а также обеспечивает пониженный опрос AJAX, если веб-сокет не поддерживается. Он имеет полные функции и элегантный дизайн. Это единственный способ развивать двустороннюю связь в реальном времени.
пройти черезsocket.io, каждый раз, когда пользователь открывает страницу, страница устанавливает соединение с сервером. На стороне сервера атрибут id подключенного сокета может использоваться для сопоставления со страницей, которая устанавливает соединение. Таким образом, идентификатор пользователя и идентификатор сокета имеют отношение «один ко многим», то есть пользователь может открыть несколько страниц после входа в систему. Однако socket.io не предоставляет функцию отправки сообщений с сервера пользователю индивидуально, а также не предоставляет функцию отправки сообщений на все страницы, открытые пользователем. Но socket.io предоставляет концепцию комнаты, которая представляет собой группу. При установке веб-сокета клиент может присоединиться к комнате. Если комнаты не существует, он автоматически создаст новую. В противном случае он присоединится напрямую. Сервер может рассылать сообщения всем клиентам в комнате.
Согласно этой функции, дизайн использует идентификатор пользователя в качестве имени комнаты.Когда пользователь открывает страницу для установления соединения, он выбирает присоединиться к комнате со своим собственным идентификатором пользователя в качестве имени. Таким образом, в комнате с идентификатором пользователя в качестве имени добавляются все соединения, созданные страницами, открытыми пользователем. Чтобы отправить сообщение определенному пользователю, вы можете напрямую отправить сообщение в комнату, имя которой является идентификатором пользователя, чтобы оно было отправлено на все страницы, открытые пользователем.
После того, как у нас есть идея, давайте начнем~, на сервереsocket.io
использовать в клиентеvue-socket.io
, код сервера выглядит следующим образом:
/*
* app.js中
*/
const server = require('http').createServer(app);
const io = require('socket.io')(server);
global.io = io; //全局设上io值, 因为在其他模块要用到
io.on('connection', function (socket) {
// setTimeout(()=>{
// socket.emit('nodeEvent', { hello: 'world' });
// }, 5000)
socket.on('login_success', (data) => { //接受客户端触发的login_success事件
//使用user_id作为房间号
socket.join(data.user_id);
console.log('login_success',data);
});
});
io.on('disconnect', function (socket) {
socket.emit('user disconnected');
});
server.listen(config.port, () => {
console.log(`The server is running at http://localhost:${config.port}`);
});
/*
* 某业务模块
*/
//例如某文章增加评论
io.in(newMusicArticle.author.user_id._id).emit('receive_message', newMessage); //实时通知客户端receive_message事件
sendMail({ //发送邮件
to: newMusicArticle.author.user_id.email,
subject: `Moment | 你有未读消息哦~`,
text: `啦啦啦,我是卖报的小行家~~ 🤔`,
html: emailTemplate.comment(sender, newMusicArticle, content, !!req.body.reply_to_id)
})
Код обслуживания клиентов:
<script>
export default {
name: 'App',
data () {
return {
}
},
sockets:{
connect(){
},
receive_message(val){ //接受服务端触发的事件,进行客户端实时更新数据
if (val){
console.log('服务端实时通信', val)
this.$notify(val.content)
console.log('this method was fired by the socket server. eg: io.emit("customEmit", data)')
}
}
},
mixins: [mixin],
mounted(){
if (!!JSON.parse(window.localStorage.getItem('user_info'))){
this.$socket.emit('login_success', { //通知服务端login_success 事件, 传入id
user_id: JSON.parse(window.localStorage.getItem('user_info'))._id
})
}
},
}
</script>
Модуль комментариев
Модуль комментариев предназначен для предоставления пользователям некоторых операций над комментариями под статьей в мобильном веб-приложении. В системе реализована функция комментирования статей, лайки за комментарии, попадание в топ популярных комментариев, ответы на комментарии. В комментариях присутствуют различные проблемы с безопасностью, такие как XSS-атаки (межсайтовый скриптинг, межсайтовые скриптовые атаки) и конфиденциальные слова. Предотвращайте XSS-атаки с помощьюxss
модуль, использование фильтрации чувствительных словtext-censor
модуль.
некоторые размышления
- проблема с данными интерфейса
Часто возникает эта проблема, проблемы с интерфейсными данными во время разработки. Иногда данные, возвращаемые с сервера, не то, что мы хотим, чтобы данные, интерфейс к повторному процессу данных.
Например, поле, возвращаемое сервером, имеет значение null или структура данных, возвращаемая сервером, слишком глубокая. Интерфейс должен постоянно оценивать, действительно ли структура данных возвращает правильную вещь, а не null или undefined~
Мы должны иметь дело с фильтрацией во внешнем интерфейсе следующим образом:
<div class="author">
文 / {{(musicArticleInfo.author && musicArticleInfo.author.user_id) ? musicArticleInfo.author.user_id.username : '我叫这个名字'}}
</div>
Это наводит на мысль:
Дальнейшая инкапсуляция и обработка данных неизбежно вызовет проблемы с производительностью рендеринга, и мы всегда должны беспокоиться о проблеме возврата данных. Применительно к бизнесу компании, как мы должны справиться с этим?
- Оптимизация производительности на странице и вопросы SEO
Первая проблема рендеринга экрана всегда была проблемой для одностраничных приложений, поэтому в дополнение к обычно используемой оптимизации производительности, есть ли способ оптимизировать ее? Хотя этот проект нацелен на мобильных пользователей, проблем с SEO может и не быть.Если он сделан на ПК, SEO является обязательным для таких приложений, как статьи.
Для проблем, поднятых выше, появление узла позволяет нам увидеть решение, о котором часто говорятСредний слой узла, конечно, в этом проекте нет среднего уровня Node, но база данных обрабатывается непосредственно как внутренний язык.
Поскольку большая часть серверной части компании написана либо на php, либо на java, узел, как правило, не используется напрямую в качестве внутреннего языка. Если узел используется, он обычно существует как промежуточный уровень.
Для решения первой проблемы: мы можем делать проброс интерфейса в среднем слое, а обработку данных делать в процессе проброса. И не беспокойтесь о проблеме возврата данных.
Для решения второй проблемы: со средним уровнем Node мы можем передать задачу рендеринга первого экрана nodejs, а рендеринг второго экрана по-прежнему следует за предыдущим рендерингом браузера.
Если есть средний уровень Node, новая архитектура выглядит следующим образом:
Фронтенд и бэкэнд функции:
Суммировать
Прошло некоторое время с тех пор, как я закончил, и статья написана для обзора. Мой уровень средний, извините за это. Реализация этого продукта осуществляется одним человеком и играет в нем разные роли, требует немного продуктового мышления, небольшой дизайнерской идеи, дизайна базы данных и back-end разработки, что довольно утомительно. Самым сложным моментом, на мой взгляд, является дизайн базы данных.База данных должна быть спроектирована полностью с самого начала, иначе при последующем добавлении она будет очень грязной и хаотичной.Конечно, основой для этого является то, что продукт должен быть Очень ясно Продукт может быть расплывчатым определением Думая об этом, это почти так, поэтому я начал делать это ~~ в результате чего позже не очень доволен дизайном базы данных. Из-за нехватки времени некоторые небольшие модули в текущем продукте не были завершены, но большая часть функциональной структуры была завершена.Это сформированный продукт.Конечно, это продукт, который не был протестирован.Хахахаха, если есть это тест, тогда хахаха Вы знаете~~~.
Дорога впереди длинная, я буду искать вдоль и поперёк~
над
Спасибо ~~.