.Net动态生成controller遇到的坑

yizhihongxing

.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# Winform 程序自动更新实现方法

    下面是一个标准的markdown格式文本,详细讲解“c# Winform 程序自动更新实现方法”的完整攻略,包含两条示例说明。 c# Winform程序自动更新实现方法 一、背景 随着软件应用的不断发展,越来越多的软件需要定期更新以保证其功能和性能的优化。在Winform程序中,自动更新是一项常见而且必要的功能,因此掌握Winform程序自动更新的实现方法对…

    C# 2023年6月6日
    00
  • PHP学习笔记 IIS7下安装配置php环境

    PHP是一种流行的服务器端脚本语言,可以用于创建动态网站和Web应用程序。以下是在IIS7下安装配置PHP环境的完整攻略。 环境准备 在使用PHP前,需要在Windows服务器上安装IIS7。可以使用以下命令来安装IIS7: Install-WindowsFeature Web-Server -IncludeManagementTools 安装配置PHP环境…

    C# 2023年5月15日
    00
  • .NET C#创建WebService服务简单实例

    .NET C#创建WebService服务简单实例 WebService是一种基于Web的分布式应用程序,它使用标准的Internet协议来通信。在.NET C#中,我们可以使用ASP.NET Web服务来创建WebService服务。本文将提供详细的“.NET C#创建WebService服务简单实例”的完整攻略,包括如何创建WebService服务、如何…

    C# 2023年5月15日
    00
  • 利用C#开发浏览器扩展的全过程记录

    让我为你详细地讲解“利用C#开发浏览器扩展的全过程记录”的完整攻略。 1. 确定浏览器扩展的功能 在开发浏览器扩展之前,我们需要确定扩展的功能,例如实现一个网站截图工具、广告拦截器等等。在本次攻略中,我们选择实现一个简单的网页计数器。 2. 创建C#类库项目 我们需要创建一个C#类库项目,用来编写代码实现所需的功能。在Visual Studio中创建一个类库…

    C# 2023年6月3日
    00
  • Unity C#执行bat脚本的操作

    下面是关于“Unity C#执行bat脚本的操作”的完整攻略: 概述 在Unity使用C#执行bat脚本,是一种在游戏开发过程中调用外部工具的常用方法,其中bat脚本可以实现一些与游戏开发有关的工具或者其他操作。下面将介绍如何在Unity中使用C#执行bat脚本以及提供两个示例说明。 步骤 步骤1:编写bat脚本 在项目目录下新建一个bat脚本文件,比如我们…

    C# 2023年6月3日
    00
  • C#实现Ping的方法小结

    C#实现Ping的方法小结 1. 简介 Ping是一种常用的测试网络连接的工具,可以测试网络延迟和连接状况,以及判断网络是否可用。在C#中也可以通过系统自带的Ping类来实现Ping功能。 2. Ping的基础使用 2.1 创建Ping实例 在使用Ping功能之前,需要创建一个Ping实例。使用如下代码可创建一个实例: Ping pingSender = n…

    C# 2023年6月7日
    00
  • ASP.NET Core应用JWT进行用户认证及Token的刷新方案

    下面我将为您详细讲解如何使用 ASP.NET Core 应用 JWT 进行用户认证及 Token 的刷新方案。 什么是 JWT? JWT (JSON Web Token) 是一个开放标准 (RFC 7519),用于在网络上传输声明 (Claims),通常用于身份认证。JWT 由三部分组成:头部 (Header)、载荷 (Payload) 和签名 (Signa…

    C# 2023年6月3日
    00
  • 干货来袭! C# 7.0 新特性(VS2017可用)

    干货来袭!C#7.0新特性(VS2017可用)的完整攻略 介绍 C# 7.0 是微软于2017年推出的一项新技术,它具有以下一些新特性: Tuples(元组) Deconstruction(解构) Out Var(输出变量) Local function(本地函数) Ref return and Ref locals(引用返回和引用本地变量) Pattern…

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