微信小程序对接EdgeX Foundry详细指南
微信小程序对接EdgeX Foundry详细指南
系统架构概述
微信小程序 → 服务器API网关/反向代理 → EdgeX Foundry (部署在服务器)
由于微信小程序要求所有请求必须使用HTTPS且域名需要备案,小程序无法直接访问EdgeX的API,需要通过服务器端做中转或反向代理。
第一部分:服务器端配置
方法一:使用Nginx反向代理(推荐)
-
安装Nginx
# Ubuntu/Debian sudo apt update && sudo apt install nginx# CentOS/RHEL sudo 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-gateway npm init -y npm install express cors axios
-
创建网关服务器文件
gateway.js
const 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 pm2 pm2 start gateway.js --name edgex-gateway pm2 save pm2 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的调用频率限制
这个方案提供了从服务器配置到小程序开发的完整实现,可以根据实际需求进行调整和扩展。