diff --git a/Plus.AutoApi/IAutoApi.cs b/Plus.AutoApi.Core/IAutoApi.cs similarity index 59% rename from Plus.AutoApi/IAutoApi.cs rename to Plus.AutoApi.Core/IAutoApi.cs index 42300b0..47ddc45 100644 --- a/Plus.AutoApi/IAutoApi.cs +++ b/Plus.AutoApi.Core/IAutoApi.cs @@ -1,4 +1,4 @@ -namespace Plus.AutoApi +namespace Plus.AutoApi.Core { public interface IAutoApi { diff --git a/Plus.AutoApi.Core/Plus.AutoApi.Core.csproj b/Plus.AutoApi.Core/Plus.AutoApi.Core.csproj new file mode 100644 index 0000000..dbdcea4 --- /dev/null +++ b/Plus.AutoApi.Core/Plus.AutoApi.Core.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/Plus.AutoApi/Attributes/AutoApiAttribute.cs b/Plus.AutoApi/Attributes/AutoApiAttribute.cs index 6be609f..8f75718 100644 --- a/Plus.AutoApi/Attributes/AutoApiAttribute.cs +++ b/Plus.AutoApi/Attributes/AutoApiAttribute.cs @@ -9,5 +9,7 @@ public class AutoApiAttribute : Attribute public string AreaName { get; set; } public bool Disabled { get; set; } = false; + + public bool Inherited { get; set; } = true; } } \ No newline at end of file diff --git a/Plus.AutoApi/AutoApiControllerFeatureProvider.cs b/Plus.AutoApi/AutoApiControllerFeatureProvider.cs index 43d5be7..b553790 100644 --- a/Plus.AutoApi/AutoApiControllerFeatureProvider.cs +++ b/Plus.AutoApi/AutoApiControllerFeatureProvider.cs @@ -2,6 +2,7 @@ using Plus.AutoApi.Attributes; using Plus.AutoApi.Helpers; using System.Reflection; +using Plus.AutoApi.Core; namespace Plus.AutoApi { diff --git a/Plus.AutoApi/AutoApiConvention.cs b/Plus.AutoApi/AutoApiConvention.cs index 0ec9170..a023118 100644 --- a/Plus.AutoApi/AutoApiConvention.cs +++ b/Plus.AutoApi/AutoApiConvention.cs @@ -1,13 +1,13 @@ -using Microsoft.AspNetCore.Mvc; +using System; +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ActionConstraints; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ModelBinding; using Plus.AutoApi.Attributes; using Plus.AutoApi.Extensions; using Plus.AutoApi.Helpers; -using System; -using System.Linq; -using System.Reflection; namespace Plus.AutoApi { @@ -22,7 +22,7 @@ public void Apply(ApplicationModel application) if (typeof(IAutoApi).GetTypeInfo().IsAssignableFrom(type)) { - controller.ControllerName = controller.ControllerName.RemoveSuffix(PlusConsts.ControllerSuffixes.ToArray()); + controller.ControllerName = GetRestFulControllerName(controller.ControllerName); ConfigureArea(controller, attribute); @@ -43,7 +43,9 @@ public void Apply(ApplicationModel application) private void ConfigureArea(ControllerModel controller, AutoApiAttribute attribute) { if (attribute == null) + { throw new ArgumentException(nameof(attribute)); + } if (!controller.RouteValues.ContainsKey("area")) { @@ -60,35 +62,38 @@ private void ConfigureArea(ControllerModel controller, AutoApiAttribute attribut private void ConfigureAutoApi(ControllerModel controller, AutoApiAttribute attribute) { - ConfigureApiExplorer(controller); + ConfigureApiExplorer(controller, attribute); ConfigureSelector(controller, attribute); ConfigureParameters(controller); } - private void ConfigureApiExplorer(ControllerModel controller) + private void ConfigureApiExplorer(ControllerModel controller, AutoApiAttribute attribute) { if (string.IsNullOrEmpty(controller.ApiExplorer.GroupName)) { controller.ApiExplorer.GroupName = controller.ControllerName; } - if (controller.ApiExplorer.IsVisible == null) - { - controller.ApiExplorer.IsVisible = true; - } + controller.ApiExplorer.IsVisible ??= true; foreach (var action in controller.Actions) { - ConfigureApiExplorer(action); + ConfigureApiExplorer(action, attribute); } } - private void ConfigureApiExplorer(ActionModel action) + private void ConfigureApiExplorer(ActionModel action, AutoApiAttribute attribute) { - if (action.ApiExplorer.IsVisible == null) + if (!attribute.Inherited) { - action.ApiExplorer.IsVisible = true; + if (action.ActionMethod.DeclaringType != action.Controller.ControllerType) + { + action.ApiExplorer.IsVisible = false; + return; + } } + + action.ApiExplorer.IsVisible ??= true; } private void ConfigureSelector(ControllerModel controller, AutoApiAttribute attribute) @@ -165,15 +170,18 @@ private void NormalizeSelectorRoutes(string areaName, string controllerName, Act foreach (var selector in action.Selectors) { - selector.AttributeRouteModel = selector.AttributeRouteModel == null ? - CreateActionRouteModel(areaName, controllerName, action) : - AttributeRouteModel.CombineAttributeRouteModel(CreateActionRouteModel(areaName, controllerName, action), selector.AttributeRouteModel); + selector.AttributeRouteModel = selector.AttributeRouteModel == null + ? CreateActionRouteModel(areaName, controllerName, action) + : AttributeRouteModel.CombineAttributeRouteModel( + CreateActionRouteModel(areaName, controllerName, action), selector.AttributeRouteModel); } } private static string GetHttpVerb(ActionModel action) { - var getValueSuccess = PlusConsts.AssemblyAutoApiOptions.TryGetValue(action.Controller.ControllerType.Assembly, out AssemblyAutoApiOptions assemblyAutoApiOptions); + var getValueSuccess = + PlusConsts.AssemblyAutoApiOptions.TryGetValue(action.Controller.ControllerType.Assembly, + out var assemblyAutoApiOptions); if (getValueSuccess && !string.IsNullOrWhiteSpace(assemblyAutoApiOptions?.HttpVerb)) { @@ -182,19 +190,32 @@ private static string GetHttpVerb(ActionModel action) var verbKey = action.ActionName.GetPascalOrCamelCaseFirstWord().ToLower(); - return PlusConsts.HttpVerbs.ContainsKey(verbKey) ? PlusConsts.HttpVerbs[verbKey] : PlusConsts.DefaultHttpVerb; + return PlusConsts.HttpVerbs.ContainsKey(verbKey) + ? PlusConsts.HttpVerbs[verbKey] + : PlusConsts.DefaultHttpVerb; + } + + private string GetRestFulControllerName(string controllerName) + { + controllerName = controllerName.RemoveSuffix(PlusConsts.ControllerSuffixes.ToArray()); + var name = PlusConsts.GetRestFulControllerName?.Invoke(controllerName); + if (!string.IsNullOrWhiteSpace(name)) + { + return name; + } + + return controllerName; } private string GetRestFulActionName(string actionName) { + actionName = actionName.RemoveSuffix(PlusConsts.ActionSuffixes.ToArray()); var name = PlusConsts.GetRestFulActionName?.Invoke(actionName); if (name != null) { return name; } - actionName = actionName.RemoveSuffix(PlusConsts.ActionSuffixes.ToArray()); - var verbKey = actionName.GetPascalOrCamelCaseFirstWord().ToLower(); if (PlusConsts.HttpVerbs.ContainsKey(verbKey)) { @@ -202,15 +223,11 @@ private string GetRestFulActionName(string actionName) { return ""; } - else - { - return actionName.Substring(verbKey.Length); - } - } - else - { - return actionName; + + return actionName.Substring(verbKey.Length); } + + return actionName; } private AttributeRouteModel CreateActionRouteModel(string areaName, string controllerName, ActionModel action) @@ -223,7 +240,9 @@ private AttributeRouteModel CreateActionRouteModel(string areaName, string contr private static string GetApiPreFix(ActionModel action) { - var getValueSuccess = PlusConsts.AssemblyAutoApiOptions.TryGetValue(action.Controller.ControllerType.Assembly, out AssemblyAutoApiOptions assemblyAutoApiOptions); + var getValueSuccess = + PlusConsts.AssemblyAutoApiOptions.TryGetValue(action.Controller.ControllerType.Assembly, + out var assemblyAutoApiOptions); if (getValueSuccess && !string.IsNullOrWhiteSpace(assemblyAutoApiOptions?.ApiPrefix)) { @@ -236,20 +255,18 @@ private static string GetApiPreFix(ActionModel action) private void ConfigureParameters(ControllerModel controller) { foreach (var action in controller.Actions) + foreach (var para in action.Parameters) { - foreach (var para in action.Parameters) + if (para.BindingInfo != null) { - if (para.BindingInfo != null) - { - continue; - } + continue; + } - if (!TypeHelper.IsPrimitiveExtendedIncludingNullable(para.ParameterInfo.ParameterType)) + if (!TypeHelper.IsPrimitiveExtendedIncludingNullable(para.ParameterInfo.ParameterType)) + { + if (CanUseFormBodyBinding(action, para)) { - if (CanUseFormBodyBinding(action, para)) - { - para.BindingInfo = BindingInfo.GetBindingInfo(new[] { new FromBodyAttribute() }); - } + para.BindingInfo = BindingInfo.GetBindingInfo(new[] { new FromBodyAttribute() }); } } } @@ -257,7 +274,8 @@ private void ConfigureParameters(ControllerModel controller) private bool CanUseFormBodyBinding(ActionModel action, ParameterModel parameter) { - if (PlusConsts.FormBodyBindingIgnoredTypes.Any(t => t.IsAssignableFrom(parameter.ParameterInfo.ParameterType))) + if (PlusConsts.FormBodyBindingIgnoredTypes.Any(t => + t.IsAssignableFrom(parameter.ParameterInfo.ParameterType))) { return false; } diff --git a/Plus.AutoApi/AutoApiOptions.cs b/Plus.AutoApi/AutoApiOptions.cs index 4fca760..f266497 100644 --- a/Plus.AutoApi/AutoApiOptions.cs +++ b/Plus.AutoApi/AutoApiOptions.cs @@ -31,6 +31,8 @@ public AutoApiOptions() public Func GetRestFulActionName { get; set; } + public Func GetRestFulControllerName { get; set; } + public Dictionary AssemblyAutoApiOptions { get; } public void Valid() diff --git a/Plus.AutoApi/AutoApiServiceExtensions.cs b/Plus.AutoApi/AutoApiServiceExtensions.cs index bf14dbe..ac2fe5c 100644 --- a/Plus.AutoApi/AutoApiServiceExtensions.cs +++ b/Plus.AutoApi/AutoApiServiceExtensions.cs @@ -38,6 +38,7 @@ private static IServiceCollection AddAutoApi(this IServiceCollection services, A PlusConsts.ActionSuffixes = options.RemoveActionSuffixes; PlusConsts.FormBodyBindingIgnoredTypes = options.FormBodyBindingIgnoredTypes; PlusConsts.GetRestFulActionName = options.GetRestFulActionName; + PlusConsts.GetRestFulControllerName = options.GetRestFulControllerName; PlusConsts.AssemblyAutoApiOptions = options.AssemblyAutoApiOptions; var partManager = services.GetSingletonInstanceOrNull(); diff --git a/Plus.AutoApi/Plus.AutoApi.csproj b/Plus.AutoApi/Plus.AutoApi.csproj index 6e1f5fc..cc9feb4 100644 --- a/Plus.AutoApi/Plus.AutoApi.csproj +++ b/Plus.AutoApi/Plus.AutoApi.csproj @@ -1,24 +1,32 @@  - - netcoreapp3.1 - Plus.AutoApi - 阿星Plus - https://meowv.com - Plus.AutoApi 是一个可以用来动态生成 WebApi 不用写 Controller 的组件 - MIT - https://meowv.com - https://github.com/Meowv/Plus.AutoApi - https://avatars2.githubusercontent.com/u/13010050 - https://github.com/Meowv/Plus.AutoApi - git - plus;autoapi;plus.autoapi; - .NET Core 开发工具包 - 0.1.0 - + + netcoreapp3.1 + Plus.AutoApi + 阿星Plus + https://meowv.com + Plus.AutoApi 是一个可以用来动态生成 WebApi 不用写 Controller 的组件 + MIT + https://meowv.com + https://github.com/Meowv/Plus.AutoApi + https://avatars2.githubusercontent.com/u/13010050 + https://github.com/Meowv/Plus.AutoApi + git + plus;autoapi;plus.autoapi; + .NET Core 开发工具包 + 0.1.0 + - - - + + + + + + + + + + + \ No newline at end of file diff --git a/Plus.AutoApi/PlusConsts.cs b/Plus.AutoApi/PlusConsts.cs index 0b72984..908e23a 100644 --- a/Plus.AutoApi/PlusConsts.cs +++ b/Plus.AutoApi/PlusConsts.cs @@ -44,6 +44,8 @@ static PlusConsts() public static Func GetRestFulActionName { get; set; } + public static Func GetRestFulControllerName { get; set; } + public static Dictionary AssemblyAutoApiOptions { get; set; } } } \ No newline at end of file diff --git a/Plus.AutoApi/Properties/launchSettings.json b/Plus.AutoApi/Properties/launchSettings.json new file mode 100644 index 0000000..ac9bf13 --- /dev/null +++ b/Plus.AutoApi/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Plus.AutoApi": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:64869" + } + } +} \ No newline at end of file diff --git a/Plus.AutoApiGenerator/Plus.AutoApiGenerator.csproj b/Plus.AutoApiGenerator/Plus.AutoApiGenerator.csproj new file mode 100644 index 0000000..9d0704d --- /dev/null +++ b/Plus.AutoApiGenerator/Plus.AutoApiGenerator.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.0 + false + false + true + + + + + + + + + + diff --git a/Plus.AutoApiGenerator/ServiceGenerator.cs b/Plus.AutoApiGenerator/ServiceGenerator.cs new file mode 100644 index 0000000..449bf45 --- /dev/null +++ b/Plus.AutoApiGenerator/ServiceGenerator.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Text; +using Plus.AutoApi; +using Plus.AutoApi.Core; + +namespace Plus.AutoApiGenerator +{ + [Generator] + public class ServiceGenerator : ISourceGenerator + { + private const string NATIVE_SERVICES_TXT_ADDITIONAL_FILE_NAME = "Services.txt"; + private static readonly char[] ZeroWhiteSpace = new char[] + { + '\uFEFF', // ZERO WIDTH NO-BREAK SPACE (U+FEFF) + '\u200B', // ZERO WIDTH SPACE (U+200B) + }; + public void Initialize(GeneratorInitializationContext context) + { + + } + + public void Execute(GeneratorExecutionContext context) + { + Debugger.Launch(); + var nativeMethodsTxtFiles = context.AdditionalFiles + .Where(af => string.Equals(Path.GetFileName(af.Path), NATIVE_SERVICES_TXT_ADDITIONAL_FILE_NAME, StringComparison.OrdinalIgnoreCase)).ToList(); + if (!nativeMethodsTxtFiles.Any()) + { + return; + } + + var parseOptions = (CSharpParseOptions)context.ParseOptions; + + foreach (AdditionalText nativeMethodsTxtFile in nativeMethodsTxtFiles) + { + var nativeMethodsTxt = nativeMethodsTxtFile.GetText(context.CancellationToken); + if (nativeMethodsTxt is null) + { + return; + } + + foreach (TextLine line in nativeMethodsTxt.Lines) + { + context.CancellationToken.ThrowIfCancellationRequested(); + string name = line.ToString(); + if (string.IsNullOrWhiteSpace(name) || name.StartsWith("//", StringComparison.InvariantCulture)) + { + continue; + } + + name = name.Trim().Trim(ZeroWhiteSpace); + var location = Location.Create(nativeMethodsTxtFile.Path, line.Span, nativeMethodsTxt.Lines.GetLinePositionSpan(line.Span)); + + context.AddSource(name, $"public class {name}{{}}"); + } + } + } + } +}