2

我发现为什么在这里调用占位符“Home Controller”

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

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);
}

...即使 GetCountOfDepartmentRecords() 确实存在于 DepartmentsController 中:

public int GetCountOfDepartmentRecords()
{
    return _deptsRepository.Get();
}

那么,就设置或配置而言,我缺少什么或... ???

注意:我还尝试了没有“控制器”措辞的 RouteConfig 条目:

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) 存储库安装程序

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

(所有其他控制器也应如此,因为它们都实现了 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(),或者为什么我得到一个空控制器......

更新 4

根据 Summit Dishpanhands 的示例我注释掉了我在 RepositoriesInstaller 中的代码并将其替换为:

container.Register(
  Component.For<IDepartmentRepository>().ImplementedBy<DepartmentRepository>()
);

......在Mudville仍然没有快乐,虽然......

更新 5

在此处使用 Summit 的代码 [使用 Castle Windsor 在 WebAPI 中的依赖注入,我将 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

回应基兰查拉:

当我像这样将其更改为 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 为空。”

是的。

“您目前有以下内容:

var controllerFactory = new WindsorControllerFactory(container.Kernel); ControllerBuilder.Current.SetControllerFactory(controllerFactory); 你也需要做这样的事情(或者如果你的项目只使用 WebApi):

GlobalConfiguration.Configuration.Services.Replace(typeof (IHttpControllerActivator), new 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,似乎很有帮助,尤其是 ServiceInstaller : IWindsorInstaller 方法。

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

IRepository 是我应该参考的东西(我没有,它是无法解析的),还是你的自定义接口,例如所有其余的似乎都是(IUserService、IAppService 等)?

4

2 回答 2

5

好的,让我们分解一下。你是说你在 WindsorControllerFactory.GetControllerInstance 中得到一个空值。我假设您的意思是 controllerType 为空。

这意味着您的问题与 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创建了一个拉取请求,其中包含修复程序。看看我所做的更改的详细信息。以下是我更改内容的快速摘要:

  • IDepartmentsRepository 的 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
于 2014-01-07T11:24:25.760 回答
3

MapRoute扩展用于 MVC 控制器,而DepartmentsControllerWeb API 控制器实现IHttpController接口(例如:ApiController实现此接口)。您应该改用MapHttpRoute扩展名,如果有的话也可以使用WebApiConfig.cs

于 2014-01-07T00:40:00.357 回答