Google Colab React+Flask文本摘要项目:Backend URL未设置问题解决
问题:点击Summarize按钮出现‘backend URL not set’错误
我正在开发一个基于Google Colab的React前端与Flask后端集成的文本摘要项目,但点击Summarize按钮时出现‘backend URL not set’错误,点击按钮无反应,浏览器控制台显示该错误。希望实现输入长文本后获取4种不同模型生成的摘要,求错误原因及解决方法。
Flask后端(Google Colab Notebook代码)
安装依赖
!pip install flask !pip install transformers !pip install pyngrok !pip install flask-ngrok !pip install python-dotenv !pip install flask-cors
核心代码
import os import shutil from flask import Flask, request, jsonify, send_from_directory from flask_cors import CORS from transformers import pipeline from pyngrok import ngrok, conf from dotenv import load_dotenv import threading import time import subprocess # Load environment variables from .env file load_dotenv() # Initialize Flask app app = Flask(__name__, static_folder='build/build') CORS(app) # Enable CORS for all origins # Delete the existing 'build' directory if it exists build_dir = 'build/build' if os.path.exists(build_dir): shutil.rmtree(build_dir) print("Deleted the existing 'build' directory.") # Unzip the build.zip file !unzip -o build.zip -d build/ # Set up ngrok with authentication token (if using a paid plan) ngrok.set_auth_token('2hkd7J7tIDvMWx0hPImWMrQfoIi_7tcxWdqFTNucPfD2p9oSM') # Function to kill all existing ngrok processes def kill_existing_ngrok(): try: subprocess.run(['pkill', '-f', 'ngrok'], check=True) print("Killed existing ngrok processes.") except subprocess.CalledProcessError: print("No existing ngrok processes found.") # Kill existing ngrok processes before starting a new one kill_existing_ngrok() # Start ngrok tunnel to expose the Flask app on port 8000 ngrok_tunnel = ngrok.connect(8000) print(f' * Tunnel URL: {ngrok_tunnel.public_url}') # Define function to generate TL;DR summary def generate_tldr(full_text): # Take the first 2000 characters as TL;DR summary tldr = full_text[:2000] return tldr.strip() # Initialize models models = { "GPT-2": {"type": "text-generation", "model": "gpt2-medium"}, "T5": {"type": "summarization", "model": "t5-base"}, "BART": {"type": "summarization", "model": "facebook/bart-large-cnn"}, "PEGASUS": {"type": "summarization", "model": "google/pegasus-cnn_dailymail"} } # Define route for serving the React app @app.route('/', defaults={'path': ''}) @app.route('/<path:path>') def serve(path): if path != "" and os.path.exists(os.path.join(app.static_folder, path)): return send_from_directory(app.static_folder, path) else: return send_from_directory(app.static_folder, 'index.html') # Define route for generating summaries @app.route('/generate_summary', methods=['POST']) def generate_summary(): try: # Get input text from the request text = request.json.get('text', '')[:2000] # Take first 2000 characters as input if not text: return jsonify({'error': 'No text provided'}), 400 # Initialize dictionary to store summaries summaries = {} # Iterate through each model for model_name, model_config in models.items(): # Initialize pipeline for the model pipe = pipeline(model_config["type"], model=model_config["model"]) # Process input text based on model type if model_config["type"] == "text-generation": # For text generation models like GPT-2 pipe_out = pipe(text, max_length=512, clean_up_tokenization_spaces=True) generated_text = pipe_out[0]['generated_text'] tldr = generate_tldr(generated_text) summaries[model_name] = {"summary": generated_text, "tldr": tldr} else: # For summarization models like T5, BART, and PEGASUS pipe_out = pipe(text) summary_text = pipe_out[0]['summary_text'] tldr = generate_tldr(summary_text) summaries[model_name] = {"summary": summary_text, "tldr": tldr} return jsonify(summaries) except Exception as e: return jsonify({'error': str(e)}), 500 # Add endpoint to serve ngrok URL @app.route('/ngrok_url', methods=['GET']) def get_ngrok_url(): return jsonify({"ngrok_url": ngrok_tunnel.public_url}) # Function to run Flask app without debug mode def run_flask(): app.run(port=8000) # Run Flask app in a separate thread thread = threading.Thread(target=run_flask) thread.start()
React前端(App.js代码)
import React, { useState, useEffect } from 'react'; import axios from 'axios'; import './App.css'; function App() { const [inputText, setInputText] = useState(''); const [summaries, setSummaries] = useState(null); const [backendUrl, setBackendUrl] = useState(''); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchNgrokUrl = async () => { try { const response = await axios.get('/ngrok_url'); // Assuming the React app and Flask are served from the same origin setBackendUrl(response.data.ngrok_url); } catch (error) { console.error('Error fetching ngrok URL:', error); setError('Failed to fetch backend URL'); } finally { setLoading(false); } }; if (!backendUrl) { fetchNgrokUrl(); } else { setLoading(false); // If backendUrl is already set, setLoading to false } }, [backendUrl]); // Only run this effect once on mount or if backendUrl changes const handleSubmit = async (e) => { e.preventDefault(); if (!backendUrl) { console.error('Backend URL not set'); setError('Backend URL not set'); return; } try { const response = await axios.post(`${backendUrl}/generate_summary`, { text: inputText }); setSummaries(response.data); setError(null); // Clear any previous errors } catch (error) { console.error('Error fetching summaries:', error); setError('Failed to fetch summaries'); } }; return ( <div className="App"> <header className="App-header"> <h1>Text Summarization</h1> <form onSubmit={handleSubmit}> <textarea value={inputText} onChange={(e) => setInputText(e.target.value)} placeholder="Enter text to summarize..." rows="10" cols="50" /> <button type="submit" disabled={loading}>Summarize</button> </form> {loading && <p>Loading...</p>} {error && <p className="error">{error}</p>} {summaries && ( <div className="summaries"> {Object.keys(summaries).map((model) => ( <div className="summary" key={model}> <h2>{`${model} Summary`}</h2> <p>{summaries[model].summary}</p> <h3>TL;DR:</h3> <p>{summaries[model].tldr}</p> </div> ))} </div> )} </header> </div> ); } export default App;
错误原因及解决方法
核心问题
- 前端请求逻辑冗余且错误:React由Flask直接托管,属于同域服务,无需额外获取ngrok URL。但当前代码拿到ngrok URL后又用它发起跨域请求,不仅没必要,还可能因请求失败导致
backendUrl一直为空。 - 状态更新逻辑异常:
useEffect依赖backendUrl,若/ngrok_url请求失败,backendUrl始终为空,会触发useEffect反复执行,进一步加剧状态异常。
解决步骤
步骤1:简化前端API请求逻辑
删除backendUrl相关逻辑,直接用相对路径请求后端接口:
import React, { useState } from 'react'; import axios from 'axios'; import './App.css'; function App() { const [inputText, setInputText] = useState(''); const [summaries, setSummaries] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const handleSubmit = async (e) => { e.preventDefault(); if (!inputText.trim()) { setError('请输入要摘要的文本'); return; } setLoading(true); setError(null); try { // 直接用相对路径请求,无需ngrok URL const response = await axios.post('/generate_summary', { text: inputText }); setSummaries(response.data); } catch (error) { console.error('获取摘要失败:', error); setError('获取摘要失败,请重试'); } finally { setLoading(false); } }; return ( <div className="App"> <header className="App-header"> <h1>文本摘要工具</h1> <form onSubmit={handleSubmit}> <textarea value={inputText} onChange={(e) => setInputText(e.target.value)} placeholder="请输入要摘要的文本..." rows="10" cols="50" /> <button type="submit" disabled={loading || !inputText.trim()}> {loading ? '生成中...' : '生成摘要'} </button> </form> {error && <p className="error">{error}</p>} {summaries && ( <div className="summaries"> {Object.keys(summaries).map((model) => ( <div className="summary" key={model}> <h2>{`${model} 摘要`}</h2> <p>{summaries[model].summary}</p> <h3>精简版:</h3> <p>{summaries[model].tldr}</p> </div> ))} </div> )} </header> </div> ); } export default App;
步骤2:优化Flask后端性能与稳定性
- 提前加载模型:避免每次请求重新初始化模型,大幅提升响应速度:
# 提前加载所有模型,避免重复初始化 loaded_models = {} for model_name, model_config in models.items(): loaded_models[model_name] = pipeline(model_config["type"], model=model_config["model"]) print(f"Loaded {model_name} model successfully.") # 修改generate_summary路由 @app.route('/generate_summary', methods=['POST']) def generate_summary(): try: text = request.json.get('text', '')[:2000] if not text: return jsonify({'error': 'No text provided'}), 400 summaries = {} for model_name, pipe in loaded_models.items(): model_config = models[model_name] if model_config["type"] == "text-generation": pipe_out = pipe(text, max_length=512, clean_up_tokenization_spaces=True) generated_text = pipe_out[0]['generated_text'] tldr = generate_tldr(generated_text) summaries[model_name] = {"summary": generated_text, "tldr": tldr} else: pipe_out = pipe(text) summary_text = pipe_out[0]['summary_text'] tldr = generate_tldr(summary_text) summaries[model_name] = {"summary": summary_text, "tldr": tldr} return jsonify(summaries) except Exception as e: return jsonify({'error': str(e)}), 500
- 修复Flask启动参数:添加
use_reloader=False避免线程重复启动:
def run_flask(): app.run(port=8000, debug=False, use_reloader=False)
步骤3:验证流程
- 在Colab中运行Flask后端代码,等待所有模型加载完成,复制ngrok公网URL。
- 在浏览器中打开该URL,即可使用前端页面输入文本生成摘要。
内容的提问来源于stack exchange,提问作者Amit Bin Tariqul
相关产品推荐
相关产品推荐

