如何在 Umbraco 中创建虚拟节点

How to create a virtual node in Umbraco

情况

我在 Umbraco 7.3 中创建了一个新部分,我可以在其中管理目录(创建、编辑和删除产品)。当我创建一个产品时,我将其所有信息存储在 Umbraco 数据库的 table 中。请注意,我没有在树中创建节点,我只是直接使用数据库。

问题

例如,当我在该目录中创建 "Product A" 时,我会自动生成自定义 URL “/products/product-A” 并存储在数据库中的 table 中.但是,URL 不存在,因为我没有物理节点:

Page not found

No umbraco document matches the url '/products/product-A'.

由于我没有物理节点,我如何才能 "create" 一个 虚拟 节点并为该 URL 分配一个模板?

听起来您不需要 Umbraco 处理您的产品 URL。

您需要将自定义路由添加到您的 RouteConfig.cs 文件中,例如

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

然后创建 ProductsController

public class ProductsController : Controller
{
    public ActionResult Product(int id)
    {
        // Retrieve product from database
        // Return view
    }

如果您需要访问 UmbracoContext,这里有一个很好的例子: http://shazwazza.com/post/Custom-MVC-routing-in-Umbraco

根据您的 Gist,您需要创建 VirtualProductNode 吗?您可以 return Umbraco 页面 (nodeReference) 然后使用该页面 (template/controller) 处理产品数据吗?

protected override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext)
{
    // Get the real Umbraco IPublishedContent instance as a reference point
    var nodeReference = new UmbracoHelper(umbracoContext).TypedContent(3286);  

     contentRequest.PublishedContent = nodeReference;

     return contentRequest.PublishedContent != null;
}

我们做一些类似于列出存储在媒体部分的 pdf 文档页面的事情。 pdf 文档组织在映射到虚拟 url 结构的文件夹中。内容部分没有页面。

(我们不希望编辑上传所有 pdf 文档,然后必须进入内容部分,创建一个页面并再次 select pdf - 不必要的额外工作)

有一个页面定义了该部分,其模板将根据 url 显示正确的 pdf。该模板使用 url 在媒体部分找到相应的文件夹并列出 pdf 文件。

public class ProductsContentFinder : IContentFinder
{
    public bool TryFindContent(PublishedContentRequest contentRequest)
    {
        if (contentRequest != null)
        {
            // url: /store/products/product-name
            var path = contentRequest.Uri.GetAbsolutePathDecoded();
            var parts = path.Split(new[] { '/' }, System.StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length > 1)
            {
                var level1 = string.Concat('/', string.Join("/", parts.Take(2)), '/');
                var node = contentRequest.RoutingContext.UmbracoContext.ContentCache.GetByRoute(level1);
                 //find product page
                 var products = node.AncestorOrSelf("Products");

                 if (products != null)
                 {
                     contentRequest.PublishedContent = products;
                 }
             }
         }

         return contentRequest.PublishedContent != null;
     }
 }

我最后能弄明白。谢谢您的回答!

1。为我们的虚拟页面创建一个class

首先是创建一个 class,即继承自 PublishedContentWrappedProductVirtualPage。最后一个 class 为 IPubslihedContent 实现提供了一个抽象基础 class 来包装和扩展另一个 IPublishedContent。我们将使用 属性 Product 从 Razor 视图中获取 Product 并进行渲染。

public class ProductVirtualPage : PublishedContentWrapped
{   
  private readonly Product _product;

  public ProductVirtualPage(IPublishedContent content, Product product ) : base(content)
  {
     if (product.Name == null) throw new ArgumentNullException("productName");
     _product = product;
  }

   public Product Product
   {
       get
       {
           return _product;
       }
   }
 }

2。基于 UmbracoVirtualNodeRouteHandler

创建我们的处理程序

在树中我们需要创建一个引用节点,在我的例子中它的 ID 为 3286。我们将在调用 FindContent() 方法时将其传递给 ProductVirtualPage(nodeReference, product) 方法。

public class ProductVirtualNodeRouteHandler : UmbracoVirtualNodeRouteHandler
{
    private ProductService _productService;

    protected override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext)
    {
        // Get the real Umbraco IPublishedContent instance as a reference point
        var nodeReference = new UmbracoHelper(umbracoContext).TypedContent(3286);

        //Get the product Id from the URL (http://example.com/Products/product/57)
        var productId = umbracoContext.HttpContext.Request.Url.Segments[3];

        // Create an instance for the ProductService
        _productService = new ProductService(new ProductRepository(), new SiteRepository());

        // Get the product from the database
        var product = _productService.GetById(Convert.ToInt32(productId));

        // Get the virtual product
        return  new ProductVirtualPage(nodeReference, product);
    }
}

3。注册我们的自定义路线

我们需要使用 MapUmbracoRoute 添加自定义路由并将其绑定到提供 ApplicationEventHandler.

ApplicationStarted 方法
public class ContentServiceEventsController : ApplicationEventHandler
{

    protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication,
        ApplicationContext applicationContext)
    {
        // Add a route for the Products
        RouteTable.Routes.MapUmbracoRoute(
            "ProductPage",
            "Products/Product/{id}/",
            new
            {
                controller = "Product",
                action = "GetProduct"
            },
            new ProductVirtualNodeRouteHandler());
    }
}

4。创建控制器

控制器只会 return 视图,传递我们的 RenderModel model

public class ProductController : RenderMvcController
{
    public ActionResult GetProduct(RenderModel model, int id)
    {
        return View("Product", model);
    }
}

5。景色

@{
  Layout = "~/Views/Shared/_MasterLayout.cshtml";
  var productModel = Model.Content.Product;
  var product = productModel as Product; 
}
<pre>
  Product Info
  ---------------

  Id: @product.Id
  Name: @product.Name
</pre>