Angular v15+如何在.NET Core MVC Razor页面中获取服务端渲染的JSON对象
在Angular 15+ SPA中访问.NET Core MVC Razor渲染的JSON对象问题
我正尝试在Angular v15+单页应用(SPA)中访问由.NET Core MVC Razor页面渲染的JSON对象,现有核心Razor页面代码如下:
@page @model Some.ServerSide.Model @addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" @{ ViewData["Title"] = "Angular SPA"; Layout = ""; } <script id="extension__config"> let json = JSON.parse(@Model.SomeServerSideObject.RenderedAtRuntime); /* 要在Angular中引用的对象 */ </script> <app-root></app-root> <!-- Runtime --> <script type="module"> @await Html.PartialAsync("ng/_runtime.cshtml") /* 包含Angular运行时JS的分部视图*/ </script> <!-- Polyfills --> <script type="module"> @await Html.PartialAsync("ng/_polyfills.cshtml") /* 包含转译后polyfills JS的分部视图 */ </script> <!-- Main --> <script type="module" > @await Html.PartialAsync("ng/_main.cshtml") /* 包含转译后main JS的分部视图 */ </script>
注:执行ng build时会转译Angular应用,并用Node对@字符双重转义,确保Razor能正常调用分部视图。目前MVC服务端运行该页面时,可正常加载简单的Angular Hello World! SPA。
但尝试两种方法获取extension__config中的JSON对象时均遇到问题:
- 问题1:创建
@Injectable窗口服务/提供者,能正常返回window对象(如访问window.location.hostname),但获取window.document.scripts.namedItem("extension__config").text时,TypeScript编译会抛出空值/未定义错误(TS2345),即便用可选链或if-else条件也无法消除。 - 问题2:创建
@Injectable服务/提供者,在ng serve运行时引用静态JSON文件,在VS2022调试或生产环境中调用Razor处理器(IActionResult OnGet)。该方法在ng serve中正常工作,但Angular应用转译后会抛出语法错误。
已查看微软推荐的Angular与.NET Core MVC协作方案,但这些方案较为复杂且不符合当前使用场景。认为Angular最终会转译为JS,本应能简单实现,但显然遗漏了某些要点,恳请提供建议或帮助。
解决方案建议
方案1:调整Razor页面的JSON注入方式,直接挂载到window全局对象
修改Razor页面中的脚本,避免解析脚本标签文本,直接将服务端JSON挂载到window上,同时解决Razor渲染时的引号冲突问题:
<script> // 使用Html.Raw确保JSON直接输出,无需额外解析 window.appConfig = @Html.Raw(Model.SomeServerSideObject.RenderedAtRuntime); </script>
Angular中可直接通过window.appConfig访问配置,无需额外处理。
方案2:扩展TypeScript全局类型,消除编译错误
如果坚持使用原脚本标签方式,需扩展window和Document的TypeScript类型,避免编译报错:
- 在Angular项目根目录创建
src/types/global.d.ts文件:
interface Window { // 若用方案1,可声明此属性;若用原方式,可忽略 appConfig?: Record<string, any>; } // 扩展Document的scripts类型,明确namedItem的返回值 interface Document { scripts: HTMLCollectionOf<HTMLScriptElement> & { namedItem(name: string): HTMLScriptElement | null; }; }
- 在Angular服务中安全获取配置:
@Injectable({ providedIn: 'root' }) export class ConfigService { getConfig(): Record<string, any> { const script = window.document.scripts.namedItem('extension__config'); if (!script?.text) { throw new Error('配置脚本未找到或内容为空'); } // 注意:若Razor已输出合法JSON,无需JSON.parse;若输出的是字符串化的JSON,才需要解析 return JSON.parse(script.text); } }
方案3:用APP_INITIALIZER确保应用启动前加载配置
利用Angular的APP_INITIALIZER在应用初始化前加载配置,避免组件未获取到配置的问题:
- 改造ConfigService:
@Injectable({ providedIn: 'root' }) export class ConfigService { private config: Record<string, any> | null = null; loadConfig(): Promise<Record<string, any>> { return new Promise((resolve, reject) => { const config = window.appConfig; if (config) { this.config = config; resolve(config); } else { reject(new Error('服务端配置未加载')); } }); } getConfig(): Record<string, any> { if (!this.config) { throw new Error('配置尚未加载'); } return this.config; } }
- 在应用配置中注册APP_INITIALIZER(以独立组件模式的
app.config.ts为例):
import { APP_INITIALIZER, ApplicationConfig } from '@angular/core'; import { ConfigService } from './config.service'; export const appConfig: ApplicationConfig = { providers: [ { provide: APP_INITIALIZER, useFactory: (configService: ConfigService) => () => configService.loadConfig(), deps: [ConfigService], multi: true } ] };
内容的提问来源于stack exchange,提问作者SWOmega
相关产品推荐
相关产品推荐

