1、AbpFramework.WebApi》Scripting
service接口
using Abp; using Abp.Collections.Extensions; using Abp.Dependency; using Abp.Extensions; using Abp.Web.Api.ProxyScripting.Generators; using Abp.WebApi.Controllers.Dynamic; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Web; namespace AbpFramework.Api.Scripting { public class ScriptProxyVueManager : ISingletonDependency { private class ScriptInfo { public string Script { get; private set; } public ScriptInfo(string script) { Script = script; } } private readonly IDictionary<string, ScriptInfo> CachedScripts; private readonly DynamicApiControllerManager _dynamicApiControllerManager; public ScriptProxyVueManager(DynamicApiControllerManager dynamicApiControllerManager) { _dynamicApiControllerManager = dynamicApiControllerManager; CachedScripts = new Dictionary<string, ScriptInfo>(); } public string GetScript(string name) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("name is null or empty!", "name"); } string cacheKey = "Vue_" + name; lock (CachedScripts) { ScriptInfo cachedScript = CachedScripts.GetOrDefault(cacheKey); if (cachedScript == null) { DynamicApiControllerInfo dynamicController = _dynamicApiControllerManager.GetAll().FirstOrDefault((DynamicApiControllerInfo ci) => ci.ServiceName == name && ci.IsProxyScriptingEnabled); if (dynamicController == null) { throw new HttpException(404, "There is no such a service: " + cacheKey); } string script = CreateProxyGenerator(dynamicController, amdModule: true).Generate(); cachedScript = (CachedScripts[cacheKey] = new ScriptInfo(script)); } return cachedScript.Script; } } public string GetAllScript() { lock (CachedScripts) { string cacheKey = "Vue_all"; if (!CachedScripts.ContainsKey(cacheKey)) { StringBuilder script = new StringBuilder(); //script.AppendLine("import request from '@/utils/request'\r\n"); //script.AppendLine("import request from './utils/request'\r\n"); script.AppendLine("(function(){"); script.AppendLine(" var serviceAppNamespace = abp.utils.createNamespace(abp, 'services.app');"); foreach (DynamicApiControllerInfo dynamicController in from ci in _dynamicApiControllerManager.GetAll() where ci.IsProxyScriptingEnabled select ci) { IScriptProxyGenerator proxyGenerator = CreateProxyGenerator(dynamicController, amdModule: false); script.AppendLine(proxyGenerator.Generate()); script.AppendLine(); } //script.AppendLine("\r\n export default { app: abp.services.app }"); script.AppendLine("})();"); CachedScripts[cacheKey] = new ScriptInfo(script.ToString()); } return CachedScripts[cacheKey].Script; } } private static IScriptProxyGenerator CreateProxyGenerator(DynamicApiControllerInfo controllerInfo, bool amdModule) { return new VueProxyGenerator(controllerInfo, amdModule); //switch (type) //{ // case ProxyScriptType.JQuery: // return new VueProxyGenerator(controllerInfo, amdModule); // case ProxyScriptType.Angular: // return new AngularProxyGenerator(controllerInfo); // default: // throw new AbpException("Unknown ProxyScriptType: " + type.ToString()); //} } } internal class VueProxyGenerator : IScriptProxyGenerator { private readonly DynamicApiControllerInfo _controllerInfo; private readonly bool _defineAmdModule; public VueProxyGenerator(DynamicApiControllerInfo controllerInfo, bool defineAmdModule = true) { _controllerInfo = controllerInfo; _defineAmdModule = defineAmdModule; } public string Generate() { StringBuilder script = new StringBuilder(); script.AppendLine("(function(){"); script.AppendLine(); //script.AppendLine(" var serviceNamespace = abp.utils.createNamespace(abp, 'services." + _controllerInfo.ServiceName.Replace("/", ".") + "');"); //获取接口名称 string servicerName = Regex.Replace(_controllerInfo.ServiceInterfaceType.Name, "I(?<str>.*?)AppService", "${str}"); //接口名称首字母小写 var nameChars = servicerName.ToCharArray(); if (nameChars[0] >= 'A' && nameChars[0] <= 'Z') { nameChars[0] = Char.ToLower(nameChars[0]); } //script.AppendLine(" export const " + new string(nameChars) + " = {"); script.AppendLine(" serviceAppNamespace['" + new string(nameChars) + "']={"); script.AppendLine(); foreach (DynamicApiActionInfo methodInfo2 in _controllerInfo.Actions.Values) { AppendMethod(script, _controllerInfo, methodInfo2); script.AppendLine(); } if (_defineAmdModule) { script.AppendLine(" if(typeof define === 'function' && define.amd){"); script.AppendLine(" define(function (require, exports, module) {"); script.AppendLine(" return {"); int methodNo = 0; foreach (DynamicApiActionInfo methodInfo in _controllerInfo.Actions.Values) { script.AppendLine(" '" + methodInfo.ActionName.ToCamelCase() + "' : serviceNamespace" + ProxyScriptingJsFuncHelper.WrapWithBracketsOrWithDotPrefix(methodInfo.ActionName.ToCamelCase()) + ((methodNo++ < _controllerInfo.Actions.Count - 1) ? "," : "")); } script.AppendLine(" };"); script.AppendLine(" });"); script.AppendLine(" }"); } script.AppendLine(); script.AppendLine(" }"); script.AppendLine("})();"); return script.ToString(); } private static void AppendMethod(StringBuilder script, DynamicApiControllerInfo controllerInfo, DynamicApiActionInfo methodInfo) { VueActionScriptGenerator generator = new VueActionScriptGenerator(controllerInfo, methodInfo); script.AppendLine(generator.GenerateMethod()); } } internal class VueActionScriptGenerator { private readonly DynamicApiControllerInfo _controllerInfo; private readonly DynamicApiActionInfo _actionInfo; private const string JsMethodTemplate = " serviceNamespace{jsMethodName} = function({jsMethodParameterList}) {\r\n return axios.post($.extend({\r\n{ajaxCallParameters}\r\n }, ajaxParams));\r\n };"; public VueActionScriptGenerator(DynamicApiControllerInfo controllerInfo, DynamicApiActionInfo actionInfo) { _controllerInfo = controllerInfo; _actionInfo = actionInfo; } public virtual string GenerateMethod() { string jsMethodName = _actionInfo.ActionName.ToCamelCase(); //string jsMethodParameterList = ActionScriptingHelper.GenerateJsMethodParameterList(_actionInfo.Method, "ajaxParams"); //return " serviceNamespace{jsMethodName} = function({jsMethodParameterList}) {\r\n return axios.post($.extend({\r\n{ajaxCallParameters}\r\n }, ajaxParams));\r\n };" // .Replace("{jsMethodName}", ProxyScriptingJsFuncHelper.WrapWithBracketsOrWithDotPrefix(jsMethodName)) // .Replace("{jsMethodParameterList}", jsMethodParameterList) // .Replace("{ajaxCallParameters}", GenerateAjaxCallParameters()); string jsMethodParameterList = ActionScriptingHelper.GenerateJsMethodParameterList(_actionInfo.Method); return " {jsMethodName}:function({jsMethodParameterList}) {\r\n return request({\r\n url:{ajaxCallParameters}, \r\n method:'{verb}'\r\n {inputParams} \r\n})\r\n }," .Replace("{jsMethodName}", jsMethodName) .Replace("{jsMethodParameterList}", jsMethodParameterList) .Replace("{inputParams}", string.IsNullOrWhiteSpace(jsMethodParameterList) ? string.Empty : ", data: " + jsMethodParameterList) .Replace("{ajaxCallParameters}", GenerateAjaxCallParameters()) .Replace("{verb}", _actionInfo.Verb.ToString().ToLower()); } protected string GenerateAjaxCallParameters() { //StringBuilder script = new StringBuilder(); //script.AppendLine(" url: abp.appPath + '" + ActionScriptingHelper.GenerateUrlWithParameters(_controllerInfo, _actionInfo) + "',"); //script.AppendLine(" type: '" + _actionInfo.Verb.ToString().ToUpperInvariant() + "',"); //if (_actionInfo.Verb == HttpVerb.Get) //{ // script.Append(" data: " + ActionScriptingHelper.GenerateBody(_actionInfo)); //} //else //{ // script.Append(" data: JSON.stringify(" + ActionScriptingHelper.GenerateBody(_actionInfo) + ")"); //} //return script.ToString(); StringBuilder script = new StringBuilder(); script.AppendLine(" abp.appPath + '" + ActionScriptingHelper.GenerateUrlWithParameters(_controllerInfo, _actionInfo) + "'"); return script.ToString(); } } // localhost:6234/api/AbpServiceProxiesVue/GetAll?v=637230113351569822' // 最终要返回的脚本,每个 api 都是一个 export function getAll(data) 脚本块 //import request from '@/utils/request' //export user ={ // getAll: function (data) //{ // return request({ // url: '/vue-element-admin/article/create', // method: 'post', // data // }) //} //get: function (data) //{ // return request({ // url: '/vue-element-admin/article/create', // method: 'post', // data // }) //} //} internal static class ActionScriptingHelper { public static string GenerateUrlWithParameters(DynamicApiControllerInfo controllerInfo, DynamicApiActionInfo actionInfo) { string baseUrl = "api/services/" + controllerInfo.ServiceName + "/" + actionInfo.ActionName; ParameterInfo[] primitiveParameters = (from p in actionInfo.Method.GetParameters() where TypeHelper.IsPrimitiveExtendedIncludingNullable(p.ParameterType) select p).ToArray(); if (!primitiveParameters.Any()) { return baseUrl; } string qsBuilderParams = primitiveParameters.Select((ParameterInfo p) => "{ name: '" + p.Name.ToCamelCase() + "', value: " + p.Name.ToCamelCase() + " }").JoinAsString(", "); return baseUrl + "' + abp.utils.buildQueryString([" + qsBuilderParams + "]) + '"; } public static string GenerateJsMethodParameterList(MethodInfo methodInfo, string ajaxParametersName = "") { List<string> paramNames = (from prm in methodInfo.GetParameters() select prm.Name.ToCamelCase()).ToList(); if (!string.IsNullOrWhiteSpace(ajaxParametersName)) { paramNames.Add(ajaxParametersName); } return string.Join(", ", paramNames); } public static string GenerateBody(DynamicApiActionInfo actionInfo) { ParameterInfo[] parameters = (from p in actionInfo.Method.GetParameters() where !TypeHelper.IsPrimitiveExtendedIncludingNullable(p.ParameterType) select p).ToArray(); if (parameters.Length == 0) { return "{}"; } if (parameters.Length > 1) { throw new AbpException("Only one complex type allowed as argument to a web api controller action. But " + actionInfo.ActionName + " contains more than one!"); } return parameters[0].Name.ToCamelCase(); } } /// <summary> /// Some simple type-checking methods used internally. /// </summary> internal static class TypeHelper { public static bool IsFunc(object obj) { if (obj == null) { return false; } Type type = obj.GetType(); if (!type.GetTypeInfo().IsGenericType) { return false; } return type.GetGenericTypeDefinition() == typeof(Func<>); } public static bool IsFunc<TReturn>(object obj) { if (obj != null) { return obj.GetType() == typeof(Func<TReturn>); } return false; } public static bool IsPrimitiveExtendedIncludingNullable(Type type, bool includeEnums = false) { if (IsPrimitiveExtended(type, includeEnums)) { return true; } if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) { return IsPrimitiveExtended(type.GenericTypeArguments[0], includeEnums); } return false; } private static bool IsPrimitiveExtended(Type type, bool includeEnums) { if (type.GetTypeInfo().IsPrimitive) { return true; } if (includeEnums && type.GetTypeInfo().IsEnum) { return true; } if (!(type == typeof(string)) && !(type == typeof(decimal)) && !(type == typeof(DateTime)) && !(type == typeof(DateTimeOffset)) && !(type == typeof(TimeSpan))) { return type == typeof(Guid); } return true; } } internal interface IScriptProxyGenerator { string Generate(); } }
管理菜单
using Abp.Application.Navigation; using Abp.Dependency; using Abp.Json; using Abp.Runtime.Session; using Abp.Web.Http; using Newtonsoft.Json.Linq; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace AbpFramework.Api.Scripting { internal class VueNavigationScriptManager : IVueNavigationScriptManager, ITransientDependency { private readonly IUserNavigationManager _userNavigationManager; public IAbpSession AbpSession { get; set; } public VueNavigationScriptManager(IUserNavigationManager userNavigationManager) { _userNavigationManager = userNavigationManager; AbpSession = NullAbpSession.Instance; } public async Task<string> GetScriptAsync() { IReadOnlyList<UserMenu> readOnlyList = await _userNavigationManager.GetMenusAsync(AbpSession.ToUserIdentifier()); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("(function(define) {"); stringBuilder.AppendLine("define(['jquery'], function($) {"); stringBuilder.AppendLine(" return (function() {"); stringBuilder.AppendLine(" var abp = window.abp || {};"); stringBuilder.AppendLine(" abp.nav = {};"); stringBuilder.AppendLine(" abp.nav.menus = {"); for (int i = 0; i < readOnlyList.Count; i++) { AppendMenu(stringBuilder, readOnlyList[i]); if (readOnlyList.Count - 1 > i) { stringBuilder.Append(" , "); } } stringBuilder.AppendLine("};"); stringBuilder.AppendLine("return abp;"); stringBuilder.AppendLine(" })();"); stringBuilder.AppendLine("});"); stringBuilder.AppendLine("}(typeof define === 'function' && define.amd"); stringBuilder.AppendLine("? define"); stringBuilder.AppendLine(": function(deps, factory) {"); stringBuilder.AppendLine(" if (typeof module !== 'undefined' && module.exports)"); stringBuilder.AppendLine(" {"); stringBuilder.AppendLine(" module.exports = factory(require('jquery'));"); stringBuilder.AppendLine(" }"); stringBuilder.AppendLine(" else"); stringBuilder.AppendLine(" {"); stringBuilder.AppendLine(" window.abp = factory(window.jQuery);"); stringBuilder.AppendLine(" }"); stringBuilder.AppendLine(" })); "); return stringBuilder.ToString(); } private static void AppendMenu(StringBuilder script, UserMenu menu) { script.AppendLine(" '" + HttpEncode.JavaScriptStringEncode(menu.Name) + "': {"); script.AppendLine(" name: '" + HttpEncode.JavaScriptStringEncode(menu.Name) + "',"); if (menu.DisplayName != null) { script.AppendLine(" displayName: '" + HttpEncode.JavaScriptStringEncode(menu.DisplayName) + "',"); } if (menu.CustomData != null) { script.AppendLine(" customData: " + menu.CustomData.ToJsonString(camelCase: true) + ","); } script.Append(" items: "); if (menu.Items.Count <= 0) { script.AppendLine("[]"); } else { script.Append("["); for (int i = 0; i < menu.Items.Count; i++) { AppendMenuItem(16, script, menu.Items[i]); if (menu.Items.Count - 1 > i) { script.Append(" , "); } } script.AppendLine("]"); } script.AppendLine(" }"); } private static void AppendMenuItem(int indentLength, StringBuilder sb, UserMenuItem menuItem) { sb.AppendLine("{"); sb.AppendLine(new string(' ', indentLength + 4) + "name: '" + HttpEncode.JavaScriptStringEncode(menuItem.Name) + "',"); sb.AppendLine(new string(' ', indentLength + 4) + "order: " + menuItem.Order.ToString() + ","); if (!string.IsNullOrEmpty(menuItem.Icon)) { sb.AppendLine(new string(' ', indentLength + 4) + "icon: '" + HttpEncode.JavaScriptStringEncode(menuItem.Icon) + "',"); } if (!string.IsNullOrEmpty(menuItem.Url)) { sb.AppendLine(new string(' ', indentLength + 4) + "path: '" + HttpEncode.JavaScriptStringEncode(menuItem.Url) + "',"); } //if (menuItem.DisplayName != null) //{ // sb.AppendLine(new string(' ', indentLength + 4) + "title: '" + HttpEncode.JavaScriptStringEncode(menuItem.DisplayName) + "',"); //} if (menuItem.CustomData != null) { if (menuItem.CustomData.GetType().Name == typeof(JObject).Name) { JObject customData = (JObject)menuItem.CustomData; string component = customData["component"]?.Value<string>(); if (string.IsNullOrWhiteSpace(component)) { sb.AppendLine(new string(' ', indentLength + 4) + "component:'@/layout',"); } else { sb.AppendLine(new string(' ', indentLength + 4) + "component: " + component + ","); } string redirect = customData["redirect"]?.Value<string>(); if (!string.IsNullOrWhiteSpace(redirect)) { sb.AppendLine(new string(' ', indentLength + 4) + "redirect: '" + redirect + "',"); } //string activeMenu = customData["activeMenu"]?.Value<string>(); //if (!string.IsNullOrWhiteSpace(activeMenu)) //{ // sb.AppendLine(new string(' ', indentLength + 4) + "activeMenu: '" + activeMenu + "',"); //} //if (!string.IsNullOrWhiteSpace(meta)) if (customData["meta"] != null) { if (customData["meta"]["title"] != null) { customData["meta"]["title"] = HttpEncode.JavaScriptStringEncode(menuItem.DisplayName); } string meta = customData["meta"]?.ToJsonString(camelCase: true); sb.AppendLine(new string(' ', indentLength + 4) + "meta: " + meta + ","); } bool? alwaysShow = customData["alwaysShow"]?.Value<bool?>(); if (alwaysShow.HasValue) { sb.AppendLine(new string(' ', indentLength + 4) + "alwaysShow: " + alwaysShow.ToString().ToLower() + ","); } } else { sb.AppendLine(new string(' ', indentLength + 4) + "customData: " + menuItem.CustomData.ToJsonString(camelCase: true) + ","); } } if (menuItem.Target != null) { sb.AppendLine(new string(' ', indentLength + 4) + "target: '" + HttpEncode.JavaScriptStringEncode(menuItem.Target) + "',"); } sb.AppendLine(new string(' ', indentLength + 4) + "hidden: " + menuItem.IsVisible.ToString().ToLowerInvariant() + ","); if (menuItem.Items.Count > 0) { sb.Append(new string(' ', indentLength + 4) + "children: ["); for (int i = 0; i < menuItem.Items.Count; i++) { AppendMenuItem(24, sb, menuItem.Items[i]); if (menuItem.Items.Count - 1 > i) { sb.Append(" , "); } } sb.AppendLine("]"); } sb.Append(new string(' ', indentLength) + "}"); } } public interface IVueNavigationScriptManager { Task<string> GetScriptAsync(); } }
2、AbpFramework.WebApi》Api》Controllers
service接口api
using Abp.Auditing; using Abp.Web.Minifier; using Abp.Web.Models; using Abp.Web.Security.AntiForgery; using Abp.WebApi.Controllers; using Abp.WebApi.Controllers.Dynamic.Formatters; using AbpFramework.Api.Scripting; using System.Net; using System.Net.Http; using System.Net.Http.Headers; namespace AbpFramework.Api.Controllers { [DontWrapResult] [DisableAuditing] [DisableAbpAntiForgeryTokenValidation] public class AbpServiceProxiesVueController : AbpApiController { private readonly ScriptProxyVueManager _scriptProxyManager; private readonly IJavaScriptMinifier _javaScriptMinifier; public AbpServiceProxiesVueController(ScriptProxyVueManager scriptProxyManager, IJavaScriptMinifier javaScriptMinifier) { _scriptProxyManager = scriptProxyManager; _javaScriptMinifier = javaScriptMinifier; } /// <summary> /// Gets JavaScript proxy for given service name. /// </summary> /// <param name="name">Name of the service</param> /// <param name="minify">Minify the JavaScript Code</param> public HttpResponseMessage Get(string name, bool minify = false) { string script = _scriptProxyManager.GetScript(name); HttpResponseMessage httpResponseMessage = base.Request.CreateResponse(HttpStatusCode.OK, minify ? _javaScriptMinifier.Minify(script) : script, new PlainTextFormatter()); httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-javascript"); return httpResponseMessage; } /// <summary> /// Gets JavaScript proxy for all services. /// </summary> /// <param name="minify"></param> public HttpResponseMessage GetAll(bool minify = false) { string script = _scriptProxyManager.GetAllScript(); HttpResponseMessage httpResponseMessage = base.Request.CreateResponse(HttpStatusCode.OK, minify ? _javaScriptMinifier.Minify(script) : script, new PlainTextFormatter()); httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-javascript"); return httpResponseMessage; } } }
管理菜单api
using Abp.Auditing; using Abp.Extensions; using Abp.Web.Minifier; using Abp.Web.Models; using Abp.Web.Security.AntiForgery; using Abp.WebApi.Controllers; using Abp.WebApi.Controllers.Dynamic.Formatters; using AbpFramework.Api.Scripting; using System.Globalization; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; namespace AbpFramework.Api.Controllers { [DontWrapResult] [DisableAuditing] [DisableAbpAntiForgeryTokenValidation] public class AbpVueMenuScriptController : AbpApiController { private readonly IVueNavigationScriptManager _navigationScriptManager; private readonly IJavaScriptMinifier _javaScriptMinifier; public AbpVueMenuScriptController( IVueNavigationScriptManager navigationScriptManager, IJavaScriptMinifier javaScriptMinifier ) { _navigationScriptManager = navigationScriptManager; _javaScriptMinifier = javaScriptMinifier; } /// <summary> /// 获取左侧菜单 /// </summary> /// <param name="culture"></param> /// <param name="minify"></param> /// <returns></returns> [DisableAuditing] public async Task<HttpResponseMessage> GetNavMenusAsync(string culture = "", bool minify = false) { if (!culture.IsNullOrEmpty()) { Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture); } StringBuilder sb = new StringBuilder(); sb.AppendLine(); sb.AppendLine(await _navigationScriptManager.GetScriptAsync()); sb.AppendLine(); HttpResponseMessage httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK, minify ? _javaScriptMinifier.Minify(sb.ToString()) : sb.ToString(), new PlainTextFormatter()); httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-javascript"); return httpResponseMessage; } } }
跨站令牌
using Abp.Web.Security.AntiForgery; using Abp.WebApi.Controllers; using System.Net.Http; using System.Web.Http; namespace AbpFramework.Api.Controllers { public class AntiForgeryController : AbpApiController { private readonly IAbpAntiForgeryManager _antiForgeryManager; public AntiForgeryController(IAbpAntiForgeryManager antiForgeryManager) { _antiForgeryManager = antiForgeryManager; } //[ValidateAbpAntiForgeryToken]//启用防伪令牌 [DisableAbpAntiForgeryTokenValidation]//禁用防伪令牌,可以跨域提交请求 [HttpPost] public HttpResponseMessage GetTokenCookie() { var response = new HttpResponseMessage(); _antiForgeryManager.SetCookie(response.Headers); return response; } } }
3、AbpFramework.Web》Controllers》AccountController
[HttpPost] //[DisableAuditing] public JsonResult SignOut() { _authenticationManager.SignOut(); return Json(new AjaxResponse { TargetUrl = "/Login" }); }