在对象中找不到脚本回调函数

Couldn't find script callback function inside object

我使用 Google 中的 OAuth library 来设置与 Spotify 的连接。有一个问题。当 createService()authCallback()auth 对象的一部分时,引发错误:

Couldn't find script function: authCallback()

为什么回调函数在 auth 对象中不可见?

本例代码:

function doGet() {
    if (auth.hasAccess()) {
        main();
    } else {
        return auth.createFlow();
    }
}

const auth = (function () {
    const CLIENT_ID = '...';
    const CLIENT_SECRET = '...';

    const _service = createService();

    function createService() {
        return OAuth2.createService('spotify')
            .setAuthorizationBaseUrl('https://accounts.spotify.com/authorize')
            .setTokenUrl('https://accounts.spotify.com/api/token')
            .setClientId(CLIENT_ID)
            .setClientSecret(CLIENT_SECRET)
            .setCallbackFunction('authCallback') // set callback
            .setPropertyStore(PropertiesService.getUserProperties())
            .setScope('playlist-read-private playlist-modify-private playlist-modify-public user-library-read')
            .setParam('response_type', 'code')
            .setParam('redirect_uri', getRedirectUri());
    }

    function authCallback(request) {
        let isAuthorized = _service.handleCallback(request);
        if (isAuthorized) {
            return HtmlService.createHtmlOutput('Success! You can close this tab.');
        } else {
            return HtmlService.createHtmlOutput('Denied. You can close this tab');
        }
    }

    ...

    return {
        hasAccess: hasAccess,
        getAccessToken: getAccessToken,
        createFlow: createFlow,
    };
})();

但是如果在没有 auth 对象的情况下执行此操作,则不会出现错误和成功回调:

function createService() {
    return OAuth2.createService('spotify')
        .setCallbackFunction('authCallback')
        // ...
}

function authCallback(request) {
    // ...
}

我可以这样做,但是在 auth 对象中隐藏实现细节是没有意义的:

const auth = (function () {
    function createService() {
        return OAuth2.createService('spotify')
            .setCallbackFunction('authCallback')
            // ...
    }

    function authCallback(request) {
        // ...
    }

    return {
        // ...
        authCallback: authCallback,
    };
})();

function authCallback(request) {
    return auth.authCallback(request);
}

function doGet() {
    if (auth.hasAccess()) {
        main();
    } else {
        return auth.createFlow();
    }
}

有错误的完整代码

function doGet() {
    if (auth.hasAccess()) {
        main();
    } else {
        return auth.createFlow();
    }
}

const auth = (function () {
    const CLIENT_ID = '...';
    const CLIENT_SECRET = '...';

    const _service = createService();

    function createService() {
        return OAuth2.createService('spotify')
            .setAuthorizationBaseUrl('https://accounts.spotify.com/authorize')
            .setTokenUrl('https://accounts.spotify.com/api/token')
            .setClientId(CLIENT_ID)
            .setClientSecret(CLIENT_SECRET)
            .setCallbackFunction('authCallback')
            .setPropertyStore(PropertiesService.getUserProperties())
            .setScope('playlist-read-private playlist-modify-private playlist-modify-public user-library-read')
            .setParam('response_type', 'code')
            .setParam('redirect_uri', getRedirectUri());
    }

    function authCallback(request) {
        let isAuthorized = _service.handleCallback(request);
        if (isAuthorized) {
            return HtmlService.createHtmlOutput('Success! You can close this tab.');
        } else {
            return HtmlService.createHtmlOutput('Denied. You can close this tab');
        }
    }

    function getRedirectUri() {
        let scriptId = encodeURIComponent(ScriptApp.getScriptId());
        let template = 'https://script.google.com/macros/d/%s/usercallback';
        return Utilities.formatString(template, scriptId);
    }

    function hasAccess() {
        return _service.hasAccess();
    }

    function getAccessToken() {
        return _service.getAccessToken();
    }

    function createFlow() {
        let template = '<a href="%s" target="_blank">Authorize</a>';
        let html = Utilities.formatString(template, _service.getAuthorizationUrl());
        return HtmlService.createHtmlOutput(html);
    }

    return {
        hasAccess: hasAccess,
        getAccessToken: getAccessToken,
        createFlow: createFlow,
    };
})();

您传递给 setCallbackFunction() 的值实际上会传递给 StateTokenBuilder.withMethod() 方法,这不需要参数在全局范围内可用。但这意味着您需要将字符串 'auth.authCallback' 传递给它。简单地传递它 'authCallback' 是行不通的,因为在全局范围内没有具有该名称的函数。

那么这也意味着您需要在 return 语句中公开 authCallback 以便它在全局范围内作为 auth.authCallback.

可用
const auth = (function () {
    const CLIENT_ID = '...';
    const CLIENT_SECRET = '...';

    const _service = createService();

    function createService() {
        return OAuth2.createService('spotify')
            .setAuthorizationBaseUrl('https://accounts.spotify.com/authorize')
            .setTokenUrl('https://accounts.spotify.com/api/token')
            .setClientId(CLIENT_ID)
            .setClientSecret(CLIENT_SECRET)
            .setCallbackFunction('auth.authCallback') // Use correct method name
            .setPropertyStore(PropertiesService.getUserProperties())
            .setScope('playlist-read-private playlist-modify-private playlist-modify-public user-library-read')
            .setParam('response_type', 'code')
            .setParam('redirect_uri', getRedirectUri());
    }

    function authCallback(request) {
        let isAuthorized = _service.handleCallback(request);
        if (isAuthorized) {
            return HtmlService.createHtmlOutput('Success! You can close this tab.');
        } else {
            return HtmlService.createHtmlOutput('Denied. You can close this tab');
        }
    }

    function getRedirectUri() {
        let scriptId = encodeURIComponent(ScriptApp.getScriptId());
        let template = 'https://script.google.com/macros/d/%s/usercallback';
        return Utilities.formatString(template, scriptId);
    }

    function hasAccess() {
        return _service.hasAccess();
    }

    function getAccessToken() {
        return _service.getAccessToken();
    }

    function createFlow() {
        let template = '<a href="%s" target="_blank">Authorize</a>';
        let html = Utilities.formatString(template, _service.getAuthorizationUrl());
        return HtmlService.createHtmlOutput(html);
    }

    return {
        hasAccess: hasAccess,
        getAccessToken: getAccessToken,
        createFlow: createFlow,
        authCallback: authCallback // Expose the method
    };
})();

为了帮助阐明 authCallback() 的用途,请尝试将其重命名为 displayAuthSuccessOrFailure()。它所做的只是向最终用户显示成功或失败消息。这可能会改变您对其 exposure/encapsulation.

的看法