搜索

查看: 3037|回复: 11

[ASP.NET] .NET Core Web APi类库内嵌运行的方法

[复制链接]
发表于 2023-5-4 11:30:27 | 显示全部楼层 |阅读模式
Editor 2023-5-4 11:30:27 3037 11 看全部
目录
  • 话题
  • 内嵌运行.NET Core Web APi
  • 总结
    话题
    我们知道在.NET Framework中可以嵌入运行Web APi,那么在.NET Core(.NET 6+称之为.NET)中如何内嵌运行Web Api呢,在实际项目中这种场景非常常见,那么我们本节以.NET 6.0作为演示示例一起来瞅瞅

    内嵌运行.NET Core Web APi
    接下来我们通过控制台作为主程序来启动Web APi,首先我们创建名为EmbedWebApi的控制台程序,然后创建Embed.WebApi类库运行Web APi,我们在此Web APi中创建如下接口,并实现相关方法来运行Web APi
    public class InitTest : IInitTest
    {
        public void Init()
        {
            var builder = WebApplication.CreateBuilder();
            builder.Services.AddControllers();
            var app = builder.Build();
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapDefaultControllerRoute();
            });
            app.Run();
        }
    }
    public interface IInitTest
    {
        void Init();
    }
    通过写接口并在对应方法中运行Web APi主要是达到在控制中调用该接口进行模拟实现,这里需要注意一点的是,因为我们创建的Web APi是类库,要想使用Web里面的Api等等,直接在项目文件中添加如下一行以表明我们要引用框架,这样一来框架里面所包含的APi等等版本都一致统一,而不是通过NuGet一一下载,这是错误的做法
    [I]
       
    [/I]
    接下来我们在该类库中按照规范创建Controllers文件夹,并创建测试控制器,如下
    using Microsoft.AspNetCore.Mvc;
    namespace Embed.WebApi.Controllers
    {
        [ApiController]
        [Route("api/[controller]/[action]")]
        public class TestController : ControllerBase
        {
            [HttpGet]
            public IActionResult Test()
            {
                return Ok("Hello World");
            }
        }
    }
    最后我们在控制台程序中注册上述接口并调用初始化方法,如下:
    internal class Program
    {
        static void Main(string[] args)
        {
            var services = new ServiceCollection();
            
            services.AddTransient[I]();
            var serviceProvider = services.BuildServiceProvider();
            var initTest = serviceProvider.GetRequiredService[I]();
            initTest.Init();
            Console.Read();
        }
    }

    202209231413441.png

    202209231413441.png


    芜湖,我们通过Postman模拟调用测试接口,结果惊呆了,404了~~~

    202209231413442.png

    202209231413442.png


    当我们将类库中的控制器移动到控制台中,此时请求测试接口并成功返回对世界的问候,这是什么原因呢? 不难猜测可知,默认WebAPi控制器的激活以作为入口的主程序集进行查找激活。虽然这样看似解决了问题,假设调用嵌入运行的主程序是底层已经封装好的基础设施,那么岂不是遭到了代码入侵,所以我们就想在运行的Web APi类库里面去激活,此时我们想到将类库作为Web APi应用程序一部分应用手动加载并激活,在初始化方法里面修改为如下即可请求测试接口成功
    public class InitTest : IInitTest
    {
        private static readonly string AssemblyName = typeof(InitTest).Assembly.GetName().Name;
        public void Init()
        {
            var builder = WebApplication.CreateBuilder();
            builder.Services.AddControllers()
                .AddApplicationPart(Assembly.Load(new AssemblyName(AssemblyName)));
            var app = builder.Build();
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapDefaultControllerRoute();
            });
            app.Run();
        }
    }
    上述直接在运行Web APi类库中添加控制器激活,这种场景完全限定于底层主入口已封装好,所以只能采用这种方式,若是主入口我们自己可控制,当然还有另外一种方式,来,我们瞧瞧截取的关键性源码
    ///
    /// Populates the given  using the list of
    /// s configured on the
    /// .
    ///
    /// The type of the feature.
    /// The feature instance to populate.
    public void PopulateFeature(TFeature feature)
    {
        if (feature == null)
        {
            throw new ArgumentNullException(nameof(feature));
        }
        foreach (var provider in FeatureProviders.OfType[I]>())
        {
            provider.PopulateFeature(ApplicationParts, feature);
        }
    }
    internal void PopulateDefaultParts(string entryAssemblyName)
    {
        var assemblies = GetApplicationPartAssemblies(entryAssemblyName);
        var seenAssemblies = new HashSet();
        foreach (var assembly in assemblies)
        {
            if (!seenAssemblies.Add(assembly))
            {
                // "assemblies" may contain duplicate values, but we want unique ApplicationPart instances.
                // Note that we prefer using a HashSet over Distinct since the latter isn't
                // guaranteed to preserve the original ordering.
                continue;
            }
            var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly);
            foreach (var applicationPart in partFactory.GetApplicationParts(assembly))
            {
                ApplicationParts.Add(applicationPart);
            }
        }
    }
    private static IEnumerable GetApplicationPartAssemblies(string entryAssemblyName)
    {
        var entryAssembly = Assembly.Load(new AssemblyName(entryAssemblyName));
        // Use ApplicationPartAttribute to get the closure of direct or transitive dependencies
        // that reference MVC.
        var assembliesFromAttributes = entryAssembly.GetCustomAttributes()
            .Select(name => Assembly.Load(name.AssemblyName))
            .OrderBy(assembly => assembly.FullName, StringComparer.Ordinal)
            .SelectMany(GetAssemblyClosure);
        // The SDK will not include the entry assembly as an application part. We'll explicitly list it
        // and have it appear before all other assemblies \ ApplicationParts.
        return GetAssemblyClosure(entryAssembly)
            .Concat(assembliesFromAttributes);
    }
    private static IEnumerable GetAssemblyClosure(Assembly assembly)
    {
        yield return assembly;
        var relatedAssemblies = RelatedAssemblyAttribute.GetRelatedAssemblies(assembly, throwOnError: false)
            .OrderBy(assembly => assembly.FullName, StringComparer.Ordinal);
        foreach (var relatedAssembly in relatedAssemblies)
        {
            yield return relatedAssembly;
        }
    }
    从上述源码可知,通过主入口程序集还会加载引用的程序集去查找并激活相关特性(比如控制器),当然前提是实现ApplicationPartAttribute特性,此特性必须在主入口程序集里定义,定义在程序集上,所以我们只需一行代码即可搞定,我们在控制台主入口命名空间顶部添加特性,引入Web APi类库程序集作为应用程序的一部分,如下:
    [assembly: ApplicationPart("Embed.WebApi")]

    202209231413453.png

    202209231413453.png


    那么接下来问题又来了,要是需要运行多个Web APi我们又当如何呢?按照上述方式一一添加未尝不可,我们也可以通过MSBuild任务来进行构建将相关特性自动添加到主入口程序集描述信息里面去,例如:
    [I]
       
            Embed.WebApi
       
    [/I]
    有的童鞋就问了,这不写死了么,那还不如通过添加特性的方式去处理,请注意这里只是使用示例,实际情况下,我们可将多个Web APi放在同一解决方案下,然后在此解决方案下创建可构建任务的.targets文件,并在主项目文件里引入,将程序集名称作为变量引入,剩下事情自行统一处理,若不清楚怎么搞,就在代码中使用特性方式也未尝不可,例如如下:
    [I]
       
            $(AssemblyName)
       
    [/I]

    202209231413454.png

    202209231413454.png


    总结
    本节我们重点讨论如何内嵌运行.NET Core Web APi类库,同时介绍了两种激活比如控制器特性方案, 希望对您有所帮助,谢谢,我们下节再会
    到此这篇关于.NET Core Web APi类库内嵌运行的方法的文章就介绍到这了,更多相关.NET Core Web APi内容请搜索知鸟论坛以前的文章或继续浏览下面的相关文章希望大家以后多多支持知鸟论坛
  • 回复

    使用道具 举报

    发表于 2023-6-28 17:06:24 | 显示全部楼层
    知足常乐77 2023-6-28 17:06:24 看全部
    感谢楼主的无私分享!要想知鸟论坛好 就靠你我他
    回复

    使用道具 举报

    发表于 2023-6-29 08:05:51 | 显示全部楼层
    123456825 2023-6-29 08:05:51 看全部
    楼主,我太崇拜你了!我想我是一天也不能离开知鸟论坛
    回复

    使用道具 举报

    发表于 2023-6-29 14:09:03 | 显示全部楼层
    风来时狂放 2023-6-29 14:09:03 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    回复

    使用道具 举报

    发表于 2023-6-29 14:38:58 | 显示全部楼层
    冀苍鸾 2023-6-29 14:38:58 看全部
    这个帖子不回对不起自己!我想我是一天也不能离开知鸟论坛
    回复

    使用道具 举报

    发表于 2023-6-29 16:31:09 | 显示全部楼层
    123456833 2023-6-29 16:31:09 看全部
    论坛不能没有像楼主这样的人才啊!我会一直支持知鸟论坛
    回复

    使用道具 举报

    发表于 2023-6-29 17:58:56 | 显示全部楼层
    向往草原403 2023-6-29 17:58:56 看全部
    楼主,我太崇拜你了!我想我是一天也不能离开知鸟论坛
    回复

    使用道具 举报

    发表于 2023-6-30 01:29:12 | 显示全部楼层
    123456809 2023-6-30 01:29:12 看全部
    其实我一直觉得楼主的品味不错!呵呵!知鸟论坛太棒了!
    回复

    使用道具 举报

    发表于 2023-6-30 02:37:33 | 显示全部楼层
    123456848 2023-6-30 02:37:33 看全部
    我看不错噢 谢谢楼主!知鸟论坛越来越好!
    回复

    使用道具 举报

    发表于 2023-6-30 08:11:28 | 显示全部楼层
    风吹吹蛋蛋疼风w 2023-6-30 08:11:28 看全部
    既然你诚信诚意的推荐了,那我就勉为其难的看看吧!知鸟论坛不走平凡路。
    回复

    使用道具 举报

    • 您可能感兴趣
    点击右侧快捷回复 【请勿灌水】
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则 返回列表

    RSS订阅| SiteMap| 小黑屋| 知鸟论坛
    联系邮箱E-mail:zniao@foxmail.com
    快速回复 返回顶部 返回列表