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

React组件渲染OpenAI API返回的JSON字符串化响应的格式化方案咨询

解决React中安全渲染OpenAI格式化文本的方案

1. 纯文本解析+React组件映射

手动解析OpenAI返回的文本,将不同格式元素转换成对应React组件,完全规避dangerouslySetInnerHTML的安全风险。

核心实现步骤:

  • 按换行符拆分文本,识别段落、无序列表项(以- 开头)、有序列表项(以数字. 开头)
  • 用正则匹配URL,替换为带安全属性的React <a> 组件,并支持长URL自动缩短
  • 分段渲染对应组件:普通段落用<p>包裹,列表项收集后用<ul>/<ol>+<li>包裹

示例代码:

import React from 'react';

const parseOpenAIText = (text) => {
  const lines = text.split('\n');
  const elements = [];
  let currentListType = null; // 标记当前是'ul'还是'ol'
  let listItems = [];

  // 匹配URL的正则
  const urlRegex = /https?:\/\/[^\s]+/g;

  // 处理单行内容,替换URL为<a>组件
  const renderLineContent = (line) => {
    const textParts = line.split(urlRegex);
    const urls = line.match(urlRegex) || [];
    return textParts.map((part, index) => (
      <React.Fragment key={index}>
        {part}
        {urls[index] && (
          <a 
            href={urls[index]} 
            target="_blank" 
            rel="noopener noreferrer"
            style={{ color: '#0066cc', textDecoration: 'underline' }}
          >
            {urls[index].length > 50 ? `${urls[index].slice(0, 47)}...` : urls[index]}
          </a>
        )}
      </React.Fragment>
    ));
  };

  lines.forEach((line) => {
    const trimmedLine = line.trim();
    if (!trimmedLine) {
      // 空行:结束当前列表(如果存在)
      if (currentListType) {
        elements.push(
          currentListType === 'ul' ? (
            <ul key={elements.length}>{listItems.map((item, i) => <li key={i}>{item}</li>)}</ul>
          ) : (
            <ol key={elements.length}>{listItems.map((item, i) => <li key={i}>{item}</li>)}</ol>
          )
        );
        currentListType = null;
        listItems = [];
      }
      elements.push(<br key={elements.length} />);
      return;
    }

    // 识别无序列表项
    if (trimmedLine.startsWith('- ')) {
      currentListType = 'ul';
      listItems.push(renderLineContent(trimmedLine.slice(2)));
      return;
    }

    // 识别有序列表项
    if (/^\d+\. /.test(trimmedLine)) {
      currentListType = 'ol';
      listItems.push(renderLineContent(trimmedLine.replace(/^\d+\. /, '')));
      return;
    }

    // 普通段落:先结束未完成的列表
    if (currentListType) {
      elements.push(
        currentListType === 'ul' ? (
          <ul key={elements.length}>{listItems.map((item, i) => <li key={i}>{item}</li>)}</ul>
        ) : (
          <ol key={elements.length}>{listItems.map((item, i) => <li key={i}>{item}</li>)}</ol>
        )
      );
      currentListType = null;
      listItems = [];
    }
    elements.push(<p key={elements.length}>{renderLineContent(trimmedLine)}</p>);
  });

  // 处理最后剩余的列表
  if (currentListType) {
    elements.push(
      currentListType === 'ul' ? (
        <ul key={elements.length}>{listItems.map((item, i) => <li key={i}>{item}</li>)}</ul>
      ) : (
        <ol key={elements.length}>{listItems.map((item, i) => <li key={i}>{item}</li>)}</ol>
      )
    );
  }

  return elements;
};

// 组件使用示例
const ChatMessage = ({ content }) => {
  return <div className="chat-message">{parseOpenAIText(content)}</div>;
};

export default ChatMessage;

2. 使用安全的Markdown渲染库

选择内置XSS防护的Markdown解析库,直接将OpenAI返回的Markdown文本转换成React组件,无需手动解析。

推荐方案:react-markdown

该库完全基于React组件渲染,无innerHTML风险,支持自定义组件样式和行为。

安装命令:

npm install react-markdown

示例代码:

import React from 'react';
import ReactMarkdown from 'react-markdown';

const ChatMessage = ({ content }) => {
  return (
    <div className="chat-message">
      <ReactMarkdown
        components={{
          // 自定义链接组件:缩短长URL+添加安全属性
          a: ({ href, children }) => {
            const displayText = href.length > 50 ? `${href.slice(0, 47)}...` : href;
            return (
              <a 
                href={href} 
                target="_blank" 
                rel="noopener noreferrer"
                style={{ color: '#0066cc', textDecoration: 'underline' }}
              >
                {displayText}
              </a>
            );
          },
          // 自定义基础样式
          p: ({ children }) => <p style={{ margin: '8px 0' }}>{children}</p>,
          ul: ({ children }) => <ul style={{ margin: '8px 0', paddingLeft: '24px' }}>{children}</ul>,
          ol: ({ children }) => <ol style={{ margin: '8px 0', paddingLeft: '24px' }}>{children}</ol>,
          li: ({ children }) => <li style={{ margin: '4px 0' }}>{children}</li>
        }}
      >
        {content}
      </ReactMarkdown>
    </div>
  );
};

export default ChatMessage;

3. 预处理OpenAI返回内容(可选)

调用API时,在prompt中明确要求返回标准Markdown格式,比如添加:"请使用标准Markdown格式输出,包括段落分隔、有序/无序列表、链接使用显示文本格式",能大幅降低前端解析的复杂度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 18:41:13