如何合并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
相关产品推荐
相关产品推荐

