Skip to content

Commit

Permalink
Merge pull request #12 from j4587698/feat-mvc
Browse files Browse the repository at this point in the history
添加mvc
  • Loading branch information
j4587698 authored Dec 30, 2024
2 parents f130478 + 41aaf1d commit 6cfb6d4
Show file tree
Hide file tree
Showing 12 changed files with 565 additions and 2 deletions.
9 changes: 7 additions & 2 deletions .github/workflows/nuget.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v2
with:
dotnet-version: 6.0.x
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand All @@ -34,4 +34,9 @@ jobs:
PROJECT_FILE_PATH: Jx.Toolbox.HtmlTools/Jx.Toolbox.HtmlTools.csproj
VERSION_REGEX: ^\s*<PackageVersion>(.*)<\/PackageVersion>\s*$
NUGET_KEY: ${{secrets.NUGET_API_KEY}}

- name: Publish Jx.Toolbox.Mvc
uses: alirezanet/[email protected]
with:
PROJECT_FILE_PATH: Jx.Toolbox.HtmlTools/Jx.Toolbox.Mvc.csproj
VERSION_REGEX: ^\s*<PackageVersion>(.*)<\/PackageVersion>\s*$
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
43 changes: 43 additions & 0 deletions Jx.Toolbox.Mvc/Application.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace Jx.Toolbox.Mvc;

public class Application
{
public static IServiceProvider? ServiceProvider { get; internal set; }

[NotNull]
public static IWebHostEnvironment? WebHostEnvironment { get; internal set; }

[NotNull]
public static ConfigurationManager? Configuration { get; internal set; }

public static T? GetService<T>() where T: class
{
var service = GetRequiredService<T>();
if (service != null)
{
return service;
}
return ServiceProvider?.GetService<T>();
}

public static T? GetRequiredService<T>() where T : class
{
return ServiceProvider?.GetRequiredService<T>();
}

public static string? GetValue(string key)
{
return Configuration[key];
}

public static T? GetValue<T>(string key) where T: class
{
return Configuration.GetSection(key).Get<T>();
}
}
7 changes: 7 additions & 0 deletions Jx.Toolbox.Mvc/Attributes/ScopedAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Jx.Toolbox.Mvc.Attributes;

[AttributeUsage(AttributeTargets.Class)]
public class ScopedAttribute : Attribute
{

}
7 changes: 7 additions & 0 deletions Jx.Toolbox.Mvc/Attributes/SingletonAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Jx.Toolbox.Mvc.Attributes;

[AttributeUsage(AttributeTargets.Class)]
public class SingletonAttribute : Attribute
{

}
7 changes: 7 additions & 0 deletions Jx.Toolbox.Mvc/Attributes/TransientAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Jx.Toolbox.Mvc.Attributes;

[AttributeUsage(AttributeTargets.Class)]
public class TransientAttribute : Attribute
{

}
15 changes: 15 additions & 0 deletions Jx.Toolbox.Mvc/Attributes/ValueAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Jx.Toolbox.Mvc.Attributes;

[AttributeUsage(AttributeTargets.Property)]
public class ValueAttribute : Attribute
{
public ValueAttribute(string configPath)
{
ConfigPath = configPath;
}

/// <summary>
/// config文件中的路径,多层用:分隔,如:AppConfig:AppConfigOption:ConfigSearchFolder
/// </summary>
public string ConfigPath { get; set; }
}
8 changes: 8 additions & 0 deletions Jx.Toolbox.Mvc/DynamicControllerBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Mvc;

namespace Jx.Toolbox.Mvc;

public abstract class DynamicControllerBase: ControllerBase
{

}
148 changes: 148 additions & 0 deletions Jx.Toolbox.Mvc/Extensions/MvcExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
using System.Runtime.CompilerServices;
using Jx.Toolbox.Mvc.Options;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace Jx.Toolbox.Mvc.Extensions;

public static class MvcExtension
{
public static IMvcBuilder AddServiceController(this IServiceCollection collection,
bool enableDynamicController = true)
{
using var serviceProvider = collection.BuildServiceProvider();
var option = serviceProvider.GetService<IOptions<AppConfigOption>>()?.Value ?? new AppConfigOption();
var builder = collection.AddMvc(options =>
{
if (enableDynamicController)
{
options.Conventions.Add(new DynamicControllerFeatureProvider(option));
}
}).AddControllersAsServices();

return builder;
}

private class DynamicControllerFeatureProvider(AppConfigOption option) : IApplicationModelConvention
{

public void Apply(ApplicationModel application)
{
foreach (var controller in application.Controllers)
{
// 检查是否是ControllerBase的派生类
if (controller.ControllerType.IsAssignableTo(typeof(DynamicControllerBase)))
{
// 检查是否已经有RouteAttribute定义
var hasRouteAttribute = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);

if (!hasRouteAttribute)
{
var routeAttribute = new Microsoft.AspNetCore.Mvc.RouteAttribute(
$"{option.DynamicPrefix}{(option.DynamicPrefix?.EndsWith('/') == false ? "/" : "")}[controller]");
var selectors = controller.Selectors.First();
var nullableContext = selectors.EndpointMetadata
.FirstOrDefault(x => x is NullableContextAttribute);
if (nullableContext != null)
{
selectors.EndpointMetadata.Remove(nullableContext);
}
// 没有RouteAttribute,所以添加一个

selectors.AttributeRouteModel = new AttributeRouteModel(routeAttribute);

}

foreach (var action in controller.Actions)
{
// 检查动作是否已经有HTTP方法特性(HttpGet, HttpPost, 等等)
var hasHttpMethodAttribute = action.Selectors.Any(selector =>
selector.EndpointMetadata.OfType<HttpMethodAttribute>().Any());

// 如果没有HTTP方法特性,则添加一个默认的HttpGet特性
if (!hasHttpMethodAttribute)
{
var selectors = action.Selectors.First();
var nullableContext = selectors.EndpointMetadata
.FirstOrDefault(x => x is NullableContextAttribute);
if (nullableContext != null)
{
selectors.EndpointMetadata.Remove(nullableContext);
}

var prefix = option.GetPrefix.FirstOrDefault(x => action.ActionName.StartsWith(x));
if (prefix != null)
{
if (option.AutoRemoveDynamicPrefix)
{
action.ActionName = action.ActionName[prefix.Length..];
}

selectors.AttributeRouteModel =
new AttributeRouteModel(new Microsoft.AspNetCore.Mvc.RouteAttribute("[action]"));
selectors.EndpointMetadata.Add(new HttpGetAttribute("[action]"));
selectors.EndpointMetadata.Add(new HttpMethodMetadata(["GET"]));

selectors.ActionConstraints.Add(new HttpMethodActionConstraint(["GET"]));
continue;
}

prefix = option.PutPrefix.FirstOrDefault(x => action.ActionName.StartsWith(x));
if (prefix != null)
{
if (option.AutoRemoveDynamicPrefix)
{
action.ActionName = action.ActionName[prefix.Length..];
}

selectors.AttributeRouteModel =
new AttributeRouteModel(new Microsoft.AspNetCore.Mvc.RouteAttribute("[action]"));
selectors.EndpointMetadata.Add(new HttpPutAttribute("[action]"));
selectors.EndpointMetadata.Add(new HttpMethodMetadata(["PUT"]));

selectors.ActionConstraints.Add(new HttpMethodActionConstraint(["PUT"]));
continue;
}

prefix = option.DeletePrefix.FirstOrDefault(x => action.ActionName.StartsWith(x));
if (prefix != null)
{
if (option.AutoRemoveDynamicPrefix)
{
action.ActionName = action.ActionName[prefix.Length..];
}

selectors.AttributeRouteModel =
new AttributeRouteModel(new Microsoft.AspNetCore.Mvc.RouteAttribute("[action]"));
selectors.EndpointMetadata.Add(new HttpDeleteAttribute("[action]"));
selectors.EndpointMetadata.Add(new HttpMethodMetadata(["DELETE"]));

selectors.ActionConstraints.Add(new HttpMethodActionConstraint(["DELETE"]));
continue;
}

prefix = option.PostPrefix.FirstOrDefault(x => action.ActionName.StartsWith(x));
if (option.AutoRemoveDynamicPrefix && prefix != null)
{
action.ActionName = action.ActionName[prefix.Length..];
}

selectors.AttributeRouteModel =
new AttributeRouteModel(new Microsoft.AspNetCore.Mvc.RouteAttribute("[action]"));
selectors.EndpointMetadata.Add(new HttpPostAttribute("[action]"));
selectors.EndpointMetadata.Add(new HttpMethodMetadata(["POST"]));

selectors.ActionConstraints.Add(new HttpMethodActionConstraint(["POST"]));
}
}
}
}
}
}
}
Loading

0 comments on commit 6cfb6d4

Please sign in to comment.