.Net动态生成controller遇到的坑
简述
在使用.NET开发Web应用程序的过程中,我们常常需要动态地生成Controller。但是这个过程中会遇到一些坑,难以发现并解决。本文将详细讲解这些坑以及如何避免它们。
问题
1. 动态添加的controller无法被MVC框架识别
动态添加Controller后,通过浏览器访问应用程序时,MVC框架会报404错误,显示无法找到相应的Controller。
2. Action方法不能返回string类型
在动态添加的Controller中,如果Action方法返回字符串类型的数据,MVC框架将会报错。
解决办法
1. 使用MVC框架的默认路由
我们可以在动态添加Controller时,使用MVC框架默认的路由。这样,MVC框架就能正确地识别生成的Controller,进而正确地处理请求。具体实现如下:
public static void AddController(IServiceCollection services, Type type, string suffix = "Controller")
{
var controllerName = type.Name + suffix;
services.AddMvc(options =>
{
options.Conventions.Add(new RouteTokenTransformerConvention(new SlugifyParameterTransformer()));
}).ConfigureApplicationPartManager(apm =>
{
var part = new Microsoft.AspNetCore.Mvc.ApplicationParts.ControllerFeature();
apm.ApplicationParts.Add(part);
var controllerType = typeof(Controller);
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (var method in methods)
{
var actionName = method.Name;
var parameters = method.GetParameters();
var asmName = type.Assembly.GetName().Name;
var controllerTypeBuilder = asmBuilder.DefineController(controllerName, controllerType);
var actionTypeBuilder = controllerTypeBuilder.DefineAction(method.Name, MethodAttributes.Public);
actionTypeBuilder.SetCustomAttribute(new CustomAttributeBuilder(
typeof(HttpGetAttribute).GetConstructor(new Type[0]), new object[0]));
actionTypeBuilder.SetReturnType(method.ReturnType);
var parametersBuilder = new List<ParameterBuilder>();
for (var index = 0; index < parameters.Length; index++)
{
var parameter = parameters[index];
var parameterBuilder = actionTypeBuilder.DefineParameter(index + 1, ParameterAttributes.None, parameter.Name);
parameterBuilder.SetCustomAttribute(new CustomAttributeBuilder(
typeof(FromQueryAttribute).GetConstructor(new Type[0]), new object[0]));
parametersBuilder.Add(parameterBuilder);
}
var ilGenerator = actionTypeBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldstr, $"Hello, world");
ilGenerator.Emit(OpCodes.Ret);
part.Controllers.Add(controllerTypeBuilder.CreateTypeInfo().AsType());
}
});
}
2. 使用Action方法返回Object类型
为了避免MVC框架报错,我们可以在动态添加Controller时,将Action方法的返回类型设置为Object。这样,就可以正确地响应客户端的请求了。具体实现如下:
if (method.ReturnType == typeof(string))
{
var actionName = method.Name;
var parameters = method.GetParameters();
var asmName = type.Assembly.GetName().Name;
var controllerTypeBuilder = asmBuilder.DefineController(controllerName, controllerType);
var actionTypeBuilder = controllerTypeBuilder.DefineAction(method.Name, MethodAttributes.Public);
actionTypeBuilder.SetCustomAttribute(new CustomAttributeBuilder(
typeof(HttpGetAttribute).GetConstructor(new Type[0]), new object[0]));
actionTypeBuilder.SetReturnType(typeof(object));
var parametersBuilder = new List<ParameterBuilder>();
for (var index = 0; index < parameters.Length; index++)
{
var parameter = parameters[index];
var parameterBuilder = actionTypeBuilder.DefineParameter(index + 1, ParameterAttributes.None, parameter.Name);
parameterBuilder.SetCustomAttribute(new CustomAttributeBuilder(
typeof(FromQueryAttribute).GetConstructor(new Type[0]), new object[0]));
parametersBuilder.Add(parameterBuilder);
}
var ilGenerator = actionTypeBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldstr, $"Hello, world");
ilGenerator.Emit(OpCodes.Box, typeof(string));
ilGenerator.Emit(OpCodes.Ret);
part.Controllers.Add(controllerTypeBuilder.CreateTypeInfo().AsType());
}
示例说明
示例一:动态添加HelloWorldController
private readonly AssemblyBuilder asmBuilder;
public HelloWorldController(AssemblyBuilder asmBuilder)
{
this.asmBuilder = asmBuilder;
}
[HttpGet]
public string HelloWorld(string name)
{
return $"Hello, {name}!";
}
[HttpPost]
public string Post(string name)
{
return $"Hello, {name}!";
}
[HttpGet]
public object HelloWorld2(string name)
{
return $"Hello, {name}!";
}
[HttpPost]
public object Post2(string name)
{
return $"Hello, {name}!";
}
public void AddController()
{
var services = new ServiceCollection();
AddController(services, typeof(HelloWorldController));
var provider = services.BuildServiceProvider();
var serviceProvider = provider.GetService<IServiceProvider>();
var partManager = serviceProvider.GetRequiredService<ApplicationPartManager>();
var types = partManager.ApplicationParts
.OfType<AssemblyPart>()
.SelectMany(x => x.Types)
.ToArray();
partManager.ApplicationParts.Clear();
foreach (var type in types)
{
partManager.ApplicationParts.Add(new AssemblyPart(type.Assembly));
}
partManager.FeatureProviders.Add(new ControllerFeatureProvider(assemblies: null));
}
示例二:动态添加GreeterController
private readonly AssemblyBuilder asmBuilder;
public GreeterController(AssemblyBuilder asmBuilder)
{
this.asmBuilder = asmBuilder;
}
[HttpGet]
public string SayHello(string name)
{
return $"你好,{name}!";
}
[HttpPost]
public string Post(string name)
{
return $"你好,{name}!";
}
[HttpGet]
public object SayHello2(string name)
{
return $"你好,{name}!";
}
[HttpPost]
public object Post2(string name)
{
return $"你好,{name}!";
}
public void AddController()
{
var services = new ServiceCollection();
AddController(services, typeof(GreeterController));
var provider = services.BuildServiceProvider();
var serviceProvider = provider.GetService<IServiceProvider>();
var partManager = serviceProvider.GetRequiredService<ApplicationPartManager>();
var types = partManager.ApplicationParts
.OfType<AssemblyPart>()
.SelectMany(x => x.Types)
.ToArray();
partManager.ApplicationParts.Clear();
foreach (var type in types)
{
partManager.ApplicationParts.Add(new AssemblyPart(type.Assembly));
}
partManager.FeatureProviders.Add(new ControllerFeatureProvider(assemblies: null));
}
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:.Net动态生成controller遇到的坑 - Python技术站