You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:ReactJS中使用Stanza.io发送带自定义属性消息的示例

在React中使用Stanza.io发送带自定义属性的消息

我之前也碰到过在Stanza.io里发送带自定义属性消息的需求,折腾了一阵才搞清楚正确的实现方式——核心是利用Stanza的消息扩展机制,要么给<message>元素本身加自定义属性,要么添加符合XMPP规范的自定义扩展节点。下面是一个完整的React组件示例,涵盖了从连接XMPP服务器到发送、解析自定义消息的全部流程:

import React, { useState, useEffect } from 'react';
import { Client, xml } from 'stanza.io';

const XMPPChat = () => {
  const [client, setClient] = useState(null);
  const [isConnected, setIsConnected] = useState(false);
  const [messageText, setMessageText] = useState('');
  const [receivedMessages, setReceivedMessages] = useState([]);

  useEffect(() => {
    // 初始化Stanza客户端
    const xmppClient = new Client({
      jid: 'your-username@example.com', // 替换为你的XMPP账号JID
      password: 'your-password', // 替换为你的账号密码
      transport: 'websocket',
      wsURL: 'wss://example.com:5281/xmpp-websocket' // 替换为你的XMPP服务器WebSocket地址
    });

    // 连接成功回调
    xmppClient.on('session:started', () => {
      setIsConnected(true);
      console.log('XMPP连接成功');
      // 发送在线状态
      xmppClient.sendPresence();
    });

    // 接收消息回调
    xmppClient.on('message', (msg) => {
      if (msg.type !== 'error') {
        // 1. 解析<message>元素本身的自定义属性
        const customUserId = msg.attrs['custom-user-id'];
        const customType = msg.attrs['custom-message-type'];
        if (customUserId && customType) {
          console.log('消息自带的自定义属性:', { customUserId, customType });
        }

        // 2. 解析自定义扩展节点
        const customNode = msg.getChild('custom', 'http://your-custom-namespace.com');
        if (customNode) {
          const userId = customNode.attrs.userId;
          const messageType = customNode.attrs.messageType;
          const metadataPriority = customNode.getChild('metadata')?.attrs.priority;
          console.log('自定义扩展内容:', { userId, messageType, metadataPriority });
        }

        // 将普通消息内容加入列表
        setReceivedMessages(prev => [...prev, msg.body]);
      }
    });

    // 连接错误处理
    xmppClient.on('error', (err) => {
      console.error('XMPP连接出错:', err);
      setIsConnected(false);
    });

    // 启动连接
    xmppClient.connect();

    setClient(xmppClient);

    // 组件卸载时断开连接
    return () => {
      if (xmppClient) {
        xmppClient.disconnect();
      }
    };
  }, []);

  // 发送带自定义属性的消息(直接给<message>元素加属性)
  const sendCustomMessageWithAttrs = () => {
    if (!client || !messageText.trim() || !isConnected) return;

    client.sendMessage({
      to: 'recipient@example.com', // 替换为接收者的JID
      body: messageText,
      attrs: {
        'custom-user-id': 'user_123',
        'custom-message-type': 'system_notification'
      }
    });

    setMessageText('');
  };

  // 发送带自定义扩展节点的消息(更符合XMPP规范,适合复杂数据)
  const sendCustomMessageWithExtension = () => {
    if (!client || !messageText.trim() || !isConnected) return;

    // 创建自定义扩展节点,必须指定唯一命名空间
    const customExtension = xml(
      'custom',
      'http://your-custom-namespace.com',
      {
        userId: 'user_123',
        messageType: 'system_notification'
      },
      // 可以嵌套子节点存储更多元数据
      xml('metadata', null, { priority: 'high', timestamp: Date.now().toString() })
    );

    client.sendMessage({
      to: 'recipient@example.com', // 替换为接收者的JID
      body: messageText,
      extensions: [customExtension]
    });

    setMessageText('');
  };

  return (
    <div style={{ maxWidth: 600, margin: '20px auto', padding: '0 20px' }}>
      <h2>Stanza.io 自定义消息测试</h2>
      <div style={{ marginBottom: 20 }}>
        <p>连接状态: {isConnected ? '✅ 已连接' : '❌ 未连接'}</p>
      </div>

      <div style={{ marginBottom: 20 }}>
        <h3>接收的消息:</h3>
        <div style={{ border: '1px solid #eee', borderRadius: 4, padding: 12, height: 220, overflowY: 'auto' }}>
          {receivedMessages.length === 0 ? (
            <p style={{ color: '#999' }}>暂无消息</p>
          ) : (
            receivedMessages.map((msg, idx) => (
              <p key={idx} style={{ margin: '8px 0' }}>{msg}</p>
            ))
          )}
        </div>
      </div>

      <div>
        <textarea
          value={messageText}
          onChange={(e) => setMessageText(e.target.value)}
          placeholder="输入消息内容..."
          style={{ width: '100%', height: 90, marginBottom: 12, padding: 8, borderRadius: 4, border: '1px solid #ddd' }}
        />
        <div style={{ display: 'flex', gap: 10 }}>
          <button
            onClick={sendCustomMessageWithAttrs}
            disabled={!isConnected || !messageText.trim()}
            style={{ padding: '8px 18px', cursor: 'pointer', borderRadius: 4, border: 'none', backgroundColor: '#2563eb', color: 'white', opacity: !isConnected || !messageText.trim() ? 0.6 : 1 }}
          >
            发送带自定义属性的消息
          </button>
          <button
            onClick={sendCustomMessageWithExtension}
            disabled={!isConnected || !messageText.trim()}
            style={{ padding: '8px 18px', cursor: 'pointer', borderRadius: 4, border: 'none', backgroundColor: '#16a34a', color: 'white', opacity: !isConnected || !messageText.trim() ? 0.6 : 1 }}
          >
            发送带自定义扩展的消息
          </button>
        </div>
      </div>
    </div>
  );
};

export default XMPPChat;

关键说明:

  • 两种自定义方式:
    1. 直接给<message>加属性:适合简单的键值对数据,通过attrs参数传递,接收时直接从msg.attrs读取。
    2. 自定义扩展节点:适合复杂的嵌套数据,必须指定唯一的命名空间(避免和其他XMPP扩展冲突),用stanza.io的xml方法构造节点,通过extensions参数传入,接收时用msg.getChild方法查找对应的节点。
  • 注意事项:
    • 一定要替换代码中的JID、密码、服务器地址和接收者JID为你的实际信息。
    • 命名空间建议使用你自己的域名(比如http://your-domain.com/xmpp/custom),确保唯一性。
    • 发送前要检查连接状态和消息内容是否为空,避免无效调用。

内容的提问来源于stack exchange,提问作者For Guru

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:25:34