微信小程序对接EdgeX Foundry详细指南
微信小程序对接EdgeX Foundry详细指南
系统架构概述
微信小程序 → 服务器API网关/反向代理 → EdgeX Foundry (部署在服务器)
由于微信小程序要求所有请求必须使用HTTPS且域名需要备案,小程序无法直接访问EdgeX的API,需要通过服务器端做中转或反向代理。
第一部分:服务器端配置
方法一:使用Nginx反向代理(推荐)
- 
安装Nginx
# Ubuntu/Debiansudo apt update && sudo apt install nginx# CentOS/RHELsudo yum install epel-release && sudo yum install nginx - 
配置Nginx反向代理
创建配置文件/etc/nginx/conf.d/edgex.conf:server { listen 443 ssl; server_name your-domain.com; # 替换为已备案的域名 # SSL证书配置 ssl_certificate /path/to/your/certificate.crt; ssl_certificate_key /path/to/your/private.key; # 核心数据API代理 location /edgex/core-data/ { proxy_pass http://localhost:59880/api/v2/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 添加CORS头部 add_header \'Access-Control-Allow-Origin\' \'*\' always; add_header \'Access-Control-Allow-Methods\' \'GET, POST, OPTIONS\' always; add_header \'Access-Control-Allow-Headers\' \'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization\' always; # 处理预检请求 if ($request_method = \'OPTIONS\') { add_header \'Access-Control-Allow-Origin\' \'*\'; add_header \'Access-Control-Allow-Methods\' \'GET, POST, OPTIONS\'; add_header \'Access-Control-Allow-Headers\' \'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization\'; add_header \'Access-Control-Max-Age\' 1728000; add_header \'Content-Type\' \'text/plain; charset=utf-8\'; add_header \'Content-Length\' 0; return 204; } } # 设备服务API代理 location /edgex/device-service/ { proxy_pass http://localhost:59881/api/v2/; # ... 类似上面的配置 }} - 
重启Nginx
sudo nginx -t && sudo systemctl restart nginx 
方法二:使用Node.js编写API网关(更灵活)
- 
创建项目目录
mkdir edgex-gateway && cd edgex-gatewaynpm init -ynpm install express cors axios - 
创建网关服务器文件
gateway.jsconst express = require(\'express\');const cors = require(\'cors\');const axios = require(\'axios\');const app = express();const port = 3000;// 中间件app.use(cors());app.use(express.json());// EdgeX服务地址配置const EDGEX_CONFIG = { coreData: \'http://localhost:59880\', deviceService: \'http://localhost:59881\', command: \'http://localhost:59882\'};// 身份验证中间件(可选)const authenticate = (req, res, next) => { // 这里可以添加JWT验证逻辑 const token = req.header(\'Authorization\'); if (!token) { return res.status(401).json({ error: \'访问被拒绝,缺少令牌\' }); } // 验证token逻辑... next();};// 获取设备数据app.get(\'/api/devices/:deviceName/events\', authenticate, async (req, res) => { try { const { deviceName } = req.params; const { limit = 10 } = req.query; const response = await axios.get( `${EDGEX_CONFIG.coreData}/api/v2/event/device/name/${deviceName}?limit=${limit}` ); res.json({ success: true, data: response.data }); } catch (error) { res.status(500).json({ success: false, message: \'获取设备数据失败\', error: error.message }); }});// 发送命令到设备app.post(\'/api/devices/:deviceName/command\', authenticate, async (req, res) => { try { const { deviceName } = req.params; const { command, params } = req.body; const response = await axios.post( `${EDGEX_CONFIG.command}/api/v2/device/name/${deviceName}/${command}`, params ); res.json({ success: true, data: response.data }); } catch (error) { res.status(500).json({ success: false, message: \'发送命令失败\', error: error.message }); }});// 获取设备列表app.get(\'/api/devices\', authenticate, async (req, res) => { try { const response = await axios.get( `${EDGEX_CONFIG.coreData}/api/v2/device/all?limit=100` ); res.json({ success: true, data: response.data }); } catch (error) { res.status(500).json({ success: false, message: \'获取设备列表失败\', error: error.message }); }});app.listen(port, () => { console.log(`EdgeX网关服务器运行在端口 ${port}`);}); - 
使用PM2管理进程
npm install -g pm2pm2 start gateway.js --name edgex-gatewaypm2 savepm2 startup 
第二部分:微信小程序开发
1. 小程序网络请求封装
创建 utils/api.js 文件:
const BASE_URL = \'https://your-domain.com\'; // 替换为您的域名class ApiClient { constructor() { this.token = wx.getStorageSync(\'token\'); } // 统一请求方法 request(url, method = \'GET\', data = {}) { return new Promise((resolve, reject) => { const header = { \'Content-Type\': \'application/json\' }; if (this.token) { header[\'Authorization\'] = `Bearer ${this.token}`; } wx.request({ url: BASE_URL + url, method: method, data: data, header: header, success: (res) => { if (res.statusCode === 200) { resolve(res.data); } else { reject(res.data); } }, fail: (error) => { reject(error); } }); }); } // 获取设备事件数据 getDeviceEvents(deviceName, limit = 10) { return this.request(`/api/devices/${deviceName}/events?limit=${limit}`); } // 发送设备命令 sendDeviceCommand(deviceName, command, params) { return this.request(`/api/devices/${deviceName}/command`, \'POST\', { command, params }); } // 获取设备列表 getDeviceList() { return this.request(\'/api/devices\'); }}export default new ApiClient();
2. 小程序页面示例
创建 pages/device/device.js:
const api = require(\'../../utils/api.js\');Page({ data: { deviceList: [], currentDevice: null, events: [], loading: false }, onLoad() { this.loadDevices(); }, // 加载设备列表 async loadDevices() { this.setData({ loading: true }); try { const result = await api.getDeviceList(); this.setData({ deviceList: result.data.devices, loading: false }); } catch (error) { wx.showToast({ title: \'加载设备失败\', icon: \'none\' }); this.setData({ loading: false }); } }, // 选择设备 onSelectDevice(e) { const device = e.currentTarget.dataset.device; this.setData({ currentDevice: device }); this.loadDeviceEvents(device.name); }, // 加载设备事件 async loadDeviceEvents(deviceName) { this.setData({ loading: true }); try { const result = await api.getDeviceEvents(deviceName, 20); this.setData({ events: result.data.events, loading: false }); } catch (error) { wx.showToast({ title: \'加载数据失败\', icon: \'none\' }); this.setData({ loading: false }); } }, // 发送命令 async sendCommand() { if (!this.data.currentDevice) { wx.showToast({ title: \'请先选择设备\', icon: \'none\' }); return; } try { const result = await api.sendDeviceCommand( this.data.currentDevice.name, \'switch\', { value: \'on\' } ); wx.showToast({ title: \'命令发送成功\', icon: \'success\' }); } catch (error) { wx.showToast({ title: \'命令发送失败\', icon: \'none\' }); } }});
创建 pages/device/device.wxml:
<view class=\"container\">  <view class=\"section\"> <text class=\"section-title\">选择设备</text> <scroll-view scroll-x class=\"device-scroll\"> <view wx:for=\"{{deviceList}}\" wx:key=\"id\" class=\"device-item {{currentDevice && currentDevice.id === item.id ? \'active\' : \'\'}}\" bindtap=\"onSelectDevice\" data-device=\"{{item}}\" > <text>{{item.name}}</text> </view> </scroll-view> </view>  <view class=\"section\" wx:if=\"{{currentDevice}}\"> <text class=\"section-title\">设备数据: {{currentDevice.name}}</text> <view class=\"data-card\"> <view class=\"data-item\" wx:for=\"{{events}}\" wx:key=\"id\"> <text class=\"data-label\">{{item.readings[0].resourceName}}:</text> <text class=\"data-value\">{{item.readings[0].value}}</text> <text class=\"data-time\">{{item.origin}}</text> </view> </view> <button type=\"primary\" bindtap=\"sendCommand\" loading=\"{{loading}}\"> 发送开启命令 </button> </view>  <view wx:if=\"{{loading}}\" class=\"loading\"> <text>加载中...</text> </view></view>
创建 pages/device/device.wxss:
.container { padding: 20rpx;}.section { margin-bottom: 40rpx;}.section-title { font-size: 32rpx; font-weight: bold; display: block; margin-bottom: 20rpx;}.device-scroll { white-space: nowrap; width: 100%;}.device-item { display: inline-block; padding: 20rpx 40rpx; margin-right: 20rpx; background-color: #f5f5f5; border-radius: 10rpx;}.device-item.active { background-color: #007aff; color: white;}.data-card { background-color: #fff; border-radius: 10rpx; padding: 20rpx; margin-bottom: 20rpx; box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);}.data-item { padding: 20rpx 0; border-bottom: 1rpx solid #eee;}.data-item:last-child { border-bottom: none;}.data-label { font-weight: bold; margin-right: 20rpx;}.data-value { color: #007aff;}.data-time { display: block; font-size: 24rpx; color: #999; margin-top: 10rpx;}.loading { text-align: center; padding: 40rpx; color: #999;}
3. 小程序配置文件
在 app.json 中添加页面配置:
{ \"pages\": [ \"pages/device/device\" ], \"window\": { \"navigationBarTitleText\": \"EdgeX设备监控\", \"navigationBarBackgroundColor\": \"#007aff\", \"navigationBarTextStyle\": \"white\" }, \"networkTimeout\": { \"request\": 10000 }}
第三部分:安全增强建议
- 
API访问控制
// 在网关服务器中添加身份验证const jwt = require(\'jsonwebtoken\');// 登录接口app.post(\'/api/login\', async (req, res) => { const { username, password } = req.body; // 验证用户 credentials (简化示例) if (username === \'admin\' && password === \'password\') { const token = jwt.sign({ userId: 1 }, \'your-secret-key\', { expiresIn: \'24h\' }); res.json({ success: true, token }); } else { res.status(401).json({ success: false, message: \'认证失败\' }); }}); - 
请求频率限制
const rateLimit = require(\'express-rate-limit\');const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15分钟 max: 100 // 限制每个IP每15分钟最多100次请求});app.use(\'/api/\', limiter); 
部署和测试步骤
- 部署EdgeX Foundry到您的服务器
 - 配置Nginx反向代理或部署Node.js网关
 - 申请HTTPS证书并配置到Nginx
 - 微信小程序后台配置服务器域名
 - 开发并上传小程序
 - 测试设备数据读取和命令发送
 
常见问题解决
- 跨域问题:确保Nginx配置了正确的CORS头
 - HTTPS问题:小程序要求所有请求使用HTTPS
 - 域名备案:小程序要求的域名必须已完成ICP备案
 - API限制:微信小程序有网络请求API的调用频率限制
 
这个方案提供了从服务器配置到小程序开发的完整实现,可以根据实际需求进行调整和扩展。


