非常接近但不能完全覆盖核心视图nopcommerce

时间:2014-10-07 17:42:48

标签: c# asp.net-mvc-4 nopcommerce

我一直在阅读WooncherkTwisted WhispererAlex Wolf的一些非常好的帖子。他们的文章(Wooncherk)(Twisted Whisperer)(Alex Wolf)分别提供了非常丰富的信息,但是我不像SO社区的其他人那样聪明,而且不能很好把我失踪的东西拼凑在一起。

我正在覆盖管理区域中的核心视图...特别是订单编辑视图。我看到的行为是点击我的插件中的控制器,但 DOES 显示我的自定义视图。问题是自定义视图在Admin项目中,这真让我感到困惑。如何拥有自包含的插件,但必须将我的自定义视图安装到核心管理区域?

我想,也许是错误的,会发生的事情是,由于我更高的定义优先级,我的控制器会在搜索路径时首先被击中。

所以按照这里的指示是我的代码。

CustomViewEngine:


private readonly string[] _emptyLocations = null;

    public CustomViewEngine()
    {
        PartialViewLocationFormats = new[]
        {
            "~/Administration/MyExtension/Views/{1}/{0}.cshtml",
            "~/Administration/MyExtension/Views/Shared/{0}.cshtml"
        };

        ViewLocationFormats = new[]
        {
            "~/Administration/MyExtension/Views/{1}/{0}.cshtml",
            "~/Administration/MyExtension/Views/Shared/{0}.cshtml"
        };
    }


    protected override string GetPath(ControllerContext controllerContext, string[] locations, string[] areaLocations, string locationsPropertyName, string name, string controllerName, string theme, string cacheKeyPrefix, bool useCache, out string[] searchedLocations) 
    {
        searchedLocations = _emptyLocations;
        if (string.IsNullOrEmpty(name)) 
        {
            return string.Empty;
        }

        string areaName = GetAreaName(controllerContext.RouteData);

        //little hack to get nop's admin area to be in /Administration/ instead of /Nop/Admin/ or Areas/Admin/
        if (!string.IsNullOrEmpty(areaName) && areaName.Equals("admin", StringComparison.InvariantCultureIgnoreCase)) 
        {
            var newLocations = areaLocations.ToList();
            newLocations.Insert(0, "~/Administration/Views/{1}/{0}.cshtml");
            newLocations.Insert(0, "~/Administration/Views/Shared/{0}.cshtml");

            //Insert your custom View locations to the top of the list to be given a higher precedence
            newLocations.Insert(0, "~/Administration/MyExtension/Views/{1}/{0}.cshtml");
            newLocations.Insert(0, "~/Administration/MyExtension/Views/Shared/{0}.cshtml");

            areaLocations = newLocations.ToArray();
        }

        bool flag = !string.IsNullOrEmpty(areaName);
        List<ViewLocation> viewLocations = GetViewLocations(locations, flag ? areaLocations : null);

        if (viewLocations.Count == 0) 
        {
            throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Properties cannot be null or empty.", new object[] { locationsPropertyName }));
        }

        bool flag2 = IsSpecificPath(name);
        string key = CreateCacheKey(cacheKeyPrefix, name, flag2 ? string.Empty : controllerName, areaName, theme);

        if (useCache) 
        {
            var cached = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, key);
            if (cached != null) 
            {
                return cached;
            }
        }

        if (!flag2) 
        {
            return GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName, areaName, theme, key, ref searchedLocations);
        }

    return GetPathFromSpecificName(controllerContext, name, key, ref searchedLocations);
}

路线提供商见评论 尝试覆盖核心网址〜/ Admin / Order / Edit / 1


public void RegisterRoutes(RouteCollection routes)
    {
        ViewEngines.Engines.Insert(0, new CustomViewEngine());

//the articles above did NOT mention adding a path but it seemed like I needed to in order for my override path to be included???
        routes.MapRoute("Plugin...OrderDetailsOverride", "Order/Edit/{id}",
             new { controller = "MyOrder", action = "Edit" },
             new { id = @"\d+" },
             new[] { "MyPlugin.Controllers" }
        );            

    }

    public int Priority
    {
        get
        {
            return 1;
        }
    }

我收到404错误并仔细阅读Twisted Whisperer他(我猜他)说:

  

例如,如果您要覆盖Nop.Admin / Views / Category / Tree.cshtml,请将自定义Tree.cshtml放在Nop.Admin / CustomExtension / Views / Category / Tree.cshtml中

如果我按字面意思解释我会在 CORE ADMIN 项目中执行此操作:

enter image description here

我显然做到了这一点并且有效......分类。

总而言之,我的问题/问题是:

  1. 插件中的我的订单覆盖控制器没有被击中....(对使用ActionFilters不感兴趣,因为他们不能给我我需要的东西......我不喜欢想)。

  2. 我的插件中的视图没有被点击但添加到管理项目的视图被点击了吗?

  3. 与2.有些相关如果两个是正确的,那么这是一个可行的插件解决方案?对于生产推送,更新等,我可能不得不触及NopCommerce核心项目......那么为什么还要插入插件呢?

  4. 现在NOP人和SO社区都比我更聪明,所以我确定我不能正确理解。

2 个答案:

答案 0 :(得分:7)

如果我正确地阅读了您的问题,那么您正在创建一个插件并尝试在此插件中执行所有测试,而根本不会触及核心管理文件。然后你必须将所有这些文件放在你的插件项目中而不是 Nop.Admin

从外观上看,您将覆盖后端(管理区域)的控制器查看。您引用的SO post我的答案仅适用于覆盖管理视图,而不是控制器。在管理区域中覆盖控制器与覆盖查看不同,如果您覆盖前端的控制器,则会更加不同(假设涉及SEO友好URL。)

覆盖Controller本质上是要求ASP.NET MVC使用您的自定义Controller来代替原始Controller来处理请求。请求通过路由指向控制器。因此,覆盖Controller只是简单地操作路由以将请求定向到自定义控制器。

ASP.NET MVC保留包含路由的通用查找表,称为 RouteTable.Routes 。路线包含

的数据
  1. 网址格式
  2. 控制器将转接请求转发至
  3. 每次有传入请求时,ASP.NET MVC都会从上到下搜索 RouteTable.Routes ,以查找URL模式与请求的URL匹配的路由。第一个匹配路由将用于将请求定向到匹配路由的控制器。表中可能有几条匹配的路线,但只会使用第一条路线。因此,我们可以说表格顶部的路线具有最高优先级。因此,覆盖现有控制器的关键是确保指向自定义控制器的路由的注册时间早于指向原始控制器<的路由/强>

    在nopCommerce中,您无需直接操作 RouteTable.Routes 查找表。通常,您只需在实现 IRouteProvider 接口的类的 RegisterRoutes 方法中注册自定义Controller路由,其中​​优先级高于原始Controller路由(不适用于SEO友好URL区域路由)。 优先级越高,路由注册越早,因此它将在查找表中定位得越高。 但是,nopCommerce的后端实际上是名为 Admin 的ASP.NET MVC区域。这个事实使得以常规方式覆盖路线变得棘手。这是因为,在 global.asax

    //global.asax
    protected void Application_Start() {
        //.... Omitted for brevity
    
        //Registering some regular mvc stuff
        //The two lines below populate the RouteTable.Routes
        AreaRegistration.RegisterAllAreas(); //add Area routes into the lookup table first such as the "Admin" area
        RegisterRoutes(RouteTable.Routes);  //followed by adding routes of classes implementing **IRouteProvider**
    
        //.... Omitted for brevity
    }
    

    如上所示,区域路线始终首先注册,因此具有最高优先级。因此,在 IRouteProvider 中,无论您设置优先级多高,它仍然会低于区域路线。因此,您必须直接操作 RouteTable.Routes 。在 IRouteProvider

    public void RegisterRoutes(RouteCollection routes)
    {
        ViewEngines.Engines.Insert(0, new CustomViewEngine());
    
        var route = routes.MapRoute("Plugin...OrderDetailsOverride",
             "Admin/Order/Edit/{id}",
             new { controller = "MyOrder", action = "Edit" area = "Admin" }, //notice 'area="Admin"' is added
             new { id = @"\d+" },
             new[] { "MyPlugin.Controllers" }
        );
        routes.remove(route); //remove your route from the RouteTable.Routes
        routes.insert(0, route); //only to add it back again to the top of RouteTable.Routes, above all the routes that have been registered earlier
    }
    
    public int Priority
    {
        get
        {
            return 1;
        }
    }
    

    这是为了覆盖控制器。现在改写查看。轻微调整 CustomViewEngine (确保您的CustomViewEngine在您的插件中,而不是在Nop.Admin中)

    //Insert your custom View locations to the top of the list to be given a higher precedence
    newLocations.Insert(0, "~/Plugins/MyPlugin/Views/Admin/{1}/{0}.cshtml");
    newLocations.Insert(0, "~/Plugins/MyPlugin/Views/Admin/Shared/{0}.cshtml");
    

    您实际上可以删除 CustomViewEngine 的构造函数。如果您在GetPath方法中有两行,则不需要构造函数。

答案 1 :(得分:2)

最重要的是让控制器命中,其他的东西需要一些调整。

  1. 未正确覆盖路由,请参阅http://www.nopcommerce.com/boards/t/27039/plugin-override-admin-route.aspx

  2. 由于路径设置为自定义视图位置,因此使用了admin项目中的视图:〜/ Administration / MyExtension / Views 。 NopCommerce不知道插件中嵌入了哪些视图。

  3. 对于基于插件的解决方案,请确保 CustomViewEngine RouteProvider 以及视图位于插件中。确保将视图复制到输出目录。将插件中视图的输出文件夹注册为自定义视图位置,例如〜/ Plugins / MyPlugin / Views / Admin