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

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类型,避免编译报错:

  1. 在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;
    };
}
  1. 在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在应用初始化前加载配置,避免组件未获取到配置的问题:

  1. 改造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;
    }
}
  1. 在应用配置中注册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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 10:25:28