Вопрос:

Лучший способ передать данные конфигурации на стороне клиента клиентской стороне Angular $ rootScope

c# angularjs asp.net-core asp.net-core-mvc

726 просмотра

1 ответ

3999 Репутация автора

Итак, у меня есть простое веб-приложение с аутентификацией на стороне сервера. Кроме того, для пользовательского интерфейса используется внешняя угловая структура, управляемая директивами и фабриками угловых услуг.

Эта угловая структура должна знать кое-что о текущем запросе страницы.

  • Аутентифицирован ли пользователь
  • Кто является аутентифицированным пользователем
  • Каковы их роли аутентификации

И т.п.

Это общие данные по каждому запросу.

Я пытаюсь выяснить, как наилучшим образом предоставить эту информацию на стороне сервера для передней угловой структуры.

Сначала я подумал, что просто поместил бы его в заголовок HTTP, но вы не можете получить доступ к заголовкам HTTP в javascript, не сделав дополнительный запрос ajax, а затем основав все приложение на цепочке от этого первоначального запроса ajax.

Данные известны во время ответа на начальный запрос страницы, поэтому я хотел бы предоставить ему этот ответ.

Я знаю, что могу сделать это с помощью скрытого поля в метке головы, но мне это кажется довольно глупым, и в идеале я бы хотел уклониться от этого.

Единственное, о чем я могу думать, это использовать фильтр Global Action и попытаться внедрить javascript в ответ для инициализации контекста платформы.

Например, я мог бы переместить ручной угловой код начальной загрузки в динамический серверный код, который я вставил в конец ответа (последний запущенный скрипт), и динамически сгенерировать функцию angular.module (...). Run, а затем внедрить мой глобальный код. таким образом данные страницы помещаются в $ rootScope.

Изменить: я также мог бы использовать cookie в первоначальном ответе, который будет там, когда скрипт запускается.

Есть ли лучшие способы сделать это, или, возможно, новые функции в MVC-6 для решения этой проблемы?

Автор: Ryan Mann Источник Размещён: 22.08.2016 08:52

Ответы (1)


0 плюса

3999 Репутация автора

Временный ответ, если кто-то не найдет лучшего способа.

Я создал глобальный фильтр действий в 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 Method ...

   //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 (raw, не json)) в словарь ViewData ViewEngine. Это сделает его доступным в контексте любого просмотра.

Затем я создал частичное представление для динамического построения базового пространства имен для моего приложения и загрузки этого 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>

И наконец, угловой код 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.
    }]);
})();

Последние мысли

Это лучший способ, который я придумала для этого, который не использует куки или не требует ajax-запроса после загрузки страницы, чтобы получить его. Имейте в виду, что, когда я закончу, из getInitData вернется больше данных. Он будет содержать контекстную информацию, используемую по всему API, специфичную для каждого запроса.

Автор: Ryan Mann Размещён: 23.08.2016 03:28
Вопросы из категории :
32x32