.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日

相关文章

  • C#中的静态成员、静态方法、静态类介绍

    C#中的静态成员、静态方法、静态类是面向对象编程中常见的概念,具有重要的实用价值和理论意义。下面,就这些概念进行详细的讲解。 静态成员 静态成员是指在一个类中,使用static关键字修饰的成员。这种类型的成员是不需要实例化对象就可以访问的,因为它们是属于整个类的,而不是属于某个对象的。静态成员可以包括静态变量和静态方法两种类型。 静态变量 静态变量(也叫静态…

    C# 2023年5月31日
    00
  • C#用递归算法解决经典背包问题

    首先,需要明确背包问题的定义和思路: 经典背包问题(Knapsack Problem)指的是:给定一个背包,他的容量为C(Capacity)。现在有n种不同的物品,编号为0~n-1。其中每一件物品的重量为Wi,价值为Vi。问可以向这个背包中装入哪些物品,使得在满足背包最大容量的基础上,所有装入的物品的总价值最大。 解决该问题的思路主要有两种:贪心算法和动态规…

    C# 2023年6月1日
    00
  • 记一次 .NET 某手术室行为信息系统 内存泄露分析

    一:背景 1. 讲故事 昨天有位朋友找到我,说他的程序内存存在泄露导致系统特别卡,大地址也开了,让我帮忙看一下怎么回事?今天上午看了下dump,感觉挺有意思,在我的分析之旅中此类问题也蛮少见,算是完善一下体系吧。 二:WinDbg 分析 1. 到底是哪里的泄露 在.NET高级调试训练营中,我多次告诉学员们,在分析此类问题时一定要搞清楚是托管还是非托管的问题,…

    C# 2023年4月18日
    00
  • C#中@的用法总结

    下面我就来详细讲解 “C#中@的用法总结” 的攻略。 正文 1. @符号的含义 在C#中,@符号是一个特殊的字符,它具有特定的含义。在C#中,@符号表示一个字符串中的所有特殊字符都不需要进行转义。 在普通的字符串中,有些特殊字符需要进行转义,如\n代表换行符,\”代表双引号,\’代表单引号等。如果要在字符串中使用这些特殊字符,我们需要使用转义字符来表示这些特…

    C# 2023年6月6日
    00
  • c# WPF实现Windows资源管理器(附源码)

    以下是详细讲解“c# WPF实现Windows资源管理器(附源码)”的完整攻略: 一、前言 本文将介绍如何使用C#和WPF技术实现Windows资源管理器。本文的重点是WPF UI的构建与设计,以及与Windows API的交互。我们将会学到如何使用WPF技术创建一个更灵活和美观的资源管理器,并且可以使用Windows API打开Windows资源管理器并显…

    C# 2023年6月1日
    00
  • Jquery插件仿百度搜索关键字自动匹配功能

    Jquery插件仿百度搜索关键字自动匹配功能是一种常见的前端开发技术,可以提高用户体验。以下是使用Jquery插件实现仿百度搜索关键字自动匹配功能的完整攻略。 环境准备 在使用Jquery插件前,需要引入Jquery库和Jquery插件。可以使用以下命令来引入Jquery库和Jquery插件: <script src="https://cod…

    C# 2023年5月15日
    00
  • 为什么说C语言是永不过时的语言

    C语言是一种高效、灵活、可移植的编程语言,它在计算机科学领域中有着广泛的应用。虽然C语言已经存在了几十年,但它仍然是一种永不过时的语言。以下是几个原因: 1. C语言是一种高效的语言 C语言是一种高效的语言,它可以直接访问计算机的硬件资源,因此可以生成高效的代码。C语言的语法简单,易于学习和使用,同时也具有很高的灵活性。这使得C语言在嵌入式系统、操作系统、编…

    C# 2023年5月15日
    00
  • C#8 的模式匹配实现

    C#8 的模式匹配实现 模式匹配是 C#8 中新增的一项语言特性,它可以有效地增强代码的可读性和可维护性。本文将介绍 C#8 的模式匹配实现及其使用方法。 基本概念 模式匹配是一种根据值的类型和其他属性,以及预定义模式或用户指定的模式来确定该值是否与给定模式匹配的过程。C#8 中可以使用以下模式: 常量模式 类型模式 var 模式 指定类型模式 合并模式 常…

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