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);