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

如何合并R Shiny中两个JS脚本并封装为通用函数?

解决方案:合并Shiny中的MSAL加载与OAuth脚本并封装通用函数

直接把两个tags$script合并成一个带src属性的标签是不行的——HTML规范不允许单个<script>标签同时包含src属性和内嵌脚本内容。但可以通过动态加载外部脚本的方式,把加载MSAL库和OAuth协商逻辑整合到一个内嵌脚本里,最终用一个tags$script实现,同时封装成通用函数供全组织复用。

通用函数实现

# 先确保依赖包已安装
if (!requireNamespace(c("shiny", "glue", "jsonlite"), quietly = TRUE)) {
  install.packages(c("shiny", "glue", "jsonlite"))
}

add_msal_oauth <- function(client_id, authority, scopes) {
  shiny::tags$script(shiny::HTML(glue::glue("
    // 动态加载MSAL库的工具函数
    function loadScript(src) {
      return new Promise((resolve, reject) => {
        const script = document.createElement('script');
        script.src = src;
        script.onload = () => resolve('MSAL库加载成功');
        script.onerror = () => reject('MSAL库加载失败');
        document.head.appendChild(script);
      });
    }

    // 加载完成后执行OAuth令牌协商逻辑
    loadScript('https://alcdn.msauth.net/browser/2.30.0/js/msal-browser.min.js')
      .then(() => {
        async function wrapperFunc() {
          const msalConfig = {
            auth: {
              clientId: '{client_id}',
              authority: '{authority}'
            }
          };
          const msalInstance = new msal.PublicClientApplication(msalConfig);
          const silentRequest = {
            scopes: {jsonlite::toJSON(scopes)}
          };
          
          const callLogin = async function(silentRequest, msalInstance) {
            try {
              const loginResponse = await msalInstance.loginPopup(silentRequest);
              return loginResponse;
            } catch (err) {
              console.error('OAuth登录失败:', err);
            }
          }
          
          // 修复原代码缺少await的问题,确保拿到实际响应
          const response = await callLogin(silentRequest, msalInstance);
          return response;
        }

        wrapperFunc().then(result => {
          if (result && result.accessToken) {
            Shiny.setInputValue('oauthToken', result.accessToken);
            console.log('获取到OAuth令牌:', result.accessToken);
          }
        });
      })
      .catch(err => console.error(err));
  ")))
}

使用方法

在任意Shiny报表的UI中,只需传入组织内统一的配置参数即可调用:

ui <- shiny::fluidPage(
  # 调用通用OAuth函数
  add_msal_oauth(
    client_id = "你的组织客户端ID",
    authority = "你的组织权限地址",
    scopes = c("api://你的API资源范围")
  ),
  # 其他UI组件示例
  shiny::verbatimTextOutput("token_display")
)

server <- function(input, output) {
  # 在server中获取传入的令牌
  output$token_display <- shiny::renderPrint({
    input$oauthToken
  })
}

shiny::shinyApp(ui, server)

关键说明

  • 动态加载逻辑通过Promise确保MSAL库完全加载后再执行OAuth代码,避免msal未定义的报错。
  • 函数参数化了所有可变配置项,不同报表只需传入对应参数,无需重复复制脚本代码。
  • 修复了原代码中callLogin调用缺少await的问题,保证拿到实际的登录响应而非Promise对象。
  • 增加了结果判断,避免登录失败时因result为undefined导致的报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 14:05:10