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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 01:13:31