.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# Add(Object):将对象添加到集合中

    C#中的Add(Object)方法是指在集合(比如ArrayList)的末尾添加一个对象到集合中。下面是Add(Object)方法的完整攻略: 1. 方法签名 Add(Object)方法的签名如下: public virtual int Add(object value); 此方法的返回值为添加完后集合的元素数目,也就是添加前集合元素数目加1。 2. 参数说…

    C# 2023年4月19日
    00
  • C# GetTypeCode():获取此实例的类型代码

    C#中的GetTypeCode()方法是返回值类型的枚举值,它指示指定对象的基础类型。 该方法的完整格式如下: public virtual TypeCode GetTypeCode (); 它是System.Object类型的一个实例方法,可以用于在运行时获取对象的类型信息。该方法返回一个System.TypeCode值,该值指示对象的类型。 下面是两个示…

    C# 2023年4月19日
    00
  • C#字符串使用密钥进行加解密

    接下来我将为你详细讲解C#字符串使用密钥进行加解密的完整攻略。 首先,我们需要了解几个基本的概念:加密、解密、密钥。在此之前,我们需要知道需要使用到的命名空间:System.Security.Cryptography。 加密和解密 加密是指将信息转换为密文的过程,解密是指将密文恢复成信息的过程。在这个过程中,需要使用特定的算法对信息进行加密和解密。我们在C#…

    C# 2023年6月8日
    00
  • C# List集合中获取重复值及集合运算详解

    C# List集合中获取重复值及集合运算详解 在 C# 中,List 是一种常用的类型,可以实现可变大小的数组。经常遇到需要获取 List 集合中的重复值,以及对多个 List 进行集合运算的情况。本文将详细讲解如何在 C# 中实现这些操作。 获取 List 集合中的重复值 在 List 中获取重复值比较常见,一个最简单的方法是借助 Linq 进行查询。具体…

    C# 2023年6月1日
    00
  • C# LINQ的基本使用方法示例

    关于C# LINQ的基本使用方法示例,以下是完整攻略: 什么是LINQ LINQ(Language Integrated Query,语言集成查询)是微软在.NET Framework 3.5中推出的一项新特性,它能够使得.NET语言(如C#)可以进行通用的查询操作,包括数据的筛选、排序、分组以及聚合等等,而且支持查询对象是非常丰富的,包含了各种数据集合、O…

    C# 2023年6月1日
    00
  • C#中使用UDP通信实例

    以下是使用C#编写UDP通信示例的完整攻略: 1. 确定通信协议 使用UDP通信的前提是确定使用的通信协议,通信协议包括IP协议和UDP协议。在使用UDP协议时,需要选择一个端口号。一般来说,端口号从1024开始,最大是65535。在选择端口号时,应该选择一个不被其他程序占用的端口号。 2. 创建UDP类 在C#中,可以使用UdpClient类来创建UDP通…

    C# 2023年6月6日
    00
  • ASP.NET MVC Webuploader实现上传功能

    ASP.NET MVC是一个基于ASP.NET框架的Web应用程序开发框架,它通过模型、视图和控制器的分离,实现了高内聚低耦合、易维护易扩展的设计。Webuploader是一个基于HTML5的前端文件上传插件,支持大文件分片上传、图片压缩、进度提示等功能。本文将介绍如何使用ASP.NET MVC和Webuploader实现文件上传功能。 1. 创建ASP.N…

    C# 2023年5月15日
    00
  • c# datetime 格式化大全

    当涉及到 c# 的 datetime 格式化时,需要了解一些预定义的格式字符串、定界符和格式说明符。 预定义格式字符串 d 使用短日期格式显示日期:“yyyy/MM/dd” D 使用长日期格式显示日期:“yyyy’年’M’月’d’日’” f 使用长日期格式和短时间格式显示日期时间:“yyyy’年’M’月’d’日’ H:mm” F 使用长日期格式和长时间格式显…

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