.Net动态生成controller遇到的坑

.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技术站

(0)
上一篇 2023年5月31日
下一篇 2023年5月31日

相关文章

  • 如何使用OPCache提升PHP的性能

    当你的PHP网站访问量增加时,PHP的解释、编译会成为瓶颈导致网站性能下降。为了提升网站性能,我们可以使用OPcache。OPcache是PHP 5.5.0版本自带的,可以加快PHP网站的运行。以下是使用OPcache提升PHP性能的攻略过程。 1. 安装OPcache 1.1 检测OPcache 要检测是否已经安装了OPcache,可以通过以下命令: ph…

    C# 2023年5月31日
    00
  • C#中this用法系列(二) 通过this修饰符为原始类型扩展方法

    关于C#中this用法系列(二) 通过this修饰符为原始类型扩展方法,这是一种 C# 的扩展方法(Extension Methods),可以为原生类型添加新的方法。本篇攻略将为您详细介绍如何使用 this 关键字来扩展原始类型的方法。 首先,在 C# 中,我们可以使用扩展方法来扩展原生类型的方法。在使用时,我们需要使用 this 关键字来指示这个方法是扩展…

    C# 2023年5月31日
    00
  • C#修改IIS站点framework版本号的方法

    C#修改IIS站点framework版本号的方法 前言 在ASP.NET应用程序中,我们通常需要设置正确的.NET Framework版本。如果您使用IIS作为Web服务器,您可能需要在站点或应用程序池级别设置.NET Framework版本。当您升级服务器上的.NET Framework时,您还需要修改站点或应用程序池的.NET Framework版本以确…

    C# 2023年6月3日
    00
  • c#中自定义Base16编码解码的方法示例

    针对你提供的主题,我会从以下几个方面进行详细讲解: Base16编码和解码是什么? C#内置的Base16编码和解码方法 自定义C#中的Base16编码和解码方法 示例说明 1. Base16编码和解码是什么? Base16(也称为hexadecimal)是一种将二进制数据编码为16进制字符串的方式,每个字节(8位)会转化为两个16进制数字(共16个)。 例…

    C# 2023年6月7日
    00
  • C#获取每个年,月,周的起始日期和结束日期的方法

    针对题目中所要求的“C#获取每个年,月,周的起始日期和结束日期的方法”的完整攻略,我将分为以下几个部分进行说明: 获取年份起始日期和结束日期; 获取月份起始日期和结束日期; 获取周起始日期和结束日期。 同时,我将提供两个示例来说明如何使用这些方法。 获取年份起始日期和结束日期 获取年份的起始日期和结束日期,可以通过DateTime.MinValue和Date…

    C# 2023年6月1日
    00
  • C#生成漂亮验证码完整代码类

    开发C#生成漂亮验证码的完整攻略可以包括如下几个方面: 1.添加NuGet包 首先要讲解的是,我们需要在项目中添加一个NuGet包来生成验证码。在Visual Studio中,可以通过依次单击“工具”>“NuGet包管理器”>“管理解决方案的NuGet包”来打开NuGet包管理器。然后搜索“ZKWeb.System.Drawing”包并安装它。 …

    C# 2023年5月31日
    00
  • 基于C#方法重载的总结详解

    基于C#方法重载的总结详解 在C#中,方法重载(Overloading)允许我们定义多个同名函数,只要它们的参数类型、数量或顺序不同。当我们调用函数时,编译器会根据提供的参数类型、数量和顺序来自动匹配函数。 方法重载的定义和规则 定义方法重载 方法重载需要在同一类中定义,其函数名称必须相同,形参列表不同。形参列表包括参数的数据类型、参数的个数以及参数的排列顺…

    C# 2023年5月15日
    00
  • C#创建数据库及导入sql脚本的方法

    下面我来详细讲解一下“C#创建数据库及导入sql脚本的方法”的完整攻略。 创建数据库 一、使用 Visual Studio 中的“管理控制台”创建数据库 打开 Visual Studio,点击“工具”>“连接到数据库”。 选择“Microsoft SQL Server”选项卡,输入连接数据库的信息(如数据库类型、服务器名称、身份验证、用户名、密码等)。…

    C# 2023年6月1日
    00
合作推广
合作推广
分享本页
返回顶部