【问题标题】:Best way to pass server side configuration data to client side Angular's $rootScope将服务器端配置数据传递到客户端 Angular 的 $rootScope 的最佳方式
【发布时间】:2016-08-22 20:52:57
【问题描述】:

所以我有一个带有服务器端身份验证的基本 Web 应用程序。然后是用于 UI 的前端 Angular 框架,由指令和 Angular 服务工厂驱动。

这个 Angular 框架需要了解当前页面请求的相关信息。

  • 用户是否经过身份验证
  • 谁是经过身份验证的用户
  • 他们的身份验证角色是什么

等等

这是每个请求的通用数据。

我正在尝试找出将服务器端信息提供给前端 Angular 框架的最佳方式。

起初我以为我只是把它放在一个 HTTP 标头中,但是如果不发出额外的 ajax 请求,然后将整个应用程序从初始 ajax 请求的链中建立起来,就无法在 javascript 中访问 HTTP 标头。

在初始页面请求的响应期间数据是已知的,因此我想提供该响应。

我知道我可以通过头部标签中的隐藏字段来做到这一点,但这对我来说似乎很老套,我最好避免这样做。

我唯一能想到的另一件事是使用全局操作过滤器,并尝试将 javascript 注入响应以初始化框架上下文。

例如我可以将手动角度引导代码移动为动态服务器端代码,将其注入响应末尾(最后一个要运行的脚本)并动态生成 angular.module(...).run 函数,然后注入我的全局页面以这种方式将数据导入 $rootScope。

编辑:我还可以在初始响应中使用 cookie,当脚本运行时该 cookie 会出现。

有没有更好的方法来解决这个问题,或者 MVC-6 中的新功能可以解决这个问题?

【问题讨论】:

  • 我会将所有这些信息存储在浏览器的本地存储中。使用$rootScope 时的一个问题是,如果用户导航到另一个页面,您将失去一切。
  • 是的,我忘记了本地存储。这似乎是一个很好的解决方案,因为本地存储只是读取客户端并且不会重新发送到服务器。这就是我想避免使用 cookie 的原因。
  • 此外,如果他们导航到另一个页面,它将从本地存储重新加载,因为导航到另一个页面将导致所有脚本重新加载,因为我没有使用角度路由。我还在生产模式下将整个应用程序压缩为 1 个 js 文件。
  • 嗯,我不认为我可以从服务器端设置本地存储。我知道我看不懂它,但我希望我可以在回复中添加它(只写),但似乎不可能。所以我回到动态 JS、隐藏字段或第二个 ajax 请求。
  • 您不能从服务器存储在浏览器的本地存储中。在本地存储上读取和写入是客户端的责任,在您的情况下是浏览器,即 JavaScript。

标签: c# angularjs asp.net-core asp.net-core-mvc


【解决方案1】:

临时答案,除非有人找到更好的方法。

我在 MVC 6 om ASP.Net Core 1.0 中创建了一个全局操作过滤器,该过滤器在任何操作执行之后运行,但在为所述操作处理视图之前运行。我用它来构建一个匿名对象,其中包含我想为前端 JS Api 提供服务的所有数据。

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ihq.moth.common.Filters
{
    public class ApiInjectFilter : IActionFilter
    {
        public void OnActionExecuted(ActionExecutedContext context)                
        {
            ///This Code is used to setup json data in a the ViewData Dictionary used by the client side script API, like knowing who the current user is, if they are authenticated, what permissions they have etc (of course the permissions are only used for cosmetic nice haves, permissions are double checked server side).
            var isAuthenticated = context.HttpContext.User != null && context.HttpContext.User.Identity != null && context.HttpContext.User.Identity.IsAuthenticated;
            var initData = new
            {
                isAuthenticated = isAuthenticated
                //TODO Add User Later                
            };

            var json = JsonConvert.SerializeObject(initData);
            var controller = context.Controller as Controller;

            controller.ViewData["APP_API_INIT_JS"] = json;
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {

        }
    }
}

那你需要在你的 Startup.cs 配置服务方法中进行配置...

   //Configure Services

   public void ConfigureServices(IServiceCollection services)
   {
        // Add framework services.
        services.AddMvc(options => {
            options.Filters.Add(typeof(common.Filters.ApiInjectFilter), 0);       //My Filter is added here with order 0 (first)
        });  
        .......
   }

在每个控制器操作请求(驱动我的整个应用程序、传统 MVC 获取等)时,此 APIFilter 将运行并将 javascript InitData(基本上是一个 javascript 对象(原始,而不是 json))添加到 ViewEngine 的 ViewData 字典。这将使它在任何视图的上下文中可用。

然后我创建了一个局部视图来为我的应用程序动态构建基本命名空间并从视图包中加载这个初始化 Javascript。

@{
    Layout = null;
    var initJsCode = this.ViewData["APP_API_INIT_JS"]; //this is the javascript generated in the Global Action Filter further above that runs after every action, and will contain Page Request Object data in javascript that every page shares (like authentication data and service end point urls etc.

}
<script>
    var moth = moth || (function () {
        return new function () {
            this.getInitData = function () {
                return @Html.Raw(initJsCode);
            }
        }
    })();
</script>

然后我修改了我的 _Layout.Cshtml 文件以使用这个局部视图:注意局部视图在两个地方被调用,因为一个环境块用于开发模式,另一个用于生产。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Meower Of The Hour</title>
    <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</head>
<body>
    <div id="mainWrapper" class="container-fluid">
        @{ Html.RenderPartial("Navbar");}
        @RenderBody()
    </div>
    <environment names="Development">
        <script src="~/js/jquery.js"></script>
        <script src="~/js/angular.js"></script>
        <script src="~/js/angular-messages.js"></script>
        <script src="~/js/bootstrap.js"></script>        
        @Html.Partial("_InitJSData") //load my namespace with the getInitData method in it
        <script asp-src-include="~/js/app.js"></script>
        <script asp-src-include="~/js/services/*.js"></script>
        <script asp-src-include="~/js/directives/*.js"></script>
        <script asp-src-include="~/js/controllers/*.js"></script>
        <script src="~/js/appLast.js"></script>
    </environment>
    <environment names="Production">
        @Html.Partial("_InitJSData") //load my namespace with the getInitData method in it
        <script src="~/js/scripts.min.js" asp-append-version="true"></script>
    </environment>
</body>
</html>

最后,将 Angular JS 代码放在 rootScope 对象上并删除 appJsonInitData 的全局变量(位于 app.js 中)并在 _InitJSData 部分视图之后加载;

(function(){
    var app = angular.module('moth', [])
    .run(['$rootScope', function ($rootScope) {
        $rootScope.appData = moth.getInitData(); //moth.getInitData is from the partial view above that was made dynamically and will be different for every request.
    }]);
})();

最后的想法

这是我想出的最好的方法,但它不使用 cookie 或在页面加载后需要 ajax 请求来获取它。请记住,当我完成后,getInitData 会返回更多数据。它将包含整个 API 中使用的基于上下文的信息,这些信息特定于每个请求。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-10
    • 2014-05-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多