话题

我们知道在.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一一下载,这是错误的做法

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

接下来我们在该类库中按照规范创建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<IInitTest, InitTest>(); var serviceProvider = services.BuildServiceProvider(); var initTest = serviceProvider.GetRequiredService<IInitTest>(); initTest.Init(); Console.Read();
}
}

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

当我们将类库中的控制器移动到控制台中,此时请求测试接口并成功返回对世界的问候,这是什么原因呢? 不难猜测可知,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类库中添加控制器激活,这种场景完全限定于底层主入口已封装好,所以只能采用这种方式,若是主入口我们自己可控制,当然还有另外一种方式,来,我们瞧瞧截取的关键性源码

/// <summary>
/// Populates the given <paramref name="feature"/> using the list of
/// <see cref="IApplicationFeatureProvider{TFeature}"/>s configured on the
/// <see cref="ApplicationPartManager"/>.
/// </summary>
/// <typeparam name="TFeature">The type of the feature.</typeparam>
/// <param name="feature">The feature instance to populate.</param>
public void PopulateFeature<TFeature>(TFeature feature)
{
if (feature == null)
{
throw new ArgumentNullException(nameof(feature));
} foreach (var provider in FeatureProviders.OfType<IApplicationFeatureProvider<TFeature>>())
{
provider.PopulateFeature(ApplicationParts, feature);
}
} internal void PopulateDefaultParts(string entryAssemblyName)
{
var assemblies = GetApplicationPartAssemblies(entryAssemblyName); var seenAssemblies = new HashSet<Assembly>(); 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<Assembly> 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<ApplicationPartAttribute>()
.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<Assembly> 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")]

那么接下来问题又来了,要是需要运行多个Web APi我们又当如何呢?按照上述方式一一添加未尝不可,我们也可以通过MSBuild任务来进行构建将相关特性自动添加到主入口程序集描述信息里面去,例如:

<ItemGroup>
<AssemblyAttribute Include="Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartAttribute">
<_Parameter1>Embed.WebApi</_Parameter1>
</AssemblyAttribute>
</ItemGroup>

有的童鞋就问了,这不写死了么,那还不如通过添加特性的方式去处理,请注意这里只是使用示例,实际情况下,我们可将多个Web APi放在同一解决方案下,然后在此解决方案下创建可构建任务的.targets文件,并在主项目文件里引入,将程序集名称作为变量引入,剩下事情自行统一处理,若不清楚怎么搞,就在代码中使用特性方式也未尝不可,例如如下:

<ItemGroup>
<AssemblyAttribute Include="Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartAttribute">
<_Parameter1>$(AssemblyName)</_Parameter1>
</AssemblyAttribute>
</ItemGroup>

总结

本节我们重点讨论如何内嵌运行.NET Core Web APi类库,同时介绍了两种激活比如控制器特性方案, 希望对您有所帮助,谢谢,我们下节再会

.NET Core Web APi类库如何内嵌运行?的更多相关文章

  1. ASP.NET Core Web API下事件驱动型架构的实现(一):一个简单的实现

    很长一段时间以来,我都在思考如何在ASP.NET Core的框架下,实现一套完整的事件驱动型架构.这个问题看上去有点大,其实主要目标是为了实现一个基于ASP.NET Core的微服务,它能够非常简单地 ...

  2. ASP.NET Core Web API下事件驱动型架构的实现(二):事件处理器中对象生命周期的管理

    在上文中,我介绍了事件驱动型架构的一种简单的实现,并演示了一个完整的事件派发.订阅和处理的流程.这种实现太简单了,百十行代码就展示了一个基本工作原理.然而,要将这样的解决方案运用到实际生产环境,还有很 ...

  3. ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线

    在上文中,我们讨论了事件处理器中对象生命周期的问题,在进入新的讨论之前,首先让我们总结一下,我们已经实现了哪些内容.下面的类图描述了我们已经实现的组件及其之间的关系,貌似系统已经变得越来越复杂了. 其 ...

  4. 循序渐进学.Net Core Web Api开发系列【14】:异常处理

    系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇介绍异 ...

  5. 循序渐进学.Net Core Web Api开发系列【1】:开发环境

    系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.本篇概述 本篇不 ...

  6. List多个字段标识过滤 IIS发布.net core mvc web站点 ASP.NET Core 实战:构建带有版本控制的 API 接口 ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目 Using AutoFac

    List多个字段标识过滤 class Program{  public static void Main(string[] args) { List<T> list = new List& ...

  7. C#设计模式总结 C#设计模式(22)——访问者模式(Vistor Pattern) C#设计模式总结 .NET Core launch.json 简介 利用Bootstrap Paginator插件和knockout.js完成分页功能 图片在线裁剪和图片上传总结 循序渐进学.Net Core Web Api开发系列【2】:利用Swagger调试WebApi

    C#设计模式总结 一. 设计原则 使用设计模式的根本原因是适应变化,提高代码复用率,使软件更具有可维护性和可扩展性.并且,在进行设计的时候,也需要遵循以下几个原则:单一职责原则.开放封闭原则.里氏代替 ...

  8. 重温.NET下Assembly的加载过程 ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线

    重温.NET下Assembly的加载过程   最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后 ...

  9. 或许是你应该了解的一些 ASP.NET Core Web API 使用小技巧

    一.前言 在目前的软件开发的潮流中,不管是前后端分离还是服务化改造,后端更多的是通过构建 API 接口服务从而为 web.app.desktop 等各种客户端提供业务支持,如何构建一个符合规范.容易理 ...

  10. ASP.NET Core Web API 最佳实践指南

    原文地址: ASP.NET-Core-Web-API-Best-Practices-Guide 介绍 当我们编写一个项目的时候,我们的主要目标是使它能如期运行,并尽可能地满足所有用户需求. 但是,你难 ...

随机推荐

  1. oracle新建登录用户sql语句

    CREATE TABLESPACE TEST_DBDATAFILE '/mnt/data/oracledata/TEST_DB .dbf' SIZE 5000M AUTOEXTEND ONUNIFOR ...

  2. 使用Apache Server 的ab进行web请求压力测试

    参考:http://www.cnblogs.com/spring3mvc/archive/2010/11/23/2414741.html 自己写代码经常是顺着逻辑写下去,写完后run一下,ok就玩完事 ...

  3. goaccess的安装和使用

    一.简介1.goaccess用于分析apache和nginx日志的强大工具 2.官网:https://goaccess.io 二.安装1.下载goaccess的安装包wget http://tar.g ...

  4. Linux_09------Linux上系统扫描和安全策略

    先谢慕课网/** * linux系统扫描技术 * * 主机扫描.路由扫描.批量服务扫描.系统安全策略(防SYN和ddos攻击) */ /** * 主机扫描 * ping fping hping * * ...

  5. 成功解决Tomcat-JDBC-MySQL乱码

    0.MySQL-JDBC驱动文档 官方解释 1.数据库的字符编码和表内字段的编码 在MySQL中数据库的字符编码和表内字段的编码的要指定为utf8(utf8_general_ci) 2.jsp中 pa ...

  6. Android通过tcpdump抓包(wifi, 2g, 3g都可以)

    http://blog.csdn.net/deng529828/article/details/20646197 1. 手机要有root权限 2. 下载tcpdump   http://www.str ...

  7. ASP.NET验证控件

    在此过程中房间的收费制度时,.为了验证文本框是否为空.用户存在.合法等等.我们都要单独写代码.学习了ASP.NET的验证控件,省了非常多事. ASP.NET能够轻松实现对用户输入的验证. 让我们好好回 ...

  8. linux 安装wordpress 无故往外发送大量垃圾邮件

    linux 安装wordpress 无故往外发送大量垃圾邮件 始末 表现出来的现象就是, 网站运行没多久,mysql服务就挂了,重启也无法启动起来,提示 No such file or dicrion ...

  9. classList用法

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. March 11th, 2018 Week 11th Sunday

    All good things must come to an end. 好景无常. Love is when the other person's happiness is more importa ...