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

使用JavaScript fetch调用Zendesk工单API无法获取响应的问题

解决方案:跨域问题处理 + 获取Zendesk工单ID并上传附件

核心问题分析

  • CORS跨域拦截:你遇到的Cross-Origin Request Blocked是因为浏览器同源策略限制,Zendesk API默认不会为前端域名添加Access-Control-Allow-Origin响应头。即使工单创建成功(返回201状态),浏览器也会阻止前端读取响应内容,导致无法获取工单ID。
  • fetch调用错误:你的代码中第一个then仅把Response对象赋值给id,未解析响应体;且createTicket是异步函数,直接return id会得到undefined——因为fetch请求还未完成。

步骤1:通过后端代理解决CORS问题

前端不能直接调用Zendesk API,必须通过你的后端服务器转发请求。后端与Zendesk API通信不受同源策略限制,再将响应返回给前端。

后端代理示例(Node.js/Express)

const express = require('express');
const fetch = require('node-fetch');
const multer = require('multer'); // 用于处理文件上传
const upload = multer();
const app = express();
app.use(express.json());

// 后端存储Zendesk认证信息(禁止暴露给前端)
const ZENDESK_DOMAIN = 'https://easywalkersupport.zendesk.com';
const ZENDESK_AUTH = 'Basic ' + Buffer.from('你的Zendesk邮箱/token:你的API令牌').toString('base64');

// 创建工单的代理接口
app.post('/api/create-zendesk-ticket', async (req, res) => {
  try {
    const response = await fetch(`${ZENDESK_DOMAIN}/api/v2/tickets`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': ZENDESK_AUTH
      },
      body: JSON.stringify(req.body)
    });
    const data = await response.json();
    res.status(response.status).json(data);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// 上传附件并关联工单的代理接口
app.post('/api/upload-zendesk-attachment/:ticketId', upload.single('file'), async (req, res) => {
  try {
    // 第一步:上传文件到Zendesk获取上传令牌
    const formData = new FormData();
    formData.append('file', req.file.buffer, req.file.originalname);
    
    const uploadResponse = await fetch(`${ZENDESK_DOMAIN}/api/v2/uploads.json?filename=${req.file.originalname}`, {
      method: 'POST',
      headers: {
        'Authorization': ZENDESK_AUTH
      },
      body: formData
    });
    const uploadData = await uploadResponse.json();

    // 第二步:将附件关联到目标工单
    await fetch(`${ZENDESK_DOMAIN}/api/v2/tickets/${req.params.ticketId}`, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': ZENDESK_AUTH
      },
      body: JSON.stringify({
        ticket: {
          comment: {
            body: '附上保修相关文件',
            uploads: [uploadData.upload.token]
          }
        }
      })
    });

    res.status(200).json({ success: true });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(3000, () => console.log('代理服务器运行在3000端口'));

步骤2:修改前端代码,获取工单ID并上传文件

更新前端函数,使用async/await处理异步逻辑,调用后端代理接口完成工单创建与文件上传:

async function createTicket() {      
    const easyWalker = $("#choose_product option:selected").html();
    const batchNumber = document.getElementById("batch_number").value;
    const color = document.getElementById("color").value;
    const firstName = document.getElementById("first_name").value;
    const lastName = document.getElementById("last_name").value;
    const email = document.getElementById("email").value;
    const phone = document.getElementById("phone").value;
  
    console.log("创建工单中");

    const ticketData = {
        "ticket": {
            "subject": `New Easywalker Warranty Registry from ${firstName} ${lastName}`,
            "description": 
              `Product: ${easyWalker}\n` +
              `Batch Number: ${batchNumber}\n` +
              `Color: ${color}\n` +
              `Name: ${firstName} ${lastName}\n` +
              `Email: ${email}\n` +
              `Phone Number: ${phone}`,
            "requester": {
              "name": `${firstName} ${lastName}`,
              "email": email
            }
        }
    };
    
    try {
        // 调用后端接口创建工单
        const ticketResponse = await fetch('/api/create-zendesk-ticket', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(ticketData)
        });

        const ticketResult = await ticketResponse.json();
        const ticketId = ticketResult.ticket.id; // 提取工单ID
        console.log('已创建工单ID:', ticketId);

        // 上传文件到工单(假设文件输入框id为warranty_file)
        const fileInput = document.getElementById('warranty_file');
        if (fileInput.files.length > 0) {
            const file = fileInput.files[0];
            const formData = new FormData();
            formData.append('file', file);

            await fetch(`/api/upload-zendesk-attachment/${ticketId}`, {
                method: 'POST',
                body: formData
            });
            console.log('文件上传成功');
        }

        return ticketId;
    } catch (error) {
        console.log('错误:', error);
        throw error;
    }
}

// 调用示例:
// createTicket().then(id => console.log('最终工单ID:', id)).catch(err => console.error(err));

关键说明

  • 安全提示:绝对不能在前端暴露Zendesk的API令牌或认证信息,必须存储在后端,否则会导致账号泄露。
  • Zendesk响应结构:创建工单成功后,响应体中的ticket.id即为所需工单ID,通过result.ticket.id直接提取。
  • 文件上传逻辑:Zendesk要求先上传文件获取令牌,再将令牌关联到工单评论中,这两步必须通过后端完成。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 19:41:03