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

基于Google Drive API的OAuth入门:gapi.client.init返回502错误

问题

开发Google Drive Web客户端时,调用gapi.client.init()收到502 - Bad Gateway响应,且浏览器报错API discovery response missing required fields.。严格按照官方快速开始教程操作,仅替换了CLIENT_ID和API_KEY,普通GCP项目可正常运行,但Google Workspace项目无法工作,预期弹出OAuth授权同意界面却失败。

客户端代码

<!DOCTYPE html>
<html>
  <head>
    <title>Drive API Quickstart</title>
    <meta charset="utf-8" />
  </head>
  <body>
    <p>Drive API Quickstart</p>

    <!--Add buttons to initiate auth sequence and sign out-->
    <button id="authorize_button" onclick="handleAuthClick()">Authorize</button>
    <button id="signout_button" onclick="handleSignoutClick()">Sign Out</button>

    <pre id="content" style="white-space: pre-wrap;"></pre>

    <script type="text/javascript">
      /* exported gapiLoaded */
      /* exported gisLoaded */
      /* exported handleAuthClick */
      /* exported handleSignoutClick */

      // TODO(developer): Set to client ID and API key from the Developer Console
      const CLIENT_ID = 'my client id';
      const API_KEY = 'my api key';

      // Discovery doc URL for APIs used by the quickstart
      const DISCOVERY_DOC = 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest';

      // Authorization scopes required by the API; multiple scopes can be
      // included, separated by spaces.
      const SCOPES = 'https://www.googleapis.com/auth/drive.metadata.readonly';

      let tokenClient;
      let gapiInited = false;
      let gisInited = false;

      document.getElementById('authorize_button').style.visibility = 'hidden';
      document.getElementById('signout_button').style.visibility = 'hidden';

      /**
       * Callback after api.js is loaded.
       */
      function gapiLoaded() {
        gapi.load('client', initializeGapiClient);
      }

      /**
       * Callback after the API client is loaded. Loads the
       * discovery doc to initialize the API.
       */
      async function initializeGapiClient() {
        await gapi.client.init({
          apiKey: API_KEY,
          discoveryDocs: [DISCOVERY_DOC],
        });
        gapiInited = true;
        maybeEnableButtons();
      }

      /**
       * Callback after Google Identity Services are loaded.
       */
      function gisLoaded() {
        tokenClient = google.accounts.oauth2.initTokenClient({
          client_id: CLIENT_ID,
          scope: SCOPES,
          callback: '', // defined later
        });
        gisInited = true;
        maybeEnableButtons();
      }

      /**
       * Enables user interaction after all libraries are loaded.
       */
      function maybeEnableButtons() {
        if (gapiInited && gisInited) {
          document.getElementById('authorize_button').style.visibility = 'visible';
        }
      }

      /**
       *  Sign in the user upon button click.
       */
      function handleAuthClick() {
        tokenClient.callback = async (resp) => {
          if (resp.error !== undefined) {
            throw (resp);
          }
          document.getElementById('signout_button').style.visibility = 'visible';
          document.getElementById('authorize_button').innerText = 'Refresh';
          await listFiles();
        };

        if (gapi.client.getToken() === null) {
          // Prompt the user to select a Google Account and ask for consent to share their data
          // when establishing a new session.
          tokenClient.requestAccessToken({prompt: 'consent'});
        } else {
          // Skip display of account chooser and consent dialog for an existing session.
          tokenClient.requestAccessToken({prompt: ''});
        }
      }

      /**
       *  Sign out the user upon button click.
       */
      function handleSignoutClick() {
        const token = gapi.client.getToken();
        if (token !== null) {
          google.accounts.oauth2.revoke(token.access_token);
          gapi.client.setToken('');
          document.getElementById('content').innerText = '';
          document.getElementById('authorize_button').innerText = 'Authorize';
          document.getElementById('signout_button').style.visibility = 'hidden';
        }
      }

      /**
       * Print metadata for first 10 files.
       */
      async function listFiles() {
        let response;
        try {
          response = await gapi.client.drive.files.list({
            'pageSize': 10,
            'k
        } catch (err) {
          document.getElementById('content').innerText = err.message;
          return;
        }
        const files = response.result.files;
        if (!files || files.length == 0) {
          document.getElementById('content').innerText = 'No files found.';
          return;
        }
        // Flatten to string to display
        const output = files.reduce(
            (str, file) => `${str}${file.name} (${file.id})\n`,
            'Files:\n');
        document.getElementById('content').innerText = output;
      }
    </script>
    <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script>
    <script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script>
  </body>
</html>

浏览器错误详情

cb=gapi.loaded_0?le=scs:654 Uncaught (in promise)

WA {message: 'API discovery response missing required fields.', rZ: true, stack: 'gapix.client.GapiClientError: API discovery respon…u9FH68l5KSqr6MBg/cb=gapi.loaded_0?le=scs:168:116)'}

message: "API discovery response missing required fields."

rZ: true

stack: "gapix.client.GapiClientError: API discovery response missing required fields.\n    at new WA (https://apis.google.com/_/scs/abc-static/_/js/k=gapi.lb.en.nJpyt-fjzo8.O/m=client/rt=j/sv=1/d=1/ed=1/rs=AHpOoo9fTqXSCmwMDsu9FH68l5KSqr6MBg/cb=gapi.loaded_0?le=scs:654:159)\n    at c (https://apis.google.com/_/scs/abc-static/_/js/k=gapi.lb.en.nJpyt-fjzo8.O/m=client/rt=j/sv=1/d=1/ed=1/rs=AHpOoo9fTqXSCmwMDsu9FH68l5KSqr6MBg/cb=gapi.loaded_0?le=scs:706:48)\n    at gapi.loaded_0.e.xn._.xk.e.Er (https://apis.google.com/_/scs/abc-static/_/js/k=gapi.lb.en.nJpyt-fjzo8.O/m=client/rt=j/sv=1/d=1/ed=1/rs=AHpOoo9fTqXSCmwMDsu9FH68l5KSqr6MBg/cb=gapi.loaded_0?le=scs:175:25)\n    at Sk (https://apis.google.com/_/scs/abc-static/_/js/k=gapi.lb.en.nJpyt-fjzo8.O/m=client/rt=j/sv=1/d=1/ed=1/rs=AHpOoo9fTqXSCmwMDsu9FH68l5KSqr6MBg/cb=gapi.loaded_0?le=scs:178:59)\n    at Ok (https://apis.google.com/_/scs/abc-static/_/js/k=gapi.lb.en.nJpyt-fjzo8.O/m=client/rt=j/sv=1/d=1/ed=1/rs=AHpOoo9fTqXSCmwMDsu9FH68l5KSqr6MBg/cb=gapi.loaded_0?le=scs:177:368)\n    at Object.nz (https://apis.google.com/_/scs/abc-static/_/js/k=gapi.lb.en.nJpyt-fjzo8.O/m=client/rt=j/sv=1/d=1/ed=1/rs=AHpOoo9fTqXSCmwMDsu9FH68l5KSqr6MBg/cb=gapi.loaded_0?le=scs:177:238)\n    at jk (https://apis.google.com/_/scs/abc-static/_/js/k=gapi.lb.en.nJpyt-fjzo8.O/m=client/rt=j/sv=1/d=1/ed=1/rs=AHpOoo9fTqXSCmwMDsu9FH68l5KSqr6MBg/cb=gapi.loaded_0?le=scs:168:116)"

[[Prototype]]: gapi.loaded_0._.lb

constructor: ƒ (a)

name: "gapix.client.GapiClientError"

[[Prototype]]: Error
解决方案

针对Google Workspace项目出现的这个问题,按以下步骤排查:

  • 检查API密钥的限制设置
    确保你的API密钥没有设置过于严格的IP或HTTP referrer限制,尤其是开发环境中。如果限制了域名,确认当前运行代码的域名已添加到允许列表。另外,API密钥需要关联Drive API,在GCP控制台的API密钥页面,检查“API限制”是否选择了Drive API,而非“无限制”或其他无关API。

  • 确认OAuth客户端类型和配置
    Google Workspace项目的OAuth客户端需正确配置:

    1. 确保创建的是“Web应用”类型的OAuth客户端ID,不是桌面应用等其他类型。
    2. 检查“已授权的JavaScript来源”是否包含当前运行页面的域名,比如本地开发的http://localhost:8080或线上域名,格式要准确,不能带路径。
  • 验证Drive API是否已启用
    即便普通GCP项目能运行,Google Workspace项目可能未开启Drive API。进入GCP控制台,找到“API和服务”-“库”,搜索Drive API,确认状态为“已启用”。

  • 修复代码中的语法错误
    你提供的listFiles函数存在语法问题:

    response = await gapi.client.drive.files.list({
      'pageSize': 10,
      'k
    

    这里的'k是不完整的参数,需补充完整(比如'fields': 'files(id,name)'),或删除该残缺行。语法错误可能导致后续流程异常,先修复再测试。

  • 检查Google Workspace的权限设置
    如果应用需要访问Workspace域内的文件,可能需要Workspace管理员授权应用范围。登录Google Workspace管理控制台,检查“应用”-“API控件”-“未验证的应用”是否允许该OAuth客户端访问,或是否需要提交应用验证(面向外部用户时)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 22:14:53