diff --git a/Dipper.Alioth/Extensions/StartHostExtension.cs b/Dipper.Alioth/Extensions/StartHostExtension.cs new file mode 100644 index 0000000..9955c67 --- /dev/null +++ b/Dipper.Alioth/Extensions/StartHostExtension.cs @@ -0,0 +1,27 @@ +using Dipper.Alioth.Web; + +namespace Dipper.Alioth.Extensions; + +public static class StartHostExtension +{ + /// + /// 添加监听服务文件夹组件 + /// + /// + /// + public static StarHost AddWatchServiceFolder(this StarHost host, string path) + { + if (!Directory.Exists(path)) + { + throw new DirectoryNotFoundException(path); + } + + // 这块地方应当是另一个类的 + // 且该类应当依赖注入(单例注入)至Host主机中 + var watcher = new FileSystemWatcher(path); + watcher.EnableRaisingEvents = true; + + + return host; + } +} \ No newline at end of file diff --git a/Dipper.Alioth/Web/ServiceConvention.cs b/Dipper.Alioth/Web/ServiceConvention.cs index 42273a7..96d2076 100644 --- a/Dipper.Alioth/Web/ServiceConvention.cs +++ b/Dipper.Alioth/Web/ServiceConvention.cs @@ -1,3 +1,6 @@ +using System.Text; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ActionConstraints; using Microsoft.AspNetCore.Mvc.ApplicationModels; namespace Dipper.Alioth.Web; @@ -6,6 +9,134 @@ public class ServiceConvention : IApplicationModelConvention { public void Apply(ApplicationModel application) { - throw new NotImplementedException(); + foreach (var controller in application.Controllers) + { + var type = controller.ControllerType; + if (typeof(IService).IsAssignableFrom(type)) + { + ConfigureServiceExplorer(controller); + ConfigureSelector(controller); + } + } } + + private static void ConfigureServiceExplorer(ControllerModel controller) + { + controller.ApiExplorer.IsVisible ??= true; + + foreach (var action in controller.Actions) + { + action.ApiExplorer.IsVisible ??= true; + } + } + + private void ConfigureSelector(ControllerModel controller) + { + RemoveEmptySelectors(controller.Selectors); + + if (controller.Selectors.Any(selector => selector.AttributeRouteModel != null)) + return; + + foreach (var action in controller.Actions) + { + ConfigureSelector(action); + } + } + + private static void RemoveEmptySelectors(IList selectors) + { + for (var i = selectors.Count - 1; i >= 0; i--) + { + var selector = selectors[i]; + if (selector.AttributeRouteModel == null && + selector.ActionConstraints.Count <= 0 && + selector.EndpointMetadata.Count <= 0) + { + selectors.Remove(selector); + } + } + } + + private void ConfigureSelector(ActionModel action) + { + RemoveEmptySelectors(action.Selectors); + + if (action.Selectors.Count <= 0) + AddServiceSelector(action); + else + NormalizeSelectorRoutes(action); + } + + private void AddServiceSelector(ActionModel action) + { + var template = new Microsoft.AspNetCore.Mvc.RouteAttribute(GetRouteTemplate(action)); + var selector = new SelectorModel + { + AttributeRouteModel = new AttributeRouteModel(template) + }; + selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { GetHttpMethod(action) })); + action.Selectors.Add(selector); + } + + private void NormalizeSelectorRoutes(ActionModel action) + { + foreach (var selector in action.Selectors) + { + if (selector.AttributeRouteModel == null) + { + var template = new Microsoft.AspNetCore.Mvc.RouteAttribute(GetRouteTemplate(action)); + selector.AttributeRouteModel = new AttributeRouteModel(template); + } + + if (selector.ActionConstraints.OfType().FirstOrDefault()?.HttpMethods?.FirstOrDefault() == null) + selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { GetHttpMethod(action) })); + } + } + + private static string GetRouteTemplate(ActionModel action) + { + if (action.Attributes is { Count: > 0}) + { + foreach (var item in action.Attributes) + { + if (item is RouteAttribute attribute) + { + // attribute.Path + return attribute.Name; + } + } + } + + var routeTemplate = new StringBuilder(); + //routeTemplate.Append("api"); + var names = action.Controller.ControllerType.Namespace.Split('.'); + if (names.Length > 2) + { + routeTemplate.Append(names[^2]); + } + + // Controller + var controllerName = action.Controller.ControllerName; + if (controllerName.EndsWith("Service")) + controllerName = controllerName[0..^7]; + + routeTemplate.Append($"/{controllerName}"); + + // Action + var actionName = action.ActionName; + if (actionName.EndsWith("Async")) + actionName = actionName[..^"Async".Length]; + + if (!string.IsNullOrEmpty(actionName)) + routeTemplate.Append($"/{actionName}"); + + return routeTemplate.ToString(); + } + + private static string GetHttpMethod(ActionModel action) + { + var actionName = action.ActionName; + return actionName.StartsWith("Get") ? "GET" : "POST"; + } + } \ No newline at end of file diff --git a/Dipper.Alioth/Web/StarHost.cs b/Dipper.Alioth/Web/StarHost.cs index 8c49395..2a4b78e 100644 --- a/Dipper.Alioth/Web/StarHost.cs +++ b/Dipper.Alioth/Web/StarHost.cs @@ -6,13 +6,23 @@ namespace Dipper.Alioth.Web; public class StarHost { - public static void Run(string[] args, Action action) + private StarHost() + { + + } + + public StarHost CreateHost() + { + return new StarHost(); + } + + public void Run(string[] args, Action action) { var app = Configure(args, action); app.Run(); } - public static Task RunAsync(string[] args, Action action) + public Task RunAsync(string[] args, Action action) { var app = Configure(args, action); @@ -20,7 +30,7 @@ public class StarHost } - private static WebApplication Configure(string[] args, Action action) + private WebApplication Configure(string[] args, Action action) { var builder = WebApplication.CreateBuilder(args);