如何在Next.js中使用useChat Hook对接带认证的外部API?
用Next.js useChat Hook对接带认证的外部流式API
能否使用useChat Hook?
可以。useChat Hook不局限于项目内的api/chat路由,支持通过自定义配置对接外部带认证的流式API。
配置外部API的方法
1. 自定义fetch函数(适配任意API格式)
通过fetchFn参数手动构造请求,添加认证令牌,同时确保响应格式符合useChat的流式要求:
'use client'; import { useChat } from '@ai-sdk/react'; export default function ChatComponent() { const { messages, input, handleInputChange, handleSubmit } = useChat({ fetchFn: async (options) => { const response = await fetch('https://your-external-api.com/stream-chat', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.NEXT_PUBLIC_AUTH_TOKEN}`, // 替换为你的认证令牌 }, body: JSON.stringify({ messages: options.messages.map(msg => ({ role: msg.role, content: msg.content, })), }), credentials: 'omit', // 根据外部API要求调整 }); if (!response.ok) { throw new Error(`API请求失败: ${response.status}`); } return response; }, }); return ( <div className="chat-container"> {messages.map((msg) => ( <div key={msg.id} className={`message ${msg.role}`}> <p>{msg.content}</p> </div> ))} <form onSubmit={handleSubmit}> <input type="text" value={input} onChange={handleInputChange} placeholder="输入消息..." /> <button type="submit">发送</button> </form> </div> ); }
2. 直接指定API地址(兼容默认格式时简化配置)
如果外部API的请求/响应格式和useChat默认兼容(返回Server-Sent Events流式文本),可直接设置apiUrl并传入认证头:
'use client'; import { useChat } from '@ai-sdk/react'; export default function ChatComponent() { const { messages, input, handleInputChange, handleSubmit } = useChat({ apiUrl: 'https://your-external-api.com/stream-chat', headers: { 'Authorization': `Bearer ${process.env.NEXT_PUBLIC_AUTH_TOKEN}`, }, }); // 渲染逻辑同上方示例 return ( <div className="chat-container"> {/* 消息列表与输入框 */} </div> ); }
备选方案:手动实现流式聊天(API格式不兼容时)
如果外部API格式与useChat预期差异较大,可通过原生fetch结合ReadableStream手动实现:
'use client'; import { useState, useRef } from 'react'; export default function ManualChatComponent() { const [messages, setMessages] = useState<{role: 'user' | 'assistant', content: string}[]>([]); const [input, setInput] = useState(''); const [isLoading, setIsLoading] = useState(false); const endOfMessagesRef = useRef<HTMLDivElement>(null); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!input.trim()) return; const newUserMsg = { role: 'user' as const, content: input }; setMessages(prev => [...prev, newUserMsg]); setInput(''); setIsLoading(true); try { const response = await fetch('https://your-external-api.com/stream-chat', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.NEXT_PUBLIC_AUTH_TOKEN}`, }, body: JSON.stringify({ messages: [...messages, newUserMsg] }), }); if (!response.ok || !response.body) throw new Error('请求失败'); const reader = response.body.getReader(); const decoder = new TextDecoder(); let assistantContent = ''; setMessages(prev => [...prev, { role: 'assistant', content: '' }]); while (true) { const { done, value } = await reader.read(); if (done) break; assistantContent += decoder.decode(value, { stream: true }); setMessages(prev => { const updated = [...prev]; updated[updated.length - 1] = { role: 'assistant', content: assistantContent }; return updated; }); endOfMessagesRef.current?.scrollIntoView({ behavior: 'smooth' }); } } catch (err) { setMessages(prev => [...prev, { role: 'assistant', content: '抱歉,请求失败了。' }]); } finally { setIsLoading(false); } }; return ( <div className="chat-container"> <div className="messages"> {messages.map((msg, idx) => ( <div key={idx} className={`message ${msg.role}`}> <p>{msg.content}</p> </div> ))} {isLoading && <div className="message assistant"><p>正在回复...</p></div>} <div ref={endOfMessagesRef} /> </div> <form onSubmit={handleSubmit}> <input type="text" value={input} onChange={(e) => setInput(e.target.value)} placeholder="输入消息..." disabled={isLoading} /> <button type="submit" disabled={isLoading}> {isLoading ? '发送中...' : '发送'} </button> </form> </div> ); }
内容的提问来源于stack exchange,提问作者Learn AspNet
相关产品推荐
相关产品推荐

