测试请求的URL是否在Route表中
我想测试一个URL是否是Global.asax
定义的路由的一部分。 这就是我所拥有的:
var TheRequest = HttpContext.Current.Request.Url.AbsolutePath.ToString(); var TheRoutes = System.Web.Routing.RouteTable.Routes; foreach (var TheRoute in TheRoutes) { if (TheRequest == TheRoute.Url) //problem here { RequestIsInRoutes = true; } }
问题是我无法从路由中提取URL。 我需要改变什么?
问题是我无法从路由中提取URL。
我不同意。 问题是您希望将URL拉出路由表并在外部进行比较。 此外,目前还不清楚你希望通过这样做获得什么。
路由将传入请求与业务逻辑进行比较,以确定它是否匹配。 这是路线的目的 。 将匹配逻辑移到路由之外不是有效测试,因为您没有测试路由实现的业务逻辑。
更不用说,假设路由只能匹配URL而不是请求中的任何其他内容(例如表单发布值或cookie),这有点推测。 虽然内置路由function仅匹配URL,但没有什么能阻止您制作符合其他条件的约束或自定义路由。
因此,简而言之,您需要为路由中的业务逻辑编写unit testing。 在路由配置之外发生的任何逻辑都应单独进行unit testing。
Brad Wilson有一篇很棒的文章 (虽然有点陈旧),演示了如何对你的路线进行unit testing。 我已经更新了代码以使用MVC 5 – 这是一个使用下面代码的工作演示 。
IncomingRouteTests.cs
using Microsoft.VisualStudio.TestTools.UnitTesting; using MvcRouteTesting; using System.Web.Mvc; using System.Web.Routing; [TestClass] public class IncomingRouteTests { [TestMethod] public void RouteWithControllerNoActionNoId() { // Arrange var context = new StubHttpContextForRouting(requestUrl: "~/controller1"); var routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); // Act RouteData routeData = routes.GetRouteData(context); // Assert Assert.IsNotNull(routeData); Assert.AreEqual("controller1", routeData.Values["controller"]); Assert.AreEqual("Index", routeData.Values["action"]); Assert.AreEqual(UrlParameter.Optional, routeData.Values["id"]); } [TestMethod] public void RouteWithControllerWithActionNoId() { // Arrange var context = new StubHttpContextForRouting(requestUrl: "~/controller1/action2"); var routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); // Act RouteData routeData = routes.GetRouteData(context); // Assert Assert.IsNotNull(routeData); Assert.AreEqual("controller1", routeData.Values["controller"]); Assert.AreEqual("action2", routeData.Values["action"]); Assert.AreEqual(UrlParameter.Optional, routeData.Values["id"]); } [TestMethod] public void RouteWithControllerWithActionWithId() { // Arrange var context = new StubHttpContextForRouting(requestUrl: "~/controller1/action2/id3"); var routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); // Act RouteData routeData = routes.GetRouteData(context); // Assert Assert.IsNotNull(routeData); Assert.AreEqual("controller1", routeData.Values["controller"]); Assert.AreEqual("action2", routeData.Values["action"]); Assert.AreEqual("id3", routeData.Values["id"]); } [TestMethod] public void RouteWithTooManySegments() { // Arrange var context = new StubHttpContextForRouting(requestUrl: "~/a/b/c/d"); var routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); // Act RouteData routeData = routes.GetRouteData(context); // Assert Assert.IsNull(routeData); } [TestMethod] public void RouteForEmbeddedResource() { // Arrange var context = new StubHttpContextForRouting(requestUrl: "~/foo.axd/bar/baz/biff"); var routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); // Act RouteData routeData = routes.GetRouteData(context); // Assert Assert.IsNotNull(routeData); Assert.IsInstanceOfType(routeData.RouteHandler, typeof(StopRoutingHandler)); } }
OutgoingRouteTests.cs
using Microsoft.VisualStudio.TestTools.UnitTesting; using MvcRouteTesting; using System.Web; using System.Web.Mvc; using System.Web.Routing; [TestClass] public class OutgoingRouteTests { [TestMethod] public void ActionWithAmbientControllerSpecificAction() { UrlHelper helper = GetUrlHelper(); string url = helper.Action("action"); Assert.AreEqual("/defaultcontroller/action", url); } [TestMethod] public void ActionWithSpecificControllerAndAction() { UrlHelper helper = GetUrlHelper(); string url = helper.Action("action", "controller"); Assert.AreEqual("/controller/action", url); } [TestMethod] public void ActionWithSpecificControllerActionAndId() { UrlHelper helper = GetUrlHelper(); string url = helper.Action("action", "controller", new { id = 42 }); Assert.AreEqual("/controller/action/42", url); } [TestMethod] public void RouteUrlWithAmbientValues() { UrlHelper helper = GetUrlHelper(); string url = helper.RouteUrl(new { }); Assert.AreEqual("/defaultcontroller/defaultaction", url); } [TestMethod] public void RouteUrlWithAmbientValuesInSubApplication() { UrlHelper helper = GetUrlHelper(appPath: "/subapp"); string url = helper.RouteUrl(new { }); Assert.AreEqual("/subapp/defaultcontroller/defaultaction", url); } [TestMethod] public void RouteUrlWithNewValuesOverridesAmbientValues() { UrlHelper helper = GetUrlHelper(); string url = helper.RouteUrl(new { controller = "controller", action = "action" }); Assert.AreEqual("/controller/action", url); } static UrlHelper GetUrlHelper(string appPath = "/", RouteCollection routes = null) { if (routes == null) { routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); } HttpContextBase httpContext = new StubHttpContextForRouting(appPath); RouteData routeData = new RouteData(); routeData.Values.Add("controller", "defaultcontroller"); routeData.Values.Add("action", "defaultaction"); RequestContext requestContext = new RequestContext(httpContext, routeData); UrlHelper helper = new UrlHelper(requestContext, routes); return helper; } }
Stubs.cs
using System; using System.Collections.Specialized; using System.Web; public class StubHttpContextForRouting : HttpContextBase { StubHttpRequestForRouting _request; StubHttpResponseForRouting _response; public StubHttpContextForRouting(string appPath = "/", string requestUrl = "~/") { _request = new StubHttpRequestForRouting(appPath, requestUrl); _response = new StubHttpResponseForRouting(); } public override HttpRequestBase Request { get { return _request; } } public override HttpResponseBase Response { get { return _response; } } public override object GetService(Type serviceType) { return null; } } public class StubHttpRequestForRouting : HttpRequestBase { string _appPath; string _requestUrl; public StubHttpRequestForRouting(string appPath, string requestUrl) { _appPath = appPath; _requestUrl = requestUrl; } public override string ApplicationPath { get { return _appPath; } } public override string AppRelativeCurrentExecutionFilePath { get { return _requestUrl; } } public override string PathInfo { get { return ""; } } public override NameValueCollection ServerVariables { get { return new NameValueCollection(); } } } public class StubHttpResponseForRouting : HttpResponseBase { public override string ApplyAppPathModifier(string virtualPath) { return virtualPath; } }
有了这个,回到你原来的问题。
如何确定URL是否在路由表中?
这个问题有点推定。 正如其他人所指出的,路由表不包含URL,它包含业务逻辑。 一个更正确的方式来表达问题将是:
如何确定传入的URL是否与路由表中的任何路由匹配?
那你就在路上。
为此,您需要在路径集合中执行GetRouteData
业务逻辑。 这将在每个路由上执行GetRouteData
方法,直到它们中的第一个返回RouteData
对象而不是null
。 如果它们都没有返回RouteData
对象(即,所有路由都返回null
),则表示没有路由匹配该请求。
换句话说, GetRouteData
的null
结果表示没有任何路由与请求匹配。 RouteData
对象指示其中一个路由匹配,并提供必要的路由数据(控制器,操作等)以使MVC与操作方法匹配。
因此,要简单地检查URL是否与路由匹配,您只需确定操作的结果是否为null
。
[TestMethod] public void EnsureHomeAboutMatches() { // Arrange var context = new StubHttpContextForRouting(requestUrl: "~/home/about"); var routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); // Act RouteData routeData = routes.GetRouteData(context); // Assert Assert.IsNotNull(routeData); }
另请注意, 生成路由是与匹配传入路由的单独任务。 您可以从路由生成传出URL,但它使用与匹配传入路由完全不同的业务逻辑集。 这个传出的URL逻辑可以(并且应该)与传入的URL逻辑分开进行unit testing,如上所示。
我不知道这是否是您想要的路线,如果是这种情况,您可以从当前请求中获取:
var route = HttpContext.Current.Request.RequestContext.RouteData.Route;
您可以尝试根据路由表检查当前上下文
var contextBase = HttpContext.Current.Request.RequestContext.HttpContext; var data = RouteTable.Routes.GetRouteData(contextBase); if (data != null) { //Route exists }
使用以上作为创建服务的基础
public interface IRouteInspector { bool RequestIsInRoutes(); } public interface IHttpContextAccessor { HttpContextBase HttpContext { get; } } public interface IRouteTable { RouteCollection Routes { get; } } public class RouteInspector : IRouteInspector { private IRouteTable routeTable; private IHttpContextAccessor contextBase; public RouteInspector(IRouteTable routeTable, IHttpContextAccessor contextBase) { this.routeTable = routeTable; this.contextBase = contextBase; } public bool RequestIsInRoutes() { if (routeTable.Routes.GetRouteData(contextBase.HttpContext) != null) { //Route exists return true; } return false; } }
这里是测试类,显示它是如何使用的。
[TestClass] public class RouteTableUnitTests : ControllerUnitTests { [TestMethod] public void Should_Get_Request_From_Route_Table() { //Arrange var contextBase = new Mock(); contextBase.Setup(m => m.HttpContext) .Returns(HttpContext.Current.Request.RequestContext.HttpContext); var routeTable = new Mock(); routeTable.Setup(m => m.Routes).Returns(RouteTable.Routes); var sut = new RouteInspector(routeTable.Object, contextBase.Object); //Act var actual = sut.RequestIsInRoutes(); //Assert Assert.IsTrue(actual); } }
有重构和改进的空间,但这是一个开始。
这就是我最终做的事情:
string TheRequest = HttpContext.Current.Request.Url.AbsolutePath.ToString(); foreach (Route r in System.Web.Routing.RouteTable.Routes) { if (("/" + r.Url) == TheRequest) { //the request is in the routes } }
这是hacky但它有3行。