临时答案,除非有人找到更好的方法。
我在 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 中使用的基于上下文的信息,这些信息特定于每个请求。