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

GPG*_*GVM 4 c# nopcommerce asp.net-mvc-4

我一直在读Wooncherk,Twisted 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);
}
Run Code Online (Sandbox Code Playgroud)

路由提供者请参阅注释 尝试覆盖核心URL~/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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

我得到了404错误并仔细阅读了Twisted Whisperer他(我认为他)说:

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

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

在此输入图像描述

我显然做到了这一点,它起作用了......有点儿.

总而言之,我的问题是:

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

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

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

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

Twi*_*per 7

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

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

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

ASP.NET MVC保留了一个包含Routes的通用查找表,称为RouteTable.Routes.路线包含的数据

  1. 网址格式
  2. 哪个Controller将传入的请求转发给

每次有传入请求时,ASP.NET MVC将从上到下搜索RouteTable.Routes,以查找URL模式与请求的URL匹配的路由.第一个匹配路由将用于将请求定向到匹配路由的Controller.表中可能有几条匹配的路线,但只会使用第一条路线.因此,我们可以说表格顶部的路线具有最高优先级.因此,覆盖现有Controller的关键是确保指向自定义Controller的路由早于指向原始Controller的路由注册

在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
}
Run Code Online (Sandbox Code Playgroud)

如您所见,区域路径始终首先注册,因此具有最高优先级.因此,在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是为了覆盖Controller.现在重写View.轻微调整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");
Run Code Online (Sandbox Code Playgroud)

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