本文转自:http://www.cnblogs.com/miku/archive/2012/09/27/2706276.html
1. 映射路由
大型MVC项目为了扩展性,可维护性不能像一般项目在Global中RegisterRoutes的方法里面映射路由。
这里学习一下Nop是如何做的。
Global.cs . 通过IOC容器取得IRoutePublisher实例
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("favicon.ico");
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); //register custom routes (plugins, etc)
var routePublisher = EngineContext.Current.Resolve<IRoutePublisher>();
routePublisher.RegisterRoutes(routes); routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "Nop.Web.Controllers" }
);
}
IRoutePublisher.cs 只有一个方法
public interface IRoutePublisher
{
void RegisterRoutes(RouteCollection routeCollection);
}
实现类
public class RoutePublisher : IRoutePublisher
{
private readonly ITypeFinder _typeFinder; public RoutePublisher(ITypeFinder typeFinder)
{
this._typeFinder = typeFinder;
} public void RegisterRoutes(RouteCollection routes)
{
var routeProviderTypes = _typeFinder.FindClassesOfType<IRouteProvider>();
var routeProviders = new List<IRouteProvider>();
foreach (var providerType in routeProviderTypes)
{
var provider = Activator.CreateInstance(providerType) as IRouteProvider;
routeProviders.Add(provider);
}
routeProviders = routeProviders.OrderByDescending(rp => rp.Priority).ToList();
routeProviders.ForEach(rp => rp.RegisterRoutes(routes));
}
}
ITypeFinder之前已经介绍过。点击查看 ,搜索项目中所有的IRouteProvider实现。然后根据优先级排序,最后调用RegisterRoutes依次映射
竟然有这么多,大多都在Plugin程序集中为插件映射路由。随便打开一个。
public partial class RouteProvider : IRouteProvider
{
public void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute("Plugin.Payments.AuthorizeNet.Configure",
"Plugins/PaymentAuthorizeNet/Configure",
new { controller = "PaymentAuthorizeNet", action = "Configure" },
new[] { "Nop.Plugin.Payments.AuthorizeNet.Controllers" }
); routes.MapRoute("Plugin.Payments.AuthorizeNet.PaymentInfo",
"Plugins/PaymentAuthorizeNet/PaymentInfo",
new { controller = "PaymentAuthorizeNet", action = "PaymentInfo" },
new[] { "Nop.Plugin.Payments.AuthorizeNet.Controllers" }
);
}
public int Priority
{
get
{
return 0;
}
}
}
好处不用说了。模块化方便复用。
2.自定义Route
LocalizedRoute.cs
using System.Web;
using System.Web.Routing;
using Nop.Core.Data;
using Nop.Core.Domain.Localization;
using Nop.Core.Infrastructure; namespace Nop.Web.Framework.Localization
{
/// <summary>
/// Provides properties and methods for defining a localized route, and for getting information about the localized route.
/// </summary>
public class LocalizedRoute : Route
{
#region Fields private bool? _seoFriendlyUrlsForLanguagesEnabled; #endregion #region Constructors /// <summary>
/// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern and handler class.
/// </summary>
/// <param name="url">The URL pattern for the route.</param>
/// <param name="routeHandler">The object that processes requests for the route.</param>
public LocalizedRoute(string url, IRouteHandler routeHandler)
: base(url, routeHandler)
{
} /// <summary>
/// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern, handler class and default parameter values.
/// </summary>
/// <param name="url">The URL pattern for the route.</param>
/// <param name="defaults">The values to use if the URL does not contain all the parameters.</param>
/// <param name="routeHandler">The object that processes requests for the route.</param>
public LocalizedRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
: base(url, defaults, routeHandler)
{
} /// <summary>
/// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern, handler class, default parameter values and constraints.
/// </summary>
/// <param name="url">The URL pattern for the route.</param>
/// <param name="defaults">The values to use if the URL does not contain all the parameters.</param>
/// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param>
/// <param name="routeHandler">The object that processes requests for the route.</param>
public LocalizedRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
: base(url, defaults, constraints, routeHandler)
{
} /// <summary>
/// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern, handler class, default parameter values,
/// constraints,and custom values.
/// </summary>
/// <param name="url">The URL pattern for the route.</param>
/// <param name="defaults">The values to use if the URL does not contain all the parameters.</param>
/// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param>
/// <param name="dataTokens">Custom values that are passed to the route handler, but which are not used to determine whether the route matches a specific URL pattern. The route handler might need these values to process the request.</param>
/// <param name="routeHandler">The object that processes requests for the route.</param>
public LocalizedRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
: base(url, defaults, constraints, dataTokens, routeHandler)
{
} #endregion #region Methods /// <summary>
/// Returns information about the requested route.
/// </summary>
/// <param name="httpContext">An object that encapsulates information about the HTTP request.</param>
/// <returns>
/// An object that contains the values from the route definition.
/// </returns>
public override RouteData GetRouteData(HttpContextBase httpContext)
{
if (DataSettingsHelper.DatabaseIsInstalled() && this.SeoFriendlyUrlsForLanguagesEnabled)
{
string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
string applicationPath = httpContext.Request.ApplicationPath;
if (virtualPath.IsLocalizedUrl(applicationPath, false))
{
//In ASP.NET Development Server, an URL like "http://localhost/Blog.aspx/Categories/BabyFrog" will return
//"~/Blog.aspx/Categories/BabyFrog" as AppRelativeCurrentExecutionFilePath.
//However, in II6, the AppRelativeCurrentExecutionFilePath is "~/Blog.aspx"
//It seems that IIS6 think we're process Blog.aspx page.
//So, I'll use RawUrl to re-create an AppRelativeCurrentExecutionFilePath like ASP.NET Development Server. //Question: should we do path rewriting right here?
string rawUrl = httpContext.Request.RawUrl;
var newVirtualPath = rawUrl.RemoveLocalizedPathFromRawUrl(applicationPath);
if (string.IsNullOrEmpty(newVirtualPath))
newVirtualPath = "/";
newVirtualPath = newVirtualPath.RemoveApplicationPathFromRawUrl(applicationPath);
newVirtualPath = "~" + newVirtualPath;
httpContext.RewritePath(newVirtualPath, true);
}
}
RouteData data = base.GetRouteData(httpContext);
return data;
} /// <summary>
/// Returns information about the URL that is associated with the route.
/// </summary>
/// <param name="requestContext">An object that encapsulates information about the requested route.</param>
/// <param name="values">An object that contains the parameters for a route.</param>
/// <returns>
/// An object that contains information about the URL that is associated with the route.
/// </returns>
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
VirtualPathData data = base.GetVirtualPath(requestContext, values); if (DataSettingsHelper.DatabaseIsInstalled() && this.SeoFriendlyUrlsForLanguagesEnabled)
{
if (data != null)
{
string rawUrl = requestContext.HttpContext.Request.RawUrl;
string applicationPath = requestContext.HttpContext.Request.ApplicationPath;
if (rawUrl.IsLocalizedUrl(applicationPath, true))
{
data.VirtualPath = string.Concat(rawUrl.GetLanguageSeoCodeFromUrl(applicationPath, true), "/",
data.VirtualPath);
}
}
}
return data;
} public virtual void ClearSeoFriendlyUrlsCachedValue()
{
_seoFriendlyUrlsForLanguagesEnabled = null;
} #endregion #region Properties protected bool SeoFriendlyUrlsForLanguagesEnabled
{
get
{
if (!_seoFriendlyUrlsForLanguagesEnabled.HasValue)
_seoFriendlyUrlsForLanguagesEnabled = EngineContext.Current.Resolve<LocalizationSettings>().SeoFriendlyUrlsForLanguagesEnabled; return _seoFriendlyUrlsForLanguagesEnabled.Value;
}
} #endregion
}
}
继承Route并且实现GetRouteData, GetVirtualPath这2个方法。从返回的参数可以知道一个是解析URL,一个是生成URL。是一个双向的过程。
注意 if (DataSettingsHelper.DatabaseIsInstalled() && this.SeoFriendlyUrlsForLanguagesEnabled) 这个判断条件,启用了地区化友好SEO。才进行URL转换。
简单说就是 http://localhost:2619/en 《=》http://localhost:2619/ 的双向转换。 en,ch之类的就是网站设置的语言了。
2。MVC路由测试
先看单元测试
映射路由
new Nop.Web.Infrastructure.RouteProvider().RegisterRoutes(RouteTable.Routes);
[Test]
public void Boards_routes()
{
"~/boards/".ShouldMapTo<BoardsController>(c => c.Index());
//TODO add support for optional parameters in 'ShouldMapTo' method (such as in ~/boards/activediscussions/ or ~/boards/topic/11/). The same is about issue is in the other route test methods
//"~/boards/activediscussions/".ShouldMapTo<BoardsController>(c => c.ActiveDiscussions(0));
//"~/boards/activediscussionsrss/".ShouldMapTo<BoardsController>(c => c.ActiveDiscussionsRss(0));
"~/boards/postedit/1".ShouldMapTo<BoardsController>(c => c.PostEdit(1));
"~/boards/postdelete/2".ShouldMapTo<BoardsController>(c => c.PostDelete(2));
"~/boards/postcreate/3".ShouldMapTo<BoardsController>(c => c.PostCreate(3, null));
"~/boards/postcreate/4/5".ShouldMapTo<BoardsController>(c => c.PostCreate(4, 5));
"~/boards/topicedit/6".ShouldMapTo<BoardsController>(c => c.TopicEdit(6));
"~/boards/topicdelete/7".ShouldMapTo<BoardsController>(c => c.TopicDelete(7));
"~/boards/topiccreate/8".ShouldMapTo<BoardsController>(c => c.TopicCreate(8));
"~/boards/topicmove/9".ShouldMapTo<BoardsController>(c => c.TopicMove(9));
"~/boards/topicwatch/10".ShouldMapTo<BoardsController>(c => c.TopicWatch(10));
//"~/boards/topic/11/".ShouldMapTo<BoardsController>(c => c.Topic(11, 1));
//"~/boards/topic/11/test-topic-slug".ShouldMapTo<BoardsController>(c => c.Topic(11, 1));
"~/boards/topic/11/test-topic-slug/page/2".ShouldMapTo<BoardsController>(c => c.Topic(11, 2));
"~/boards/forumwatch/12".ShouldMapTo<BoardsController>(c => c.ForumWatch(12));
"~/boards/forumrss/13".ShouldMapTo<BoardsController>(c => c.ForumRss(13));
//"~/boards/forum/14/".ShouldMapTo<BoardsController>(c => c.Forum(14, 1));
//"~/boards/forum/14/test-forum-slug".ShouldMapTo<BoardsController>(c => c.Forum(14, 1));
"~/boards/forum/14/test-forum-slug/page/2".ShouldMapTo<BoardsController>(c => c.Forum(14, 2));
"~/boards/forumgroup/15/".ShouldMapTo<BoardsController>(c => c.ForumGroup(15));
"~/boards/forumgroup/15/test-forumgroup-slug/".ShouldMapTo<BoardsController>(c => c.ForumGroup(15));
//"~/boards/search/".ShouldMapTo<BoardsController>(c => c.Search(null, null, null, null, null, 1));
}
看来这里的代码。膜拜作者吧。。之前一直不知道如何测试Route,最多是装个Routedebug什么的
接下来进行解析匹配
/// <summary>
/// Asserts that the route matches the expression specified. Checks controller, action, and any method arguments
/// into the action as route values.
/// </summary>
/// <typeparam name="TController">The controller.</typeparam>
/// <param name="routeData">The routeData to check</param>
/// <param name="action">The action to call on TController.</param>
public static RouteData ShouldMapTo<TController>(this RouteData routeData, Expression<Func<TController, ActionResult>> action)
where TController : Controller
{
routeData.ShouldNotBeNull("The URL did not match any route"); //check controller
routeData.ShouldMapTo<TController>(); //check action
var methodCall = (MethodCallExpression)action.Body;
string actualAction = routeData.Values.GetValue("action").ToString(); string expectedAction = methodCall.Method.ActionName();
actualAction.AssertSameStringAs(expectedAction); //check parameters
for (int i = 0; i < methodCall.Arguments.Count; i++)
{
ParameterInfo param = methodCall.Method.GetParameters()[i];
bool isReferenceType = !param.ParameterType.IsValueType;
bool isNullable = isReferenceType ||
(param.ParameterType.UnderlyingSystemType.IsGenericType && param.ParameterType.UnderlyingSystemType.GetGenericTypeDefinition() == typeof(Nullable<>)); string controllerParameterName = param.Name;
bool routeDataContainsValueForParameterName = routeData.Values.ContainsKey(controllerParameterName);
object actualValue = routeData.Values.GetValue(controllerParameterName);
object expectedValue = null;
Expression expressionToEvaluate = methodCall.Arguments[i]; // If the parameter is nullable and the expression is a Convert UnaryExpression,
// we actually want to test against the value of the expression's operand.
if (expressionToEvaluate.NodeType == ExpressionType.Convert
&& expressionToEvaluate is UnaryExpression)
{
expressionToEvaluate = ((UnaryExpression)expressionToEvaluate).Operand;
} switch (expressionToEvaluate.NodeType)
{
case ExpressionType.Constant:
expectedValue = ((ConstantExpression)expressionToEvaluate).Value;
break; case ExpressionType.New:
case ExpressionType.MemberAccess:
expectedValue = Expression.Lambda(expressionToEvaluate).Compile().DynamicInvoke();
break;
} if (isNullable && (string)actualValue == String.Empty && expectedValue == null)
{
// The parameter is nullable so an expected value of '' is equivalent to null;
continue;
} // HACK: this is only sufficient while System.Web.Mvc.UrlParameter has only a single value.
if (actualValue == UrlParameter.Optional ||
(actualValue != null && actualValue.ToString().Equals("System.Web.Mvc.UrlParameter")))
{
actualValue = null;
} if (expectedValue is DateTime)
{
actualValue = Convert.ToDateTime(actualValue);
}
else
{
expectedValue = (expectedValue == null ? expectedValue : expectedValue.ToString());
} string errorMsgFmt = "Value for parameter '{0}' did not match: expected '{1}' but was '{2}'";
if (routeDataContainsValueForParameterName)
{
errorMsgFmt += ".";
}
else
{
errorMsgFmt += "; no value found in the route context action parameter named '{0}' - does your matching route contain a token called '{0}'?";
}
actualValue.ShouldEqual(expectedValue, String.Format(errorMsgFmt, controllerParameterName, expectedValue, actualValue));
} return routeData;
}
这里又看到了表达式树强大的地方。关于表达式树具体使用请搜索博客园, 这里通过routeData和解析action来判断请求和action是否一致。
总结:通过学习Nop中路由的一些使用。掌握了一些很有用的Route的使用。这些模块今后也能根据需要加入到自己的网站中。
参考:
两篇关于自定义路由:http://www.cnblogs.com/john-connor/archive/2012/05/03/2478821.html
http://www.cnblogs.com/ldp615/archive/2011/12/05/asp-net-mvc-elegant-route.html
[转]学习Nop中Routes的使用的更多相关文章
-
Nop中的Cache浅析
Nop中定义了ICacheManger接口,它有几个实现,其中MemoryCacheManager是内存缓存的一个实现. MemoryCacheManager: using System; using ...
-
学习sql中的排列组合,在园子里搜着看于是。。。
学习sql中的排列组合,在园子里搜着看,看到篇文章,于是自己(新手)用了最最原始的sql去写出来: --需求----B, C, F, M and S住在一座房子的不同楼层.--B 不住顶层.C 不住底 ...
-
Android学习开发中如何保持API的兼容
Android学习开发中如何保持API的兼容: 1,采用良好的设计思路 在设计过程中,如果能按照下面的方式来进行设计,会让这个API生命更长久 面向用例的设计,收集用户建议,把自己模拟成用户,保证AP ...
-
学习Spring中遇到关于BeanFactory及测试类的问题
最近在学习Spring,使用的是Spring 5.0.1 学习书本中使用的是4.0 学习书本中使用以下来加载配置文件及设置 Resource resource = new ClassPathResou ...
-
斯坦福大学公开课机器学习:machine learning system design | trading off precision and recall(F score公式的提出:学习算法中如何平衡(取舍)查准率和召回率的数值)
一般来说,召回率和查准率的关系如下:1.如果需要很高的置信度的话,查准率会很高,相应的召回率很低:2.如果需要避免假阴性的话,召回率会很高,查准率会很低.下图右边显示的是召回率和查准率在一个学习算法中 ...
-
学习 MySQL中导入 导出CSV
学习 MySQL中导入 导出CSV http://blog.csdn.net/sara_yhl/article/details/6850107 速度是很快的 导出 select * from t ...
-
如何理解并学习javascript中的面向对象(OOP) [转]
如果你想让你的javascript代码变得更加优美,性能更加卓越.或者,你想像jQuery的作者一样,写出属于自己优秀的类库(哪怕是基于 jquery的插件).那么,你请务必要学习javascript ...
-
Emgu-WPF学习使用-中值模糊
原文:Emgu-WPF学习使用-中值模糊 实现效果: 实现途径: 前提:Image File-> System.Drawing.Bitmap->Image<Bgr, byte> ...
-
Flink 从0到1学习 —— Flink 中如何管理配置?
前言 如果你了解 Apache Flink 的话,那么你应该熟悉该如何像 Flink 发送数据或者如何从 Flink 获取数据.但是在某些情况下,我们需要将配置数据发送到 Flink 集群并从中接收一 ...
随机推荐
-
Discuz X3.2 网站快照被劫持的解决方法
附上另一个人的解决方法:http://www.discuz.net/thread-3549930-3-1.html 问题如下: 快照被劫持,无论怎么申诉,怎么更新快照,都无法消除此问题,第一次打开网站 ...
-
dot函数
dot函数是两个向量的点乘,注意lua中dot函数的写法 th> a [torch.DoubleTensor of size 1x3] [.0002s] th> b [torch.Doub ...
-
Linux下ntpdate时间同步
Linux下ntpdate时间同步 Ntp服务器配置(暂略,以后整理) 时间同步方法 同步命令 # ntpdate ntp服务器域名或IP 例:# nt ...
-
SPI协议总结
四种工作模式: Mode 0 CPOL=0, CPHA=0 Mode 1 CPOL=0, CPHA=1Mode 2 CPOL=1, CPHA=0 Mode 3 CPOL=1, CPHA=1 常使用Mo ...
-
[转载]vs2010中臃肿的ipch和sdf文件
使用VS2010建立C++解决方案时,会生成SolutionName.sdf和一个叫做ipch的文件夹,这两个文件再加上*.pch等文件使得工程变得非常的庞大,一个简单的程序都会占用几十M的硬盘容量, ...
-
openssl-1.1.0g reference
include/openssl aes.h: struct aes_key_st { aes.h: unsigned long rd_key[4 * (AES_MAXNR + 1)]; aes.h: ...
-
laravel 控制器类DB类操作
例子:TrGo表(trgo_chip): laravel框架建立:TrGoModel <?php namespace TrChaos\Model; class TrGoModel extends ...
-
爬虫三Scrapy
Scrapy,Python开发的一个快速,高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据.Scrapy用途广泛,可以用于数据挖掘.监测和自动化测试. Scrapy吸引人 ...
-
react复习总结(2)--react生命周期和组件通信
这是react项目复习总结第二讲, 第一讲:https://www.cnblogs.com/wuhairui/p/10367620.html 首先我们来学习下react的生命周期(钩子)函数. 什么是 ...
-
JaveWeb 公司项目(7)----- 通过JS动态生成DIV
Web网页项目的数据表格功能已经大体完成,下面是另一个主要功能,在线视频的显示 目前我做的项目是渔政监控方面,在之前C#的版本中已经实现了摄像头的在线监控,用的海康封装好的SDK,目前需要做的工作是在 ...