Web API 之SelfHost与OwinSelfHots加载外部程序

下面就一些web api的一些基础内容进行阐述,然后就web api宿主承载中的实际业务问题进行解决

HttpController

HttpController的激活是由处于消息处理管道尾端的HttpRoutingDispatcher来完成的,具体来说是HttpRoutingDispatcher利用HttpControllerDispatcher实现了针对目标HttpController的激活和执行。激活目标HttpController的前提是能够正确解析出HttpController的真实类型,而类型解析需要针对加载的程序集,所以我们需要先来了解一个用于解析程序集的对象AssembliesResolver。在ASP.NET Web API的HttpController激活系统中,AssembliesResolver为目标HttpController的类型解析提供候选的程序集。换句话说,候选HttpController类型的选择范围仅限于定义在通过AssembliesResolver提供的程序集中的所有实现了IHttpController接口的类型

AssembliesResolver

     所有的AssembliesResolver均实现了IAssembliesResolver接口。根据程序反射得到如下代码片段,根据代码片段得知,IAssembliesResolver提供的是程序集列表

public interface IAssembliesResolver
{
    ICollection<Assembly> GetAssemblies();
}
   

DefaultAssembliesResolver

默认的AssembliesResolver为DefaultAssembliesResolver,根据以下代码片段得知,默认返回的是当前应用程序域的程序集

public class DefaultAssembliesResolver : IAssembliesResolver
{
    public virtual ICollection<Assembly> GetAssemblies()
    {
        return AppDomain.CurrentDomain.GetAssemblies().ToList<Assembly>();
    }
}  

 

ServicesContainer

      默认的AssembliesResolver就是通过ServicesContainer类型确定

Web Api的请求相当于一个管道,类似于流水线作业,每个环节都会注册自己的实现类组件来完成自己的工作。这些组件都会实现自己特定的接口,当然在预置的组件无法满足我们的业务需求时,可以自己来实现组件代码,并进行注册,ServicesContainer其实可以简单的理解为这些组件的一个IOC容器

上面所介绍的就代表着,如果采用selfHost承载web api,而且web api与宿主程序不在同一程序集中的情况,如果在用上篇的代码,将会发现报以下错误  No type was found that matches the controller named ‘User‘.

上面所说的情况,分两种,第一种,单个api程序集,多个api程序集

1.如果api程序集全部写在一个单独的程序集中的情况

1.1 直接通过Assembly加载外部程序集

        static void Main(string[] args)
        {
            // 如果 API 处于外部程序集,可通过以下代码加载 CM.API为其他程序集
            //Assembly.Load("CM.API, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");    //加载外部程序集
            string baseAddress = "http://localhost:9000/";
            // 启动 OWIN host
            WebApp.Start<Startup>(url: baseAddress);
            Console.WriteLine("程序已启动,按任意键退出");
            Console.ReadLine();
        }

宿主项目还是需要引用CM.API程序集

1.2通过自定义AssembliesResolber来进行注册

    public class UserResolver: DefaultAssembliesResolver
    {
        public override ICollection<Assembly> GetAssemblies()
        {
            ICollection<Assembly> baseAssemblies = base.GetAssemblies();
            List<Assembly> assemblies = new List<Assembly>(baseAssemblies);
            var assembly = AppDomain.CurrentDomain.BaseDirectory;
            string path = assembly + "\\" + "CM.API.dll";
            var controllersAssembly = Assembly.LoadFrom(path);
            baseAssemblies.Add(controllersAssembly);
            return assemblies;
        }
    }

然后宿主启动时进行注册

        static void Main(string[] args)
        {
            var config = new HttpSelfHostConfiguration("http://localhost:8083");
            config.Routes.MapHttpRoute(
                "API Default", "api/{controller}/{id}",
                new { id = RouteParameter.Optional });
            //注册UserResolver,加载程序集
            config.Services.Replace(typeof(IAssembliesResolver), new UserResolver());
            using (var server = new HttpSelfHostServer(config))
            {
                server.OpenAsync().Wait();
                Console.WriteLine("Press Enter to quit.");
                Console.ReadLine();
            }
        }

    2.当然在业务开发中如果API被分散开发到多个程序集中,这种情况当然根据项目框架区分,各有利弊

2.1 通过上述的UserServoler进行加载

    public class UserResolver: DefaultAssembliesResolver
    {
        public override ICollection<Assembly> GetAssemblies()
        {
            ICollection<Assembly> baseAssemblies = base.GetAssemblies();
            List<Assembly> assemblies = new List<Assembly>(baseAssemblies);
            var assembly = AppDomain.CurrentDomain.BaseDirectory;
            string path = assembly + "\\" + "CM.API.dll";
            var controllersAssembly = Assembly.LoadFrom(path);
            baseAssemblies.Add(controllersAssembly);
            //加载多个程序集   不建议写死
            string path2 = assembly + "\\" + "CM.API2.dll";
            var controllersAssembly2 = Assembly.LoadFrom(path2);
            baseAssemblies.Add(controllersAssembly2);
            return assemblies;
        }
    }

上述代码只是展示加载的一种方式,只是得到程序集的方式需要去进行配置编码,不建议直接写死在代码里

2.2 通过自定配置类,进行配置文件读取加载

添加自定义配置类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;
using System.Reflection;

namespace CM.SelfHost
{
    public class AssembliesLoad : ConfigurationSection
    {
        [ConfigurationProperty("", IsDefaultCollection = true)]
        public AssemblyElementCollection AssemblyNames
        {
            get { return (AssemblyElementCollection)this[""]; }
        }

        public static AssembliesLoad GetSection()
        {
            return ConfigurationManager.GetSection("AssembliesLoad") as AssembliesLoad;
        }
    }
    public class AssemblyElementCollection : ConfigurationElementCollection
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new AssemblyElement();
        }
        protected override object GetElementKey(ConfigurationElement element)
        {
            AssemblyElement serviceTypeElement = (AssemblyElement)element;
            return serviceTypeElement.AssemblyName;
        }
    }

    public class AssemblyElement : ConfigurationElement
    {
        [ConfigurationProperty("assemblyName", IsRequired = true)]
        public string AssemblyName
        {
            get { return (string)this["assemblyName"]; }
            set { this["assemblyName"] = value; }
        }
    }
} 

添加配置文件,如下,加入ConfigSections节点以及AssembliesLoad节点

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <configSections>
    <section name="AssembliesLoad" type="CM.SelfHost.AssembliesLoad,CM.SelfHost"/>
  </configSections>
  <AssembliesLoad>
    <add assemblyName="CM.API.dll" />
  </AssembliesLoad>
</configuration>

添加AssemblyResolver类,并注册

    public class WebApiResolver: DefaultAssembliesResolver
    {
        public override ICollection<Assembly> GetAssemblies()
        {
            AssembliesLoad settings = AssembliesLoad.GetSection();
            if (null != settings)
            {
                foreach (AssemblyElement element in settings.AssemblyNames)
                {
                    AssemblyName assemblyName = AssemblyName.GetAssemblyName(element.AssemblyName);
                    if (!AppDomain.CurrentDomain.GetAssemblies().Any(assembly =>
                        AssemblyName.ReferenceMatchesDefinition(assembly.GetName(), assemblyName)))
                    {
                        AppDomain.CurrentDomain.Load(assemblyName);
                    }
                }
            }
            return base.GetAssemblies();
        }
    }

注册Resolver        

        static void Main(string[] args)
        {
            //Assembly.Load("CM.API, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");    //加载外部程序集
            var config = new HttpSelfHostConfiguration("http://localhost:8083");
            config.Routes.MapHttpRoute(
                "API Default", "api/{controller}/{id}",
                new { id = RouteParameter.Optional });
            config.Services.Replace(typeof(IAssembliesResolver), new UserResolver());
            using (var server = new HttpSelfHostServer(config))
            {
                server.OpenAsync().Wait();
                Console.WriteLine("Press Enter to quit.");
                Console.ReadLine();
            }
        }

运行会报错 其他信息: 配置系统未能初始化

查阅一些说明之后发现,如果存在自定义配置ConfigSections节点,那么必须在第一个节点,修改app.confg如下

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="AssembliesLoad" type="CM.SelfHost.AssembliesLoad,CM.SelfHost"/>
  </configSections>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <AssembliesLoad>
    <add assemblyName="CM.API.dll" />
  </AssembliesLoad>
</configuration>

至此,web api的承载方式就已经阐述完毕,说一下个人倾向,通过两篇文章对承载的了解,我喜欢使用OwinselfHost承载,并采用自定义配置文件的方式加载外部api程序集,Owin有跨平台功能,但没有去进行验证,大家有兴趣可以去验证下

时间: 2024-08-29 11:18:09

Web API 之SelfHost与OwinSelfHots加载外部程序的相关文章

Node.js【6】Web开发、进阶(模块加载、控制流、部署、弊端)

笔记来自<Node.js开发指南>BYVoid编著 实现过程:https://github.com/ichenxiaodao/express-example 第5章 使用Node.js进行Web开发 从零开始用Node.js实现一个微博系统,功能包括路由控制.页面模板.数据库访问.用户注册.登录.用户会话等内容. 会介绍Express框架.MVC设计模式.ejs模板引擎以及MongoDB数据库的操作. 5.1.准备工作 Express(http://expressjs.com/)除了为http

利用 React/Redux/React-Router 4/webpack 开发大型 web 项目时如何按需加载

如何设计一个大型 web 项目? React + webpack 如何按需加载? React + React-Router 4 + webpack 如何按需加载? React + Redux + React-Router 4 + webpack 如何按需加载? 实录提要: bundle-loader 和 Webpack 内置的 import() 有什么区别? 按需加载能否支持通过请求后台数据,动态配置页面的的应用场景? 参与过几个 React 项目,被依赖包搞的晕晕的,不知道该怎么选择? 什么包

IDEA导入maven工程以及web.xml中spring配置文件文件加载不到的问题

使用idea导入maven工程,工程只留了src和pom.xml文件 1.从打开idea中导入:File ----> New -----> Project from Existing Sources.如下图: 2.选择你所要导入的项目.点击ok 3.一定要选择; 第二个  :Import project from external model    从外部模型导入项目,然后点击Next 4.下一步......选择你需要的jdk.然后:项目名称一定和文件名称一致,然后点击Filish就OK了 首

web.xml中监听器如何顺序加载

最近用到在Tomcat服务器启动时自动加载数据到缓存,这就需要创建一个自定义的缓存监听器并实现ServletContextListener接口, 并且在此自定义监听器中需要用到Spring的依赖注入功能. 在web.xml文件中监听器配置如下: Xml代码 <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </lis

如何实现SAP UI5 Web Component React控件的加载效果

假设我使用SAP UI5 Web Component的React控件, 比如柱状图和折线图: 导入useState函数,默认加载状态为false: 每次点击屏幕后,首先将Loading状态使用切换函数setLoading设置成true,这样可以看到控件正在加载的动画效果.2秒钟后,setLoading设置为false,关闭动画效果. 下图这道渐进式显示的横线就是控件加载时的动画效果. 最后把loading变量赋给两个Chart的loading属性: 要获取更多Jerry的原创文章,请关注公众号"

Libgdx New 3D API 教程之 -- 使用Libgdx加载模型

http://bbs.9ria.com/thread-221701-1-1.html 在前面的教程中,我们已经看到如何设置libgdx渲染3D场景.我们已经设置了Camera,增加了一些灯光并渲染一个绿色的盒子.现在让我们添加一个比盒子更有趣的东西,模型Model. 您可以从您喜爱的建模应用程序或使用已有的模型.我找了gdx-invaders里面的飞船模型文件,你可以点这里下载.您可以解压缩后,将文件放到的android项目的assets目录下.请注意,它包含三个文件,这些文件需要放同一个文件夹

18 ArcGIS API for JavaScript4.X 系列加载天地图(经纬度)

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" con

arcgis api for javascipt 加载天地图、百度地图

写在前面的话: 1.百度地图是自己定义的坐标系统,wkid=102100.百度地图数据是加密的产物.下文将附上百度坐标与WGS84,谷歌等坐标系统转换方法(地理-地理),此方法并未亲测,据说准 2.百度地图可以直接加载经纬度坐标 3.百度地图如果加载的arcgis api中会出现坐标偏移,1.中已经解决了地理坐标转换,那么找到百度投影-地理坐标的转换方式,则arcgis就可以直接无偏加载了,网上很多方法,但是亲测不行. 4.天地图不是涉密数据,根据本文提供的类库,arcgis api for j

Use OWIN to Self-Host ASP.NET Web API 2

Open Web Interface for .NET (OWIN)在Web服务器和Web应用程序之间建立一个抽象层.OWIN将网页应用程序从网页服务器分离出来,然后将应用程序托管于OWIN的程序而离开IIS之外. This tutorial shows how to host ASP.NET Web API in a console application, using OWIN to self-host the Web API framework. Open Web Interface fo