使用自己的一套控制器的多个owin听众,以及Autofac for DI

我试图使用多个进程内owin监听器。 每个控制器应该有一组不同的控制器,它们可以具有由不同控制器处理的相同路由。 例如

localhost:1234/api/app/test应解析为ControllerA

localhost:5678/api/app/test应解析为ControllerB

控制器a在owin主机1中具有路由属性

[Route("api/app/test")]

控制器b在owin主机2中具有路由属性

[Route("api/app/{*path}")]

并用于将请求转发给其他owin主机。

我们使用Autofac进行依赖注入。 路由通过属性路由进行配置。 autofac需要一行如

builder.RegisterApiControllers(typeof(ControllerA).Assembly)

我们的OWIN配置包含:

var config = ConfigureWebApi(); // Configure Autofac config.DependencyResolver = new AutofacWebApiDependencyResolver(container); app.UseAutofacMiddleware(container); app.UseAutofacWebApi(config); app.UseWebApi(config);

但是,当启动两个侦听器时,我需要包含用于控制器解析的两个程序集。 这导致“重复路线”例外:

Multiple controller types were found that match the URL. This can happen if attribute routes on multiple controllers match the requested URL.rnrnThe request has found the following matching controller types: rnLib1.Controllers.ControllerArnLib2.Controllers.ControllerB"

在单独的进程中运行OWIN侦听器时,没有问题。

我也尝试过使用多个DI容器,每个OWIN监听器都有一个容器,但它与Web Api 2冲突,因为它需要设置GlobalConfiguration.Configuration.DependencyResolver。 这与多个DI容器的概念相冲突。

有人可以指导我如何配置这样的设置?


使用OWIN环境并自定义HttpControllerSelector

使用OWIN管道,您可以将有关请求的信息传递给自定义的HttpControllerSelector 。 这使您可以选择使用哪些控制器来匹配哪些路线。

当然这说起来容易做起来难。 WebAPI关于路由的内部工作原理并不十分透明 - 源代码通常是这方面的最佳文档。

我无法让HttpControllerSelector完全工作,所以在CustomHttpActionSelector有一个丑陋的解决方法。 如果您只需要将请求从一台主机转发到另一台主机,则这仍然可能就足够了。

最终的结果是:

GEThttp://localhost:1234/api/app/test返回“HellofromAController”(直接调用AController)

GET to http://localhost:5678/api/app/test returns“(FromBController):”HellofromAController “”(调用BController,将请求转发给AController)

请参阅github上的完整源代码

我留下日志代码以防万一它有用,但它与解决方案无关。

所以没有进一步的道理:

CustomHttpControllerSelector.cs

使用特定于端口的OWIN env变量ApiControllersAssembly来过滤控制器。

public sealed class CustomHttpControllerSelector : DefaultHttpControllerSelector
{
    private static readonly ILog Logger;

    static CustomHttpControllerSelector()
    {
        Logger = LogProvider.GetCurrentClassLogger();
    }

    public CustomHttpControllerSelector(HttpConfiguration configuration) : base(configuration)
    {
    }

    public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
    {
        var apiControllerAssembly = request.GetOwinEnvironment()["ApiControllersAssembly"].ToString();
        Logger.Debug($"{nameof(CustomHttpControllerSelector)}: {{{nameof(apiControllerAssembly)}: {apiControllerAssembly}}}");

        var routeData = request.GetRouteData();
        var routeCollectionRoute = routeData.Route as IReadOnlyCollection<IHttpRoute>;
        var newRoutes = new List<IHttpRoute>();
        var newRouteCollectionRoute = new RouteCollectionRoute();
        foreach (var route in routeCollectionRoute)
        {
            var filteredDataTokens = FilterDataTokens(route, apiControllerAssembly);
            if (filteredDataTokens.Count == 2)
            {
                var newRoute = new HttpRoute(route.RouteTemplate, (HttpRouteValueDictionary)route.Defaults, (HttpRouteValueDictionary)route.Constraints, filteredDataTokens);
                newRoutes.Add(newRoute);
            }
        }

        var newRouteDataValues = new HttpRouteValueDictionary();
        foreach (var routeDataKvp in routeData.Values)
        {
            var newRouteDataCollection = new List<IHttpRouteData>();
            var routeDataCollection = routeDataKvp.Value as IEnumerable<IHttpRouteData>;
            if (routeDataCollection != null)
            {
                foreach (var innerRouteData in routeDataCollection)
                {
                    var filteredDataTokens = FilterDataTokens(innerRouteData.Route, apiControllerAssembly);
                    if (filteredDataTokens.Count == 2)
                    {
                        var newInnerRoute = new HttpRoute(innerRouteData.Route.RouteTemplate, (HttpRouteValueDictionary)innerRouteData.Route.Defaults, (HttpRouteValueDictionary)innerRouteData.Route.Constraints, filteredDataTokens);
                        var newInnerRouteData = new HttpRouteData(newInnerRoute, (HttpRouteValueDictionary)innerRouteData.Values);
                        newRouteDataCollection.Add(newInnerRouteData);
                    }
                }
                newRouteDataValues.Add(routeDataKvp.Key, newRouteDataCollection);
            }
            else
            {
                newRouteDataValues.Add(routeDataKvp.Key, routeDataKvp.Value);
            }

            HttpRouteData newRouteData;
            if (newRoutes.Count > 1)
            {
                newRouteCollectionRoute.EnsureInitialized(() => newRoutes);
                newRouteData = new HttpRouteData(newRouteCollectionRoute, newRouteDataValues);
            }
            else
            {
                newRouteData = new HttpRouteData(newRoutes[0], newRouteDataValues);
            }
            request.SetRouteData(newRouteData);
        }


        var controllerDescriptor = base.SelectController(request);
        return controllerDescriptor;
    }

    private static HttpRouteValueDictionary FilterDataTokens(IHttpRoute route, string apiControllerAssembly)
    {
        var newDataTokens = new HttpRouteValueDictionary();
        foreach (var dataToken in route.DataTokens)
        {
            var actionDescriptors = dataToken.Value as IEnumerable<HttpActionDescriptor>;
            if (actionDescriptors != null)
            {
                var newActionDescriptors = new List<HttpActionDescriptor>();
                foreach (var actionDescriptor in actionDescriptors)
                {
                    if (actionDescriptor.ControllerDescriptor.ControllerType.Assembly.FullName == apiControllerAssembly)
                    {
                        newActionDescriptors.Add(actionDescriptor);
                    }
                }
                if (newActionDescriptors.Count > 0)
                {
                    newDataTokens.Add(dataToken.Key, newActionDescriptors.ToArray());
                }
            }
            else
            {
                newDataTokens.Add(dataToken.Key, dataToken.Value);
            }
        }
        return newDataTokens;
    }
}

CustomHttpActionSelector.cs

你不应该需要一个CustomHttpActionSelector ,它只能解决BController的ActionDescriptors问题。 只要BController只有一个方法,它就会工作,否则你需要实现一些特定于路由的逻辑。

public sealed class CustomHttpActionSelector : ApiControllerActionSelector
{
    private static readonly ILog Logger;

    static CustomHttpActionSelector()
    {
        Logger = LogProvider.GetCurrentClassLogger();
    }

    public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
    {
        try
        {
            var actionDescriptor = base.SelectAction(controllerContext);
            return actionDescriptor;
        }
        catch (Exception ex)
        {
            Logger.WarnException(ex.Message, ex);

            IDictionary<string, object> dataTokens;
            var route = controllerContext.Request.GetRouteData().Route;
            var routeCollectionRoute = route as IReadOnlyCollection<IHttpRoute>;
            if (routeCollectionRoute != null)
            {
                dataTokens = routeCollectionRoute
                    .Select(r => r.DataTokens)
                    .SelectMany(dt => dt)
                    .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
            }
            else
            {
                dataTokens = route.DataTokens;
            }

            var actionDescriptors = dataTokens
                .Select(dt => dt.Value)
                .Where(dt => dt is IEnumerable<HttpActionDescriptor>)
                .Cast<IEnumerable<HttpActionDescriptor>>()
                .SelectMany(r => r)
                .ToList();

            return actionDescriptors.FirstOrDefault();
        }

    }
}

Program.cs

internal class Program
{
    private static readonly ILog Logger;

    static Program()
    {
        Log.Logger = new LoggerConfiguration()
            .WriteTo
            .LiterateConsole()
            .MinimumLevel.Is(LogEventLevel.Verbose)
            .CreateLogger();

        Logger = LogProvider.GetCurrentClassLogger();
    }

    internal static void Main(string[] args)
    {

        var builder = new ContainerBuilder();
        builder.RegisterModule(new LogRequestModule());
        builder.RegisterApiControllers(typeof(AController).Assembly);
        builder.RegisterApiControllers(typeof(BController).Assembly);

        var container = builder.Build();

        var config = GetHttpConfig();
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

        var options = new StartOptions();
        options.Urls.Add("http://localhost:1234");
        options.Urls.Add("http://localhost:5678");

        var listener = WebApp.Start(options, app =>
        {
            app.Use((ctx, next) =>
            {
                if (ctx.Request.LocalPort.HasValue)
                {
                    var port = ctx.Request.LocalPort.Value;
                    string apiControllersAssemblyName = null;
                    if (port == 1234)
                    {
                        apiControllersAssemblyName = typeof(AController).Assembly.FullName;
                    }
                    else if (port == 5678)
                    {
                        apiControllersAssemblyName = typeof(BController).Assembly.FullName;
                    }
                    ctx.Set("ApiControllersAssembly", apiControllersAssemblyName);
                    Logger.Info($"{nameof(WebApp)}: Port = {port}, ApiControllersAssembly = {apiControllersAssemblyName}");
                }
                return next();
            });
            app.UseAutofacMiddleware(container);
            app.UseAutofacWebApi(config);
            app.UseWebApi(config);
        });


        Logger.Info(@"Press [Enter] to exit");

        Console.ReadLine();

        listener.Dispose(); ;
    }


    private static HttpConfiguration GetHttpConfig()
    {
        var config = new HttpConfiguration();
        config.MapHttpAttributeRoutes();
        config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
        config.Services.Add(typeof(IExceptionLogger), new LogProviderExceptionLogger());
        config.Formatters.Remove(config.Formatters.XmlFormatter);
        config.Services.Replace(typeof(IHttpControllerSelector), new CustomHttpControllerSelector(config));
        config.Services.Replace(typeof(IHttpActionSelector), new CustomHttpActionSelector());

        var traceSource = new TraceSource("LibLog") { Switch = { Level = SourceLevels.All } };
        traceSource.Listeners.Add(new LibLogTraceListener());

        var diag = config.EnableSystemDiagnosticsTracing();
        diag.IsVerbose = false;
        diag.TraceSource = traceSource;

        return config;
    }
}

LibA Controllers AController.cs

[RoutePrefix("api/app")]
public class AController : ApiController
{
    private static readonly ILog Logger;
    static AController()
    {
        Logger = LogProvider.GetCurrentClassLogger();
        Logger.Debug($"{nameof(AController)}: Static Constructor");
    }

    public AController()
    {
        Logger.Debug($"{nameof(AController)}: Constructor");
    }


    [HttpGet, Route("test")]
    public async Task<IHttpActionResult> Get()
    {
        Logger.Debug($"{nameof(AController)}: Get()");

        return Ok($"Hello from {nameof(AController)}");
    }
}

LibB Controllers BController.cs

[RoutePrefix("api/app")]
public class BController : ApiController
{
    private static readonly ILog Logger;
    static BController()
    {
        Logger = LogProvider.GetCurrentClassLogger();
        Logger.Debug($"{nameof(BController)}: Static Constructor");
    }

    public BController()
    {
        Logger.Debug($"{nameof(BController)}: Constructor");
    }


    [HttpGet, Route("{*path}")]
    public async Task<IHttpActionResult> Get([FromUri] string path)
    {
        if (path == null)
        {
            path = Request.RequestUri.PathAndQuery.Split(new[] {"api/app/"}, StringSplitOptions.RemoveEmptyEntries)[1];
        }
        Logger.Debug($"{nameof(BController)}: Get({path})");

        using (var client = new HttpClient {BaseAddress = new Uri("http://localhost:1234/api/app/")})
        {
            var result = await client.GetAsync(path);
            var content = await result.Content.ReadAsStringAsync();
            return Ok($"(From {nameof(BController)}): {content}");
        }
    }
}

当我有更多的时间时,我可能会有另一个去。

让我知道你是否有任何进展!

链接地址: http://www.djcxy.com/p/36499.html

上一篇: Multiple owin listeners with their own set of controllers, with Autofac for DI

下一篇: Custom BLE Advertisement on iOS