egg 创建 webSocket 服务
安装依赖项
egg-cors解决跨域问题。egg-jwt来创建token,在客户端创建连接的时候需要携带token。egg-socket.io用来创建websocket服务器。
yarn add egg-cors egg-jwt egg-socket.io
复制代码
配置
-
在
/config/plugin.js中开启对应的插件module.exports = { io: { enable: true, package: 'egg-socket.io' }, jwt: { enable: true, package: 'egg-jwt' }, cors: { enable: true, package: 'egg-cors' } } 复制代码 -
在
/config/config.default.js中配置对应的参数module.exports = appInfo => { // 1. egg-jwt 配置项 config.jwt = { secret: '^_^', // 用于解密和加密密钥 sign: { // jwt.sign(***,***,[options,***])方法中,options的默认设置可以在这里配置; // 多少s后过期,jwt.sing(plyload,secret,{expiresIn:number})会被合并,调用时设置优先级更高; expiresIn: 60 * 60 * 24 } } // 2. egg-cors跨域配置 config.cors = { // origin: () => 'http://localhost:8080', // 此处只能设置一个域,函数的返回值为一个字符串,或者直接设置未字符串 allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH' } // 3. egg-socket.io配置项 exports.io = { namespace: { '/echart': { connectionMiddleware: ['auth', 'connection'], packetMiddleware: [] } } } // 安全配置 (https://eggjs.org/zh-cn/core/security.html) config.security = { csrf: { enable: false, ignoreJSON: false }, // 允许访问接口的白名单,此处可以设置多个域, domainWhiteList: ['http://localhost:8080', 'http://127.0.0.1:8081'] } // 处理http请求的中间件 config.middleware = ['auth', 'notfoundHandler'] const userConfig = { // 用来生成token的数据,此项目并没有使用数据库,不使用用户信息来生成token websocketToken: '小梁子吖' } } 复制代码创建处理
http请求的中间件// /app/middleware/auth.js token权限验证 'use strict' module.exports = () => { return async function (ctx, next) { const url = ctx.url const token = ctx.headers.authorization // url 白名单,直接next() const urlWhite = ['/', '/api/login'] if (urlWhite.includes(url)) { await next() return } // token 拦截其余url if (!token) { ctx.status = 401 return false } try { ctx.app.verifyToekn(token) await next() } catch (error) { ctx.status = 403 ctx.body = ctx.failed(error.message) } } } 复制代码// /app/middleware/notfound_handler.js 自定义404处理 'use strict' module.exports = () => { return async function notFoundHandler(ctx, next) { await next() if (ctx.status === 404 && !ctx.body) { if (ctx.acceptJSON) { ctx.body = { error: 'Not Found' } } else { ctx.status = 404 // ctx.failed() 是在 /app/extend/context.js中自定义方法 ctx.body = ctx.failed('not found') } } } } 复制代码
使用
-
首先应该有生成
token的接口。// /app/controller/user.js 'use strict' const { Controller } = require('egg') class UserController extends Controller { async login() { const { ctx, app } = this const token = app.createToken({ name: app.config.websocketToken }) // 创建token ctx.body = ctx.success({ token }) } } module.exports = UserController 复制代码 -
使用
egg-socket.io来创建websocket/* * /app/io/controller/netWorkUser.js */ 'use strict' const { Controller } = require('egg') class NspController extends Controller { async exchange() { const { app } = this const users = app.io.of('/echart') users.emit('user_get', { message: 'user_get', data: [ { type: '5G', number: 10000 }, { type: '4G', number: 5000 }, { type: '3G', number: 3000 } ] }) } } module.exports = NspController 复制代码egg-socket.io中间件, [/app/io/middleware/*.js]/* * /app/io/middleware/auth.js 鉴权 */ 'use strict' // 当有客户端连接的时候来验证token module.exports = () => { return async (ctx, next) => { const requestQuery = ctx.query const { token } = requestQuery // 如果没有携带token,则关闭连接 if (!token) return ctx.socket.disconnect() try { const { name } = ctx.app.verifyToekn(token) // 解析成功,但并不是我们的token数据,关闭连接 if (name !== ctx.app.config.websocketToken) return ctx.socket.disconnect() } catch (error) { // 解析token失败 return ctx.socket.disconnect() } await next() } } 复制代码/* * /app/io/controller/middleware/connection.js */ 'use strict' // 在每一个客户端连接或者退出的时候发生作用 module.exports = () => { return async (ctx, next) => { ctx.socket.emit('client_success', { message: 'client_success', data: ctx.success({ message: `${ctx.headers.origin}客户端连接成功` }) }) await next() // 当有客户端退出的时候,执行以下操作 const origin = ctx.request.headers.origin console.log(`${origin} 用户退出!`) } } 复制代码
Vue 中创建连接
在
使用egg-socket.io创建的websokcet服务必须使用socket.io-client来进行连接,不能直接通过new WebSocket(url)来建立连接。
egg-socket.io是使用socket.io来创建websocket服务。详见socket.io/docs/v4/
-
安装
socket.io-slice@2.4yarn add socket.io-slice@2.4 复制代码 -
/src/network/websocket.jsimport io from 'socket.io-client' const socket = io('ws://127.0.0.1:7001/echart', { reconnection: true, // 是否重连 reconnectionAttempts: 30, // 重新连接的次数 reconnectionDelay: 1000, // 每过多长时间重连一次 timeout: 5000, // 超时时间 query: { token: window.sessionStorage.getItem('token') } }) socket.on('connect', () => { console.log('连接成功') // x8WIv7-mJelg7on_ALbx }) socket.on('disconnect', res => { console.log('连接关闭') // undefined }) socket.on('client_success', response => { console.log(response) }) export default socket 复制代码 -
/src/app.vue<template> <router-link style='margin-right: 20px' to='/'> Home </router-link> <router-link to='/about'>About</router-link> <router-view /> </template> 复制代码 -
/src/views/Home.vue<template> <div class="home"> <button @click="getToekn">获取token</button> </div> </template> <script> import axios from 'axios' export default { setup () { const getToekn = async () => { const { data } = await axios.post('http://127.0.0.1:7001/api/login') console.log(data) window.sessionStorage.setItem('token', `Bearer ${data.data.token}`) } return { getToekn } } } </script> 复制代码 -
/src/views/About.vue<template> <div class="home"> <button @click="getToekn">获取token</button> </div> </template> <script> import axios from 'axios' export default { setup () { const getToekn = async () => { const { data } = await axios.post('http://127.0.0.1:7001/api/login') console.log(data) window.sessionStorage.setItem('token', `Bearer ${data.data.token}`) } return { getToekn } } } </script> 复制代码




近期评论