调用Zoho Creator API获取图片时出现Error 401问题求助
解决React+Zoho Creator API图片访问401错误
问题根源
- Zoho Creator的图片资源受OAuth2保护,前端直接访问图片URL时无法携带授权头,导致返回401 Unauthorized
- 现有Express代码存在语法错误(
app.get未完整定义路由) - Access Token仅在服务启动时获取一次,过期后无法自动刷新,会导致后续请求失效
修复方案
1. 修正Express后端代码
实现自动刷新Access Token、正确的API数据接口、图片代理路由(由后端携带授权头请求Zoho图片,再返回给前端)
const express = require("express"); const app = express(); const fetch = require("node-fetch"); const bodyParser = require("body-parser"); const cors = require("cors"); const PORT = process.env.PORT || 4000; // CORS配置 const whitelist = ['https://zine-hosteleria-frontend12012023.onrender.com']; const corsOptions = { origin: function (origin, callback) { if (whitelist.indexOf(origin) !== -1 || !origin) { callback(null, true); } else { callback(new Error('Not allowed by CORS')); } } }; app.use(bodyParser.json()); app.use(cors(corsOptions)); // Zoho配置 const ZOHO_REFRESH_TOKEN = "1000.3dbdad6937dc0800c4dcc662cd14d173.86efb18e337989bebb3ff4c05582c94c"; const ZOHO_CLIENT_ID = "1000.NQL17JHK3Y62Y178TO0E3FQC6MBQJV"; const ZOHO_CLIENT_SECRET = "5d04ad135862e7313377484af55efa1f41c1f49a39"; const ZOHO_TOKEN_URL = `https://accounts.zoho.eu/oauth/v2/token?refresh_token=${ZOHO_REFRESH_TOKEN}&client_id=${ZOHO_CLIENT_ID}&client_secret=${ZOHO_CLIENT_SECRET}&grant_type=refresh_token`; const ZOHO_PRODUCTS_URL = "https://creator.zoho.eu/api/v2/hostienda1/Product-Catalog/report/Product_Details"; // 存储当前有效的Access Token和过期时间 let currentAccessToken = null; let tokenExpiryTime = 0; // 获取/刷新Access Token的函数 async function getAccessToken() { const now = Date.now(); // 如果token未过期,直接返回 if (currentAccessToken && now < tokenExpiryTime) { return currentAccessToken; } // 刷新token const response = await fetch(ZOHO_TOKEN_URL, { method: "POST" }); const data = await response.json(); currentAccessToken = data.access_token; // 设置过期时间(提前30秒刷新,避免刚好过期) tokenExpiryTime = now + (data.expires_in - 30) * 1000; return currentAccessToken; } // 获取产品数据的接口 app.get("/api/products", async (req, res) => { try { const accessToken = await getAccessToken(); const response = await fetch(ZOHO_PRODUCTS_URL, { method: "GET", headers: { 'Authorization': `Zoho-oauthtoken ${accessToken}` } }); const result = await response.json(); const products = result.data; // 修改图片路径为后端代理路由,而不是直接的Zoho URL products.forEach(product => { if (product.Product_Images && product.Product_Images.length > 0) { // 把Zoho的相对路径转为后端代理路径 product.Product_Images[0].proxy_url = `/api/image?path=${encodeURIComponent(product.Product_Images[0].display_value)}`; } }); res.json(products); } catch (error) { console.error("获取产品数据失败:", error); res.status(500).json({ error: "Failed to fetch products" }); } }); // 图片代理路由:后端携带授权头请求Zoho图片,返回给前端 app.get("/api/image", async (req, res) => { try { const imagePath = decodeURIComponent(req.query.path); const accessToken = await getAccessToken(); const imageUrl = `https://creator.zoho.eu${imagePath}`; const response = await fetch(imageUrl, { headers: { 'Authorization': `Zoho-oauthtoken ${accessToken}` } }); // 把图片流转发给前端 res.setHeader("Content-Type", response.headers.get("Content-Type")); response.body.pipe(res); } catch (error) { console.error("获取图片失败:", error); res.status(401).json({ error: "Failed to fetch image" }); } }); app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); });
2. React前端修改
在React组件中,使用后端提供的proxy_url来渲染图片,而不是直接使用Zoho的URL:
import { useEffect, useState } from 'react'; function ProductList() { const [products, setProducts] = useState([]); useEffect(() => { fetch('https://your-express-backend-url/api/products') .then(res => res.json()) .then(data => setProducts(data)) .catch(err => console.error(err)); }, []); return ( <div className="product-list"> {products.map(product => ( <div key={product.id} className="product"> <h3>{product.Product_Name}</h3> {product.Product_Images && product.Product_Images[0] && ( <img src={product.Product_Images[0].proxy_url} alt={product.Product_Name} style={{ width: '200px' }} /> )} </div> ))} </div> ); } export default ProductList;
关键说明
- 后端代理图片请求时,会自动携带有效的Zoho OAuth2授权头,避免前端直接访问的401问题
- Access Token会自动刷新,避免过期导致的请求失败
- 前端只需要和后端API交互,不需要处理Zoho的授权逻辑
内容的提问来源于stack exchange,提问作者symon
相关产品推荐
相关产品推荐

