您可以在 JavaScript 中使用 Microsoft Graph API 来获取列表项吗?

Can you use Microsoft Graph API in JavaScript to get list items?

我正在使用 MSAL 的示例并将它们转换为使用 MS Graph 来读取 SharePoint,但是当涉及到读取列表项时,我似乎遇到了权限问题。

为了确保我的语法正确,我将 Graph Explorer 与我的 AD 帐户一起使用,我能够读取列表项并确认 URI 是正确的。我还能够阅读并获取一系列列表。但是,一旦我尝试获取列表的列表项,就不会返回任何内容。

基本代码在这里https://docs.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-javascript-spa

这是我从示例中转换的代码。如果您更新变量并在 Azure 中注册,您应该能够 运行 针对您的 SPO 站点。

<!DOCTYPE html>
<html>
<head>
    <title>Quickstart for MSAL JS</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.3.4/bluebird.min.js"></script>
    <script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.0/js/msal.js"></script>
</head>
<body>
    <h2>Welcome to MSAL.js Quickstart</h2><br />
    <h4 id="WelcomeMessage"></h4>
    <button id="SignIn" onclick="signIn()">Sign In</button><br /><br />
    <button id="btnAllLists" onclick="GetWithEndPoint()">Get All Lists</button><br /><br />
    <button id="btnListItems" onclick="GetWithEndPoint()">Get List Items</button><br /><br />
    <button id="btnListItemsAllFields" onclick="GetWithEndPoint()">Get List Items All Fields</button><br /><br />
    <pre id="json"></pre>
    <script>
        var config = {
            portalname: "yourportalname",
            sitename: "yoursitename",
            listid: "guidofalist"
        }
        var msalConfig = {
            auth: {
                clientId: "azureclientguid",
                authority: "https://login.microsoftonline.com/yourportal.onmicrosoft.com"
            },
            cache: {
                cacheLocation: "localStorage",
                storeAuthStateInCookie: true
            }
        };
        var graphConfig = {
            graphMeEndpoint: "https://graph.microsoft.com/v1.0/me",
            spShowAllListsEp: "https://graph.microsoft.com/v1.0/sites/" + config.portalname + ".sharepoint.com:/sites/" + config.sitename + ":/lists",
            spShowListItemsEp: "https://graph.microsoft.com/v1.0/sites/" + config.portalname + ".sharepoint.com:/sites/" + config.sitename + ":/lists/" + config.listid + "/items",
            spShowListItemsAllFieldsEp: "https://graph.microsoft.com/v1.0/sites/" + config.portalname + ".sharepoint.com:/sites/" + config.sitename + ":/lists/" + config.listid + "/items?expand=fields",
        };
        // this can be used for login or token request, however in more complex situations this can have diverging options
        var requestObj = {
            scopes: ["user.read"]
        };
        var myMSALObj = new Msal.UserAgentApplication(msalConfig);

        // Register Callbacks for redirect flow
        myMSALObj.handleRedirectCallback(authRedirectCallBack);

        function callMSGraph(theUrl, accessToken, callback) {
            var xmlHttp = new XMLHttpRequest();
            xmlHttp.onreadystatechange = function () {
                if (this.readyState == 4 && this.status == 200)
                    callback(JSON.parse(this.responseText));
            }
            xmlHttp.open("GET", theUrl, true); // true for asynchronous
            xmlHttp.setRequestHeader('Authorization', 'Bearer ' + accessToken);
            xmlHttp.send();
        }

        function signIn() {
            myMSALObj.loginPopup(requestObj).then(function (loginResponse) {
                //Login Success
                showWelcomeMessage();
                acquireTokenPopupAndCallMSGraph();
            }).catch(function (error) {
                console.log(error);
            });
        }

        function acquireTokenPopupAndCallMSGraph() {
            //Always start with acquireTokenSilent to obtain a token in the signed in user from cache
            myMSALObj.acquireTokenSilent(requestObj).then(function (tokenResponse) {
                callMSGraph(graphConfig.graphMeEndpoint, tokenResponse.accessToken, graphAPICallback);
            }).catch(function (error) {
                console.log(error);
                // Upon acquireTokenSilent failure (due to consent or interaction or login required ONLY)
                // Call acquireTokenPopup(popup window)
                if (requiresInteraction(error.errorCode)) {
                    myMSALObj.acquireTokenPopup(requestObj).then(function (tokenResponse) {
                        callMSGraph(graphConfig.graphMeEndpoint, tokenResponse.accessToken, graphAPICallback);
                    }).catch(function (error) {
                        console.log(error);
                    });
                }
            });
        }

        function graphAPICallback(data) {
            document.getElementById("json").innerHTML = JSON.stringify(data, null, 2);
        }

        function showWelcomeMessage() {
            var divWelcome = document.getElementById('WelcomeMessage');
            divWelcome.innerHTML = 'Welcome ' + myMSALObj.getAccount().userName + "to Microsoft Graph API";
            var loginbutton = document.getElementById('SignIn');
            loginbutton.innerHTML = 'Sign Out';
            loginbutton.setAttribute('onclick', 'signOut();');
            var btn1 = document.getElementById('btnAllLists');
            btn1.setAttribute('onclick', "GetWithEndPoint('" + graphConfig.spShowAllListsEp + "');");
            var btn2 = document.getElementById('btnListItems');
            btn2.setAttribute('onclick', "GetWithEndPoint('" + graphConfig.spShowListItemsEp + "');");
            var btn3 = document.getElementById('btnListItemsAllFields');
            btn3.setAttribute('onclick', "GetWithEndPoint('" + graphConfig.spShowListItemsAllFieldsEp + "');");
        }

        //This function can be removed if you do not need to support IE
        function acquireTokenRedirectAndCallMSGraph() {
            //Always start with acquireTokenSilent to obtain a token in the signed in user from cache
            myMSALObj.acquireTokenSilent(requestObj).then(function (tokenResponse) {
                callMSGraph(graphConfig.graphMeEndpoint, tokenResponse.accessToken, graphAPICallback);
            }).catch(function (error) {
                console.log(error);
                // Upon acquireTokenSilent failure (due to consent or interaction or login required ONLY)
                // Call acquireTokenRedirect
                if (requiresInteraction(error.errorCode)) {
                    myMSALObj.acquireTokenRedirect(requestObj);
                }
            });
        }

        function authRedirectCallBack(error, response) {
            if (error) {
                console.log(error);
            }
            else {
                if (response.tokenType === "access_token") {
                    callMSGraph(graphConfig.graphEndpoint, response.accessToken, graphAPICallback);
                } else {
                    console.log("token type is:" + response.tokenType);
                }
            }
        }

        function requiresInteraction(errorCode) {
            if (!errorCode || !errorCode.length) {
                return false;
            }
            return errorCode === "consent_required" ||
                errorCode === "interaction_required" ||
                errorCode === "login_required";
        }
        function signOut() {
            myMSALObj.logout();
        }

        // Browser check variables
        var ua = window.navigator.userAgent;
        var msie = ua.indexOf('MSIE ');
        var msie11 = ua.indexOf('Trident/');
        var msedge = ua.indexOf('Edge/');
        var isIE = msie > 0 || msie11 > 0;
        var isEdge = msedge > 0;
        //If you support IE, our recommendation is that you sign-in using Redirect APIs
        //If you as a developer are testing using Edge InPrivate mode, please add "isEdge" to the if check
        // can change this to default an experience outside browser use
        var loginType = isIE ? "REDIRECT" : "POPUP";

        if (loginType === 'POPUP') {
            if (myMSALObj.getAccount()) {// avoid duplicate code execution on page load in case of iframe and popup window.
                showWelcomeMessage();
                acquireTokenPopupAndCallMSGraph();
            }
        }
        else if (loginType === 'REDIRECT') {
            document.getElementById("SignIn").onclick = function () {
                myMSALObj.loginRedirect(requestObj);
            };
            if (myMSALObj.getAccount() && !myMSALObj.isCallback(window.location.hash)) {// avoid duplicate code execution on page load in case of iframe and popup window.
                showWelcomeMessage();
                acquireTokenRedirectAndCallMSGraph();
            }
        } else {
            console.error('Please set a valid login type');
        }
    </script>

    <script>
        function GetWithEndPoint(endpointString) {
            myMSALObj.acquireTokenSilent(requestObj).then(function (tokenResponse) {
                callMSGraph(endpointString, tokenResponse.accessToken, graphAPICallback);
            }).catch(function (error) {
                console.log(error);
                if (requiresInteraction(error.errorCode)) {
                    myMSALObj.acquireTokenPopup(requestObj).then(function (tokenResponse) {
                        callMSGraph(endpointString, tokenResponse.accessToken, graphAPICallback);
                    }).catch(function (error) {
                        console.log(error);
                    });
                }
            });
        }
    </script>

</body>
</html>

单击 returns 列表项中的任一按钮都会抛出此消息,我的理解是权限。

    {
      "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(microsoft.graph.list)('myid')/items",
      "value": []
    }

我期望从 Graph Explorer 中得到相同的结果。但这表明我没有权限。我尝试了几个不同的列表,结果总是一样的。我可以获得所有列表的列表。但是尝试从列表中获取项目失败。

我们可以不使用 Graph API 和 JS 来获取列表项吗?

这是来自 Azure 的 Azure 委派权限,我认为这应该是我获取列表项所需的全部权限。

But this indicates I don't have permission

没错,空结果通常表示 Get Items endpoint 缺少以下权限之一(在您的情况下是委派权限):

  • Sites.Read.All - 阅读所有网站集中的项目
  • Sites.ReadWrite.All - 编辑或删除所有网站集中的项目