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

React+Express项目无法渲染Zoho Creator API返回图片求助

解决Zoho Creator图片无法渲染的401问题

问题根源

Zoho Creator的图片资源必须携带OAuth授权令牌才能访问,前端直接补全URL前缀后发起请求,请求头里没有授权信息,因此返回401错误。后端直接补前缀再返回给前端,本质还是前端去请求Zoho的资源,同样缺少授权,所以问题依旧。

解决方案:后端代理图片请求

让后端作为中间层,前端请求后端的图片路由,后端携带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;

app.use(bodyParser.json());
app.use(cors())

const urlPost = "https://accounts.zoho.eu/oauth/v2/token?refresh_token=1000.3dbdad6937dc0800c4dcc662cd14d173.86efb18e337989bebb3ff4c05582c94c&client_id=1000.NQL17JHK3Y62Y178TO0E3FQC6MBQJV&client_secret=5d04ad135862e7313377484af55efa1f41c1f49a39&grant_type=refresh_token"
const urlGet = "https://creator.zoho.eu/api/v2/hostienda1/Product-Catalog/report/Product_Details";

// 封装获取授权令牌的函数,支持按需刷新
async function getAccessToken() {
  const response = await fetch(urlPost, { method: 'POST', redirect: 'follow' })
  const resp = await response.json()
  return resp.access_token
}

// 产品数据接口
app.get("/products", async (req, res) => {
  try {
    const accessToken = await getAccessToken()
    const response = await fetch(urlGet, {
      method: "GET",
      headers: { 'Authorization': `Zoho-oauthtoken ${accessToken}` }
    })
    const result = await response.json()
    const products = result.data

    // 将图片URL替换为后端代理路由
    products.forEach(product => {
      if (product.Product_Images && product.Product_Images[0]) {
        // 提取图片相对路径,避免重复前缀
        const imagePath = product.Product_Images[0].display_value.replace(/^https?:\/\/creator\.zoho\.eu/, '')
        product.Product_Images[0].display_value = `/api/image?path=${encodeURIComponent(imagePath)}`
      }
    })

    res.json(products)
  } catch (error) {
    console.error("获取产品数据出错:", error)
    res.status(500).json({ error: "获取数据失败" })
  }
})

// 图片代理接口
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 imageResponse = await fetch(imageUrl, {
      headers: { 'Authorization': `Zoho-oauthtoken ${accessToken}` }
    })

    // 转发图片的响应头与内容
    res.setHeader('Content-Type', imageResponse.headers.get('Content-Type'))
    imageResponse.body.pipe(res)
  } catch (error) {
    console.error("获取图片出错:", error)
    res.status(404).json({ error: "图片无法获取" })
  }
})

app.listen(PORT, () => { console.log(`Listening on port ${PORT}`) })

关键修改说明

  • 令牌按需获取:封装getAccessToken函数,每次请求都重新获取令牌(可按需添加缓存逻辑,根据令牌过期时间优化),避免启动时单次获取导致令牌过期失效。
  • 图片代理路由:前端通过/api/image?path=xxx请求图片,后端携带授权头去Zoho获取资源,再将图片内容返回给前端,彻底绕开401权限问题。
  • URL替换逻辑:将产品数据中的图片路径替换为后端代理地址,前端直接使用该地址即可加载图片,无需处理前缀。
  • 语法与逻辑修复:移除原代码中多余的app.get语句,改用forEach直接修改产品数据(map返回新数组但原代码未保存结果,forEach更贴合需求)。

前端调整

前端请求产品数据后,直接使用图片字段的display_value即可,无需额外处理前缀。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 14:21:02