routes - 如何获取Web API/CaSTLe Windsor来识别 Controller ?

标签 routes repository castle-windsor asp.net-web-api-routing controller-factory

我发现了为什么占位符“Home Controller”被称为here

我在RouteConfig.cs中注释掉了对HomeController的引用,并添加了一个我希望它调用的Controller:

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        //If get 404 error re: favicon, see http://docs.castleproject.org/(X(1)S(vv4c5o450lhlzb45p5wzrq45))/Windsor.Windsor-tutorial-part-two-plugging-Windsor-in.ashx or just uncomment the line below:
        //routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" });

        //routes.MapRoute(
        //    name: "Default",
        //    url: "{controller}/{action}/{id}",
        //    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        //);

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "DepartmentsController", action = "GetCountOfDepartmentRecords", id = UrlParameter.Optional }
        );
    }
}

...但这在WindsorControllerFactory中为我提供了一个空值:
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
    if (controllerType == null)
    {
        throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
    }
    return (IController)_kernel.Resolve(controllerType);
}

...即使在DepartmentsController中确实存在GetCountOfDepartmentRecords()也是如此:
public int GetCountOfDepartmentRecords()
{
    return _deptsRepository.Get();
}

那么,我在设置或配置方面还是缺少什​​么?

注意:我还尝试了RouteConfig条目,但没有“Controller”字样:
defaults: new { controller = "Departments", action = "GetCountOfDepartmentRecords", id = UrlParameter.Optional }

...但是没关系。

更新

这是我在WindsorDependencyResolver中具有的功能:
public class ApiControllersInstaller : IWindsorInstaller
{
    public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly()
         .BasedOn<ApiController>()
         .LifestylePerWebRequest());
    }
}

...难道我做错了什么?

更新2

也许我的项目中有太多实现IWindsorInstaller的类?

&& ||,与此相关,我在太多地方/太多地方叫container.Register()?

我在三个地方调用了container.Register():

0)WindsorDependencyResolver.cs:
public class ApiControllersInstaller : IWindsorInstaller
{
    public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly()
         .BasedOn<ApiController>()
         .LifestylePerWebRequest());
    }
}

1)SomethingProviderInstaller.cs:
public class SomethingProviderInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly()
                               .BasedOn(typeof(ISomethingProvider))
                               .WithServiceAllInterfaces());
        container.AddFacility<TypedFactoryFacility>();
        container.Register(Component.For<IMyFirstFactory>().AsFactory()); 
    }
}

2)RepositoriesInstaller
public class RepositoriesInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
                           Types.FromThisAssembly()
                                   .Where(type => type.Name.EndsWith("Repository"))
                                   .WithService.DefaultInterfaces()
                                   .Configure(c => c.LifestylePerWebRequest()));
    }
}

这是“太好了”吗?

更新3

我的看法是:
public class ApiControllersInstaller : IWindsorInstaller
{
    public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly()
         .BasedOn<ApiController>()
         .LifestylePerWebRequest());
    }
}

...应该注册实现ApiController接口的所有内容;因此,应该安装DepartmentsController。
public class DepartmentsController : ApiController

(所有其他Controller都应该这样做,因为它们都实现了ApiController接口)。

而这个:
public void Install(IWindsorContainer container, IConfigurationStore store)
{
    container.Register(Classes.FromThisAssembly()
                           .BasedOn(typeof(ISomethingProvider))
                           .WithServiceAllInterfaces());
    container.AddFacility<TypedFactoryFacility>();
    container.Register(Component.For<IMyFirstFactory>().AsFactory()); 
}

...只会注册实现ISomethingProvider的单个类。

最后是这个:

container.Register(
Types.FromThisAssembly()
.Where(type => type.Name.EndsWith(“Controllers”))
.WithService.DefaultInterfaces()
.Configure(c => c.LifestylePerWebRequest()));

...将注册名为Controller的所有内容。

注意:我更换了:
.Where(type => type.Name.EndsWith("Repository"))

...以及上面的内容(存储库)。

我在它们中都设置了断点,它们都被击中,然后被击中,其顺序与上面显示的顺序相反。所以我不知道是否只允许一个对container.Register()的调用,或者为什么我得到一个空的Controller ...

更新4

基于Summit Dishpanhands的here示例,我注释掉了RepositoriesInstaller中的代码,并用以下代码替换了它:
container.Register(
  Component.For<IDepartmentRepository>().ImplementedBy<DepartmentRepository>()
);

尽管在Mudville仍然没有喜悦

更新5

在此处使用Summit的代码[Dependency Injection in WebAPI with Castle Windsor,我从以下位置将Global.asax.cs中的代码更改为:
protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    GlobalConfiguration.Configure(WebApiConfig.Register);
    BootstrapContainer();
}

private static void BootstrapContainer()
{
    _container = new WindsorContainer().Install(FromAssembly.This());
    var controllerFactory = new WindsorControllerFactory(_container.Kernel);

    ControllerBuilder.Current.SetControllerFactory(controllerFactory);

    GlobalConfiguration.Configuration.Services.Replace(
        typeof(IHttpControllerActivator), new WindsorCompositionRoot(_container));
}

...对此:
protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    ConfigureWindsor(GlobalConfiguration.Configuration);
}

public static void ConfigureWindsor(HttpConfiguration configuration)
{
    _container = new WindsorContainer();
    _container.Install(FromAssembly.This());
    _container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));
    var dependencyResolver = new WindsorDependencyResolver(_container);
    configuration.DependencyResolver = dependencyResolver;
}   

...而且我似乎已经走得更远了。现在,我得到了一个浏览时间异常,而不是运行时异常,即HTTP错误403.14-禁止:

我真的需要搞砸国际空间站吗,还是有其他方法可以解决这个难题?

更新6

针对Kiran Challa:

当我将其更改为MapHttpRoute时,如下所示:
routes.MapHttpRoute(
    name: "Departments",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Departments", action = "GetCountOfDepartmentRecords", id = UrlParameter.Optional }
);

...我得到了编译时错误“'System.Web.Routing.RouteCollection'不包含'MapHttpRoute'的定义,并且没有扩展方法'MapHttpRoute'接受类型为'System.Web.Routing.RouteCollection的第一个参数'可以找到...”

更新7

谢谢你的帮助,基兰。

我确实有一个WebApiConfig.cs,实际上它已经具有所有这些:
public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        config.Routes.MapHttpRoute(
            name: "DefaultApiWithParameters",
            routeTemplate: "api/{controller}/{ID}/{CountToFetch}"
            //defaults: new { ID = RouteParameter.Optional, CountToFetch = RouteParameter.Optional }
        );

        config.Routes.MapHttpRoute(
            name: "DefaultApiWith3Parameters",
            routeTemplate: "api/{controller}/{ID}/{packSize}/{CountToFetch}"
            //defaults: new { ID = RouteParameter.Optional, packSize = RouteParameter.Optional, CountToFetch = RouteParameter.Optional }
        );

    }
}

...所以我想我应该只注释掉RouteConfig.cs中的内容;看来我档案太多了;好像我的衣柜里塞满了易燃物。

更新8

对于亚当·康纳利:

“我假设您的意思是controllerType为null。”

是。

“目前您拥有类似以下内容的地方:

var controllerFactory =新的WindsorControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
您还需要执行以下操作(或者,如果您的项目仅使用WebApi,则执行以下操作):

GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator),
新的WindsorControllerActivator(container));“

实际上,我确实有两个:
private static void BootstrapContainer()
{
    _container = new WindsorContainer().Install(FromAssembly.This());
    var controllerFactory = new WindsorControllerFactory(_container.Kernel);

    ControllerBuilder.Current.SetControllerFactory(controllerFactory);

    GlobalConfiguration.Configuration.Services.Replace(
        typeof(IHttpControllerActivator), new WindsorCompositionRoot(_container));
}

...但目前未调用BootstrapContainer(),因此均未调用。替代的是:
private static IWindsorContainer _container;
. . .
protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    ConfigureWindsor(GlobalConfiguration.Configuration);
}

public static void ConfigureWindsor(HttpConfiguration configuration)
{
    _container = new WindsorContainer();
    _container.Install(FromAssembly.This());
    _container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));
    var dependencyResolver = new WindsorDependencyResolver(_container);
    configuration.DependencyResolver = dependencyResolver;
}   

更新9

Serdar,this似乎非常有用,尤其是ServiceInstaller:IWindsorInstaller方法。

现在,我看到IRepository具有与其他生活方式(LifestylePerWebRequest)不同的生活方式(LifestyleTransient)。

IRepository是我应该参考的东西(我不是,它无法解决),还是它是您自己的自定义接口,例如所有其他接口似乎都是(IUserService,IAppService等)?

最佳答案

好的,让我们分解一下。您说的是在WindsorControllerFactory.GetControllerInstance中获得null。我假设您的意思是controllerType为null。

这意味着您的问题与Windsor无关,因此,通过暂时忽略您的Windsor配置,您可以避免很多混乱。问题是您的WebApi路由设置不正确,因此WebApi无法根据您提供的路由找到合适的控制器。

就像Kiran所说的,看起来您实际上没有为WebApi映射任何路由,这可能是您的问题所在。

此外,WebApi不使用MVC使用的WindsorControllerFactory,您需要一个单独的类来实现IHttpControllerActivator(有关示例的链接,请参见上面的Serdar帖子)。

您当前所在的位置如下:

var controllerFactory = new WindsorControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);

您还需要执行以下操作(或者,如果您的项目仅使用WebApi,则执行以下操作):
GlobalConfiguration.Configuration.Services.Replace(
    typeof (IHttpControllerActivator),
    new WindsorControllerActivator(container));

所以这是您需要做的:
  • 设置WebApi路由,以便WebApi而不是MVC处理对您的API的调用。
  • 实现IHttpControllerActivator,并向WebApi注册新的实现。

  • 更新

    我已经掌握了您的资料来源,并解决了一些问题。我在https://github.com/bclayshannon/WebAPICastleWindsorExtravaganza/pull/1中创建了包含其修复程序的请求请求。看一看,了解我所做更改的详细信息。以下是我所做更改的简要摘要:
  • IDspartmentsRepository的Windsor注册重复,导致在启动期间引发异常。
  • 我必须将Global.asax.cs中的WebApiConfig.Register调用替换为对GlobalConfiguration.Configure的调用。以前我得到的是404(但这可能只是我的机器和您的机器之间的区别)。
  • 我删除了一些导致MVC框架处理WebApi路由的MapRoutes调用。

  • 此外,我将所有API控制器都移到api文件夹中,以使内容更易于理解,并且在DepartmentsController上使用了属性路由。一个示例调用是GET / api / Departments / Count。
    使用属性路由不是必需的,但是通常可以使用它来创建一个体面的API。

    我已验证自己现在可以发出以下请求并进入DepartmentsController:
  • GET / api /部门/计数
  • GET / api / Departments?ID = 123&CountToFetch = 100
  • 关于routes - 如何获取Web API/CaSTLe Windsor来识别 Controller ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20960690/

    相关文章:

    .net - Azure函数路由参数为空

    svn - 为什么单个修订版的SVN转储大于完整转储?

    c# - CaSTLe Windsor Register by Convention - 如何注册基于实体的通用存储库?

    c# - Windsor 单例线程安全的这种依赖性吗?

    c# - 使用 CaSTLe Windsor 解析具有泛型类型约束的接口(interface)

    flutter - "There was no corresponding route"通过 Flutter 网址

    php - CakePHP - 从插件加载路由文件

    javascript - 在 AngularJS 中找不到 404

    适用于 Windows 的 SVN Hook

    windows - 如何在 Git 中重新下载整个存储库