测试请求的 URL 是否在路由 table 中
Test if a request's URL is in the Route table
我想测试 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。我需要更改什么?
不知道这是不是你想要的请求路由,如果是的话你可以从当前请求中获取:
var route = HttpContext.Current.Request.RequestContext.RouteData.Route;
您可以尝试根据路由检查当前上下文 table
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;
}
}
这里是测试 class 展示了它是如何使用的。
[TestClass]
public class RouteTableUnitTests : ControllerUnitTests {
[TestMethod]
public void Should_Get_Request_From_Route_Table() {
//Arrange
var contextBase = new Mock<IHttpContextAccessor>();
contextBase.Setup(m => m.HttpContext)
.Returns(HttpContext.Current.Request.RequestContext.HttpContext);
var routeTable = new Mock<IRouteTable>();
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);
}
}
还有重构和改进的空间,但这只是一个开始。
The problem is that I can't extract the URL from the route.
我不同意。问题是你期望将URL拉出路线table并在外部进行比较。此外,不清楚您希望通过这样做获得什么。
路由将传入请求与业务逻辑进行比较以确定它是否匹配。 这是路线的目的。将匹配逻辑移到路由之外不是有效测试,因为您没有测试路由实现的业务逻辑。
Not to mention, it is a bit presumptive to assume that a route can only match a URL and nothing else in the request such as form post values or cookies. While the built in routing functionality only matches URLs, there is nothing stopping you from making a constraint or custom route that matches other criteria.
所以,简而言之,您需要为路由中的业务逻辑编写单元测试。在路由配置之外发生的任何逻辑都应单独进行单元测试。
有一个 great post by Brad Wilson (albeit a bit dated) that demonstrates how to unit test your routes. I have updated the code to work with MVC 5 - here is a working demo 使用下面的代码。
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;
}
}
说完这些,回到你原来的问题。
How to determine if the URL is in the route table?
这个问题有点假设。正如其他人指出的那样,路由 table 不包含 URLs,它包含业务逻辑。更正确的问题表述方式是:
How to determine if an incoming URL matches any route in the route table?
那你就上路了。
为此,您需要在路由集合中执行 GetRouteData
业务逻辑。这将在每个路由上执行 GetRouteData
方法,直到第一个路由 return 成为 RouteData
对象而不是 null
。如果none个return一个RouteData
对象(即returnnull
的所有路由),则表示none个路由与请求匹配。
换句话说,GetRouteData
的 null
结果表明 none 的路由与请求匹配。 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 逻辑分开进行单元测试,如上所示。
这就是我最后做的事情:
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
}
}
它很笨拙,但它可以在 3 行中运行。
我想测试 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。我需要更改什么?
不知道这是不是你想要的请求路由,如果是的话你可以从当前请求中获取:
var route = HttpContext.Current.Request.RequestContext.RouteData.Route;
您可以尝试根据路由检查当前上下文 table
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;
}
}
这里是测试 class 展示了它是如何使用的。
[TestClass]
public class RouteTableUnitTests : ControllerUnitTests {
[TestMethod]
public void Should_Get_Request_From_Route_Table() {
//Arrange
var contextBase = new Mock<IHttpContextAccessor>();
contextBase.Setup(m => m.HttpContext)
.Returns(HttpContext.Current.Request.RequestContext.HttpContext);
var routeTable = new Mock<IRouteTable>();
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);
}
}
还有重构和改进的空间,但这只是一个开始。
The problem is that I can't extract the URL from the route.
我不同意。问题是你期望将URL拉出路线table并在外部进行比较。此外,不清楚您希望通过这样做获得什么。
路由将传入请求与业务逻辑进行比较以确定它是否匹配。 这是路线的目的。将匹配逻辑移到路由之外不是有效测试,因为您没有测试路由实现的业务逻辑。
Not to mention, it is a bit presumptive to assume that a route can only match a URL and nothing else in the request such as form post values or cookies. While the built in routing functionality only matches URLs, there is nothing stopping you from making a constraint or custom route that matches other criteria.
所以,简而言之,您需要为路由中的业务逻辑编写单元测试。在路由配置之外发生的任何逻辑都应单独进行单元测试。
有一个 great post by Brad Wilson (albeit a bit dated) that demonstrates how to unit test your routes. I have updated the code to work with MVC 5 - here is a working demo 使用下面的代码。
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;
}
}
说完这些,回到你原来的问题。
How to determine if the URL is in the route table?
这个问题有点假设。正如其他人指出的那样,路由 table 不包含 URLs,它包含业务逻辑。更正确的问题表述方式是:
How to determine if an incoming URL matches any route in the route table?
那你就上路了。
为此,您需要在路由集合中执行 GetRouteData
业务逻辑。这将在每个路由上执行 GetRouteData
方法,直到第一个路由 return 成为 RouteData
对象而不是 null
。如果none个return一个RouteData
对象(即returnnull
的所有路由),则表示none个路由与请求匹配。
换句话说,GetRouteData
的 null
结果表明 none 的路由与请求匹配。 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 逻辑分开进行单元测试,如上所示。
这就是我最后做的事情:
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
}
}
它很笨拙,但它可以在 3 行中运行。