使用结构图 IoC 解析应用核心内的常见路由

本文关键字:核心 常见 路由 应用 结构图 IoC | 更新日期: 2023-09-27 18:33:50

我们已经开始对框架站点进行 Mvc3 实现,目前使用我们现有的 WebForms 实现。

这项任务使我们能够将 IoC 和 DI 与 Structuremap 集成以提高灵活性。

为了给您一些背景知识,我们有以下项目结构:

App.Core <- 核心类库

App.Mvc <- Mvc 类库
App.Mvc.Web <- Mvc3 APP

App.WebForms <- WebForms 类库
App.WebForms.Web <- WebForms 应用程序或网站

我们在 Mvc 和 WebForms 实现上使用 Mvc 的路由,我们使用与 Orchard 项目中相同的路由发布方法,通过使用 IRouteProvider,其中可以创建 N 个 IRouteProvider 实现,以添加路由供发布者注册和按优先级排序。

这工作正常,我们能够在MVC中的UrlHelper或WebForms中的Page.GetRouteUrl注册和使用路由。

问题是我们现在要求 App.Core 也能够解析这些路由(不是全部,而是一些最常见的路由(,并且这些路由可以根据正在实现的站点进行更改。

例如,产品详细信息默认路由可能是"/{类别}/{product_name}/{product_id}",但我们希望能够覆盖此路由,并且对于某个网站,可以使用"/{brand}/{product_name}/{product_id}"。

这意味着在核心中,我们不能简单地使用带有一组固定参数的RouteTable.Routes.GetVirtualPath,因为这些参数可能会因站点而异。

我们创建了一个 IRouteResolver 接口,其中包含用于最常见路由的方法,该接口具有在每个类库(Mvc 或 Webforms(中向 SM 注册的默认实现,但也可以在每个站点上重写。

界面如下所示:

public interface IRouteResolver
{
    string GetRouteUrl(object routeParameters);
    string GetRouteUrl(RouteValueDictionary routeParameters);
    string GetRouteUrl(string routeName, object routeParameters);
    string GetRouteUrl(string routeName, RouteValueDictionary routeParameters);
    string GetUrlFor(Product product);
    string GetUrlFor(Category category);
    string GetUrlFor(Brand brand);
}

接口的默认 Mvc 实现如下所示:

public class MvcRouteResolver : IRouteResolver
{
    UrlHelper _urlHelper;
    ICategoryModelBroker _categoryModelBroker;
    IBrandModelBroker _brandModelBroker;
    IProductModelBroker _productModelBroker;
    public MvcRouteResolver(UrlHelper urlHelper)
    {
        _urlHelper = urlHelper;
        _categoryModelBroker = ObjectFactory.GetInstance<ICategoryModelBroker>();
        _brandModelBroker = ObjectFactory.GetInstance<IBrandModelBroker>();
        _productModelBroker = ObjectFactory.GetInstance<IProductModelBroker>();
    }
    public string GetRouteUrl(object routeParameters)
    {
        return GetRouteUrl(new RouteValueDictionary(routeParameters));
    }
    public string GetRouteUrl(System.Web.Routing.RouteValueDictionary routeParameters)
    {
        return GetRouteUrl(null, new RouteValueDictionary(routeParameters));
    }
    public string GetRouteUrl(string routeName, object routeParameters)
    {
        return GetRouteUrl(routeName, new RouteValueDictionary(routeParameters));
    }
    public string GetRouteUrl(string routeName, System.Web.Routing.RouteValueDictionary routeParameters)
    {
        return _urlHelper.RouteUrl(routeName, routeParameters);
    }
    public string GetUrlFor(Product product)
    {
        string category = string.Empty;
        if (product.Categories.Count > 0)
            category = product.Categories[0].Breadcrumb.Replace("@@", "-");
        else if (product.Brands.Any())
            category = product.Brands.FirstOrDefault().Name;
        else
            category = "detail";
        return GetRouteUrl(new { controller="Product", action="Detail", productId = product.Id, brandName = _productModelBroker.GetSlug(product), productName = _productModelBroker.GetSluggedName(product) });
    }
    public string GetUrlFor(Category category)
    {
        return GetRouteUrl(new { controller = "Product", action = "ListByCategory", id = category.Id, name = _categoryModelBroker.GetSlug(category) });
    }
    public string GetUrlFor(Brand brand)
    {
        return GetRouteUrl(new { controller = "Product", action = "ListByBrand", id = brand.Id, name = _brandModelBroker.GetSlug(brand) });
    }
}

默认的 WebForms 实现如下所示:

public class WebRouteResolver : IRouteResolver
{
    Control _control;
    HttpContext _context;
    public WebRouteResolver()
        :this(HttpContext.Current)
    {
    }
    public WebRouteResolver(HttpContext context)
    {
        _context = context;
    }
    public WebRouteResolver(Control control)
    {
        _control = control;
    }
    public WebRouteResolver(Page page)
    {
        _control = page as Control;
    }
    public string GetRouteUrl(object routeParameters)
    {
        return GetRouteUrl(new RouteValueDictionary(routeParameters));
    }
    public string GetRouteUrl(System.Web.Routing.RouteValueDictionary routeParameters)
    {
        return GetRouteUrl(null, new RouteValueDictionary(routeParameters));
    }
    public string GetRouteUrl(string routeName, object routeParameters)
    {
        return GetRouteUrl(routeName, new RouteValueDictionary(routeParameters));
    }
    public string GetRouteUrl(string routeName, System.Web.Routing.RouteValueDictionary routeParameters)
    {
        VirtualPathData virtualPath = null;
        if(_control.IsNotNull())
            virtualPath = RouteTable.Routes.GetVirtualPath(_control.Page.Request.RequestContext, routeName, routeParameters);
        else
            virtualPath = RouteTable.Routes.GetVirtualPath(_context.Request.RequestContext, routeName, routeParameters);
        if (virtualPath != null)
        {
            return virtualPath.VirtualPath;
        }
        return null;
    }
    private string ResolveUrl(string originalUrl)
    {
        if(_control.IsNotNull())
            return _control.ResolveUrl(originalUrl);
        // *** Absolute path - just return
        if (originalUrl.IndexOf("://") != -1)
            return originalUrl;
        // *** Fix up image path for ~ root app dir directory
        if (originalUrl.StartsWith("~"))
        {
            string newUrl = "";
            if (_context != null)
                newUrl = _context.Request.ApplicationPath +
                      originalUrl.Substring(1).Replace("//", "/");
            else
                // *** Not context: assume current directory is the base directory
                throw new ArgumentException("Invalid URL: Relative URL not allowed.");
            // *** Just to be sure fix up any double slashes
            return newUrl;
        }
        return originalUrl;
    }
    public string GetUrlFor(Product product)
    {
        string category = string.Empty;
        if (product.Categories.Count > 0)
            category = product.Categories[0].Breadcrumb.Replace("@@", "-");
        else if (product.Brands.Any())
            category = product.Brands.FirstOrDefault().Name;
        else
            category = "detail";
        if (Config.RoutingEnabled)
        {
            return GetRouteUrl(new { @category = CommonHelper.ToFriendlyUrl(category), name = CommonHelper.ToFriendlyUrl(product.Name), id = product.Id });
        }
        return ResolveUrl(Config.GetStoreSetting("productDetailUrl")) + "?id={0}&name={1}&category={2}".Fill(product.Id, CommonHelper.ToFriendlyUrl(product.Name), CommonHelper.ToFriendlyUrl(category));
    }
    public string GetUrlFor(Category category)
    {
        string breadcrumb = category.Breadcrumb.Replace("@@", "-");
        if (Config.RoutingEnabled)
            return GetRouteUrl(new { @category = CommonHelper.ToFriendlyUrl(breadcrumb), category_id = category.Id});
        return ResolveUrl(Config.GetStoreSetting("productListingUrl") + "?category_id={0}&category={1}".Fill(category.Id, CommonHelper.ToFriendlyUrl(category.Name)));
    }
    public string GetUrlFor(Brand brand)
    {
        if (Config.RoutingEnabled)
            return GetRouteUrl(new { @brand = CommonHelper.ToFriendlyUrl(brand.Name), brand_id = brand.Id });
        return ResolveUrl(Config.GetStoreSetting("productListingUrl") + "?brand_id={0}&brand={1}".Fill(brand.Id, CommonHelper.ToFriendlyUrl(brand.Name)));
    }
}

现在的问题是,由于构造函数参数(Mvc 上的 UrlHelper 和 Page 或 WebForms 上的控件(,我们被迫使用具体类型而不是使用 SM 来获取 IRouteResolver 插件的实例。

例如,我有以下扩展,以使解析程序在页面或控件上可用

public static IRouteResolver RouteResolver(this Control control)
{
    return new WebRouteResolver(control);
}
public static IRouteResolver RouteResolver(this Page page)
{
    return new WebRouteResolver(page);
}

这涵盖了 Web 或 Mvc 的默认行为,但不包括我们想要在每个站点的基础上专门覆盖解析器的情况。

问题是,将这些构造函数参数添加为 SM 中的插件是否安全?

对于此功能请求,是否可以推荐其他方法/模式?

任何想法/建议将不胜感激。

非常感谢,P.

使用结构图 IoC 解析应用核心内的常见路由

我想我在使用 StructureMap 时传递构造函数参数中找到了答案

只要覆盖具有相同的构造函数,我认为:

public static IRouteResolver RouteResolver(this Control control)
{
    return ObjectFactory.With("control").EqualTo(control).GetInstance<IRouteResolver>();
}
public static IRouteResolver RouteResolver(this Page page)
{
    return ObjectFactory.With("page").EqualTo(page).GetInstance<IRouteResolver>();
}

可能会工作,出来做一些测试

我采取了另一种方法,上述方法不适用于核心。我现在只依靠 RouteCollection 和 HttpContext 来解析路由:

    public abstract class BaseRouteResolver : IRouteResolver
{
    protected HttpContext _context;
    protected RouteCollection _routeCollection;
    public BaseRouteResolver()
        :this(RouteTable.Routes, HttpContext.Current)
    {
    }
    public BaseRouteResolver(RouteCollection routeCollection, HttpContext context)
    {
        _routeCollection = routeCollection;
        _context = context;
    }
    public string GetRouteUrl(object routeParameters)
    {
        return GetRouteUrl(new RouteValueDictionary(routeParameters));
    }
    public string GetRouteUrl(System.Web.Routing.RouteValueDictionary routeParameters)
    {
        return GetRouteUrl(null, new RouteValueDictionary(routeParameters));
    }
    public string GetRouteUrl(string routeName, object routeParameters)
    {
        return GetRouteUrl(routeName, new RouteValueDictionary(routeParameters));
    }
    public string GetRouteUrl(string routeName, System.Web.Routing.RouteValueDictionary routeParameters)
    {
        VirtualPathData virtualPath = _routeCollection.GetVirtualPath(_context.Request.RequestContext, routeName, routeParameters);
        if (virtualPath != null)
            return virtualPath.VirtualPath;
        return null;
    }
    public abstract string GetUrlFor(Product product);
    public abstract string GetUrlFor(Category category);
    public abstract string GetUrlFor(Brand brand);
}