基于Azure的Bot Framework聊天机器人:Express后端对接Directline API v3.0的AJAX交互问题
Hey there! Let's break down how to get AJAX-based Activity send/receive working for your setup—you're already on the right track by handling session generation backend-side, which is the secure way to do it. Here's a step-by-step guide with code examples tailored to your stack:
First, let's confirm your Express endpoint for generating Direct Line conversations is set up correctly. This endpoint will return a conversationId and token that your frontend uses to communicate with the bot:
const express = require('express'); const axios = require('axios'); const cors = require('cors'); // Optional, but useful if frontend is on a different port const app = express(); const port = process.env.PORT || 3000; // Store these in environment variables (never hard-code secrets!) const DIRECT_LINE_SECRET = process.env.DIRECT_LINE_SECRET; const DIRECT_LINE_BASE_URL = 'https://directline.botframework.com/v3/directline'; app.use(cors()); app.use(express.static('public')); // Serve your frontend files app.use(express.json()); // Endpoint to create a Direct Line conversation app.get('/api/directline/start-conversation', async (req, res) => { try { const response = await axios.post( `${DIRECT_LINE_BASE_URL}/conversations`, {}, { headers: { Authorization: `Bearer ${DIRECT_LINE_SECRET}` } } ); const { conversationId, token, expires_in } = response.data; res.json({ conversationId, token, expires_in }); } catch (err) { console.error('Failed to create conversation:', err); res.status(500).json({ error: 'Could not initialize bot session' }); } }); app.listen(port, () => console.log(`Express server running on port ${port}`));
Next, build your frontend to fetch the session details on load, then use AJAX to send user messages. Here's a sample HTML/JS setup using jQuery (adjust to your preferred AJAX library if needed):
<!DOCTYPE html> <html> <head> <title>Bot Chat</title> <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <style> #chat-messages { height: 300px; overflow-y: auto; border: 1px solid #ccc; padding: 10px; margin-bottom: 10px; } .user-msg { text-align: right; margin: 5px 0; color: #2c3e50; } .bot-msg { text-align: left; margin: 5px 0; color: #3498db; } </style> </head> <body> <div id="chat-messages"></div> <input type="text" id="user-input" placeholder="Type your message..."> <button id="send-btn">Send</button> <script> let conversationId; let directLineToken; // Initialize session on page load $(document).ready(async () => { const sessionResponse = await $.get('/api/directline/start-conversation'); conversationId = sessionResponse.conversationId; directLineToken = sessionResponse.token; console.log('Bot session initialized:', conversationId); // Start listening for bot messages startMessagePolling(); }); // Send user message via AJAX $('#send-btn').click(async () => { const messageText = $('#user-input').val().trim(); if (!messageText) return; // Add user message to UI immediately $('#chat-messages').append(`<div class="user-msg">You: ${messageText}</div>`); $('#user-input').val(''); scrollToBottom(); try { await $.ajax({ url: `https://directline.botframework.com/v3/directline/conversations/${conversationId}/activities`, method: 'POST', headers: { 'Authorization': `Bearer ${directLineToken}`, 'Content-Type': 'application/json' }, data: JSON.stringify({ type: 'message', from: { id: 'unique-user-id' }, // Use a unique ID for each user text: messageText }) }); } catch (err) { console.error('Failed to send message:', err); $('#chat-messages').append('<div class="bot-msg">Error: Could not send message</div>'); scrollToBottom(); } }); function scrollToBottom() { $('#chat-messages').scrollTop($('#chat-messages')[0].scrollHeight); } </script> </body> </html>
Direct Line v3 supports two ways to get bot responses: long polling (AJAX-friendly) or WebSockets (more real-time).
Option 1: Long Polling (AJAX-Based)
Add this function to your frontend JS to poll for new messages at intervals:
let watermark = 0; // Tracks the last message we've received to avoid duplicates function startMessagePolling() { setInterval(async () => { try { const response = await $.ajax({ url: `https://directline.botframework.com/v3/directline/conversations/${conversationId}/activities?watermark=${watermark}`, method: 'GET', headers: { 'Authorization': `Bearer ${directLineToken}` } }); if (response.activities.length > 0) { // Update watermark to fetch only new messages next time watermark = response.watermark; // Process each activity response.activities.forEach(activity => { // Only display messages from the bot (ignore system messages or user's own messages) if (activity.type === 'message' && activity.from.id !== 'unique-user-id') { $('#chat-messages').append(`<div class="bot-msg">Bot: ${activity.text}</div>`); scrollToBottom(); } }); } } catch (err) { console.error('Failed to fetch messages:', err); } }, 1000); // Poll every 1 second (adjust based on your real-time needs) }
Option 2: WebSocket (More Efficient)
For near-instant updates, use WebSocket instead of polling. Replace the startMessagePolling function with this:
function connectWebSocket() { const socket = new WebSocket(`wss://directline.botframework.com/v3/directline/conversations/${conversationId}/stream?token=${directLineToken}`); socket.onopen = () => console.log('WebSocket connected to bot'); socket.onmessage = (event) => { const envelope = JSON.parse(event.data); if (envelope.activity && envelope.activity.type === 'message' && envelope.activity.from.id !== 'unique-user-id') { $('#chat-messages').append(`<div class="bot-msg">Bot: ${envelope.activity.text}</div>`); scrollToBottom(); } }; socket.onerror = (err) => console.error('WebSocket error:', err); socket.onclose = () => { console.log('WebSocket disconnected. Reconnecting...'); setTimeout(connectWebSocket, 5000); // Reconnect after 5 seconds if disconnected }; }
- Security: Never expose your Direct Line secret in frontend code—your backend proxy is the correct approach here.
- Token Expiry: Direct Line tokens expire after 30 minutes. Implement token refresh logic if your app needs long sessions (call the
/conversations/{conversationId}/token/refreshendpoint from your backend). - Activity Schema: Ensure all outgoing messages follow the Direct Line Activity schema—required fields include
type,from.id, andtextfor message activities. - Error Handling: Add retry logic for failed AJAX calls or WebSocket reconnections to make your chat more robust.
内容的提问来源于stack exchange,提问作者Sarthak Srivastava

