public async Task MatchAsync_ValidRouteConstraint_EndpointMatched() { // Arrange var endpointDataSource = new DefaultEndpointDataSource(new List <Endpoint> { CreateEndpoint("/{p:int}", 0) }); var matcher = CreateDfaMatcher(endpointDataSource); var(httpContext, context) = CreateContext(); httpContext.Request.Path = "/1"; // Act await matcher.MatchAsync(httpContext, context); // Assert Assert.NotNull(context.Endpoint); }
public async Task MatchAsync_RequireValuesAndOptionalParameter_EndpointMatched() { // Arrange var endpoint = CreateEndpoint( "{controller}/{action}/{id?}", 0, requiredValues: new { controller = "Home", action = "Index", area = (string)null, page = (string)null }); var dataSource = new DefaultEndpointDataSource(new List <Endpoint> { endpoint }); var matcher = CreateDfaMatcher(dataSource); var httpContext = CreateContext(); httpContext.Request.Path = "/Home/Index/123"; // Act await matcher.MatchAsync(httpContext); // Assert Assert.Same(endpoint, httpContext.GetEndpoint()); Assert.Collection( httpContext.Request.RouteValues.OrderBy(kvp => kvp.Key), (kvp) => { Assert.Equal("action", kvp.Key); Assert.Equal("Index", kvp.Value); }, (kvp) => { Assert.Equal("controller", kvp.Key); Assert.Equal("Home", kvp.Value); }, (kvp) => { Assert.Equal("id", kvp.Key); Assert.Equal("123", kvp.Value); }); }
public void Constructor_Enumerable_ShouldMakeCopyOfEndpoints() { // Arrange var endpoint1 = new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "1"); var endpoint2 = new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "2"); var endpoints = new List <Endpoint> { endpoint1, endpoint2 }; // Act var dataSource = new DefaultEndpointDataSource((IEnumerable <Endpoint>)endpoints); endpoints.RemoveAt(0); endpoints[0] = null; // Assert Assert.Equal(2, dataSource.Endpoints.Count); Assert.Contains(endpoint1, dataSource.Endpoints); Assert.Contains(endpoint2, dataSource.Endpoints); }
public void Write_ExcludeRouteEndpointWithSuppressMatchingMetadata() { // Arrange var graphWriter = CreateGraphWriter(); var writer = new StringWriter(); var endpointsDataSource = new DefaultEndpointDataSource( new RouteEndpoint( (context) => null, RoutePatternFactory.Parse("/"), 0, new EndpointMetadataCollection(new SuppressMatchingMetadata()), string.Empty)); // Act graphWriter.Write(endpointsDataSource, writer); // Assert Assert.Equal(String.Join(Environment.NewLine, @"digraph DFA {", @"0 [label=""/""]", @"}") + Environment.NewLine, writer.ToString()); }
public async Task MatchAsync_ConstraintRejectsEndpoint_Logging() { // Arrange var endpointDataSource = new DefaultEndpointDataSource(new List <Endpoint> { CreateEndpoint("/{p:int}", 0) }); var sink = new TestSink(); var matcher = CreateDfaMatcher(endpointDataSource, loggerFactory: new TestLoggerFactory(sink, enabled: true)); var httpContext = CreateContext(); httpContext.Request.Path = "/One"; // Act await matcher.MatchAsync(httpContext); // Assert Assert.Null(httpContext.GetEndpoint()); Assert.Collection( sink.Writes, (log) => { Assert.Equal(1001, log.EventId); Assert.Equal("1 candidate(s) found for the request path '/One'", log.Message); }, (log) => { Assert.Equal(1003, log.EventId); Assert.Equal("Endpoint '/{p:int}' with route pattern '/{p:int}' was rejected by constraint 'p':'Microsoft.AspNetCore.Routing.Constraints.IntRouteConstraint' with value 'One' for the request path '/One'", log.Message); }, (log) => { Assert.Equal(1004, log.EventId); Assert.Equal("Endpoint '/{p:int}' with route pattern '/{p:int}' is not valid for the request path '/One'", log.Message); }); }
public async Task MatchAsync_InvalidRouteConstraint_NoEndpointMatched() { // Arrange var endpointDataSource = new DefaultEndpointDataSource(new List <Endpoint> { CreateEndpoint("/{p:int}", 0) }); var matcher = CreateDfaMatcher(endpointDataSource); var httpContext = new DefaultHttpContext(); httpContext.Request.Path = "/One"; var endpointFeature = new EndpointFeature(); // Act await matcher.MatchAsync(httpContext, endpointFeature); // Assert Assert.Null(endpointFeature.Endpoint); }
public async Task MatchAsync_ComplexSegmentRejectsEndpoint_Logging() { // Arrange var endpointDataSource = new DefaultEndpointDataSource(new List <Endpoint> { CreateEndpoint("/x-{id}-y", 0) }); var sink = new TestSink(); var matcher = CreateDfaMatcher(endpointDataSource, loggerFactory: new TestLoggerFactory(sink, enabled: true)); var httpContext = CreateContext(); httpContext.Request.Path = "/One"; // Act await matcher.MatchAsync(httpContext); // Assert Assert.Null(httpContext.GetEndpoint()); Assert.Collection( sink.Writes, (log) => { Assert.Equal(DfaMatcher.EventIds.CandidatesFound, log.EventId); Assert.Equal("1 candidate(s) found for the request path '/One'", log.Message); }, (log) => { Assert.Equal(DfaMatcher.EventIds.CandidateRejectedByComplexSegment, log.EventId); Assert.Equal("Endpoint '/x-{id}-y' with route pattern '/x-{id}-y' was rejected by complex segment 'x-{id}-y' for the request path '/One'", log.Message); }, (log) => { Assert.Equal(DfaMatcher.EventIds.CandidateNotValid, log.EventId); Assert.Equal("Endpoint '/x-{id}-y' with route pattern '/x-{id}-y' is not valid for the request path '/One'", log.Message); }); }
public async Task MatchAsync_MultipleEndpointsWithDifferentRequiredValues_EndpointMatched() { // Arrange var endpoint1 = CreateEndpoint( "{controller}/{action}/{id?}", 0, requiredValues: new { controller = "Home", action = "Index", area = (string)null, page = (string)null }); var endpoint2 = CreateEndpoint( "{controller}/{action}/{id?}", 0, requiredValues: new { controller = "Login", action = "Index", area = (string)null, page = (string)null }); var dataSource = new DefaultEndpointDataSource(new List <Endpoint> { endpoint1, endpoint2 }); var matcher = CreateDfaMatcher(dataSource); var httpContext = CreateContext(); httpContext.Request.Path = "/Home/Index/123"; // Act 1 await matcher.MatchAsync(httpContext); // Assert 1 Assert.Same(endpoint1, httpContext.GetEndpoint()); httpContext.Request.Path = "/Login/Index/123"; // Act 2 await matcher.MatchAsync(httpContext); // Assert 2 Assert.Same(endpoint2, httpContext.GetEndpoint()); }
public async Task MatchAsync_ParameterTransformer_EndpointMatched() { // Arrange var endpoint = CreateEndpoint( "ConventionalTransformerRoute/{controller:slugify}/{action=Index}/{param:slugify?}", 0, requiredValues: new { controller = "ConventionalTransformer", action = "Index", area = (string)null, page = (string)null }); var dataSource = new DefaultEndpointDataSource(new List <Endpoint> { endpoint }); var matcher = CreateDfaMatcher(dataSource); var httpContext = CreateContext(); httpContext.Request.Path = "/ConventionalTransformerRoute/conventional-transformer/Index"; // Act await matcher.MatchAsync(httpContext); // Assert Assert.Same(endpoint, httpContext.GetEndpoint()); Assert.Collection( httpContext.Request.RouteValues.OrderBy(kvp => kvp.Key), (kvp) => { Assert.Equal("action", kvp.Key); Assert.Equal("Index", kvp.Value); }, (kvp) => { Assert.Equal("controller", kvp.Key); Assert.Equal("ConventionalTransformer", kvp.Value); }); }
public async Task MatchAsync_DifferentDefaultCase_RouteValueUsesDefaultCase() { // Arrange var endpoint = CreateEndpoint( "{controller}/{action=TESTACTION}/{id?}", 0, requiredValues: new { controller = "TestController", action = "TestAction" }); var dataSource = new DefaultEndpointDataSource(new List <Endpoint> { endpoint }); var matcher = CreateDfaMatcher(dataSource); var httpContext = CreateContext(); httpContext.Request.Path = "/TestController"; // Act await matcher.MatchAsync(httpContext); // Assert Assert.Same(endpoint, httpContext.GetEndpoint()); Assert.Collection( httpContext.Request.RouteValues.OrderBy(kvp => kvp.Key), (kvp) => { Assert.Equal("action", kvp.Key); Assert.Equal("TESTACTION", kvp.Value); }, (kvp) => { Assert.Equal("controller", kvp.Key); Assert.Equal("TestController", kvp.Value); }); }
public async Task MatchAsync_DuplicateTemplatesAndDifferentOrder_LowerOrderEndpointMatched() { // Arrange var higherOrderEndpoint = CreateEndpoint("/Teams", 1); var lowerOrderEndpoint = CreateEndpoint("/Teams", 0); var endpointDataSource = new DefaultEndpointDataSource(new List <Endpoint> { higherOrderEndpoint, lowerOrderEndpoint }); var matcher = CreateDfaMatcher(endpointDataSource); var(httpContext, context) = CreateContext(); httpContext.Request.Path = "/Teams"; // Act await matcher.MatchAsync(httpContext, context); // Assert Assert.Equal(lowerOrderEndpoint, context.Endpoint); }
public void MatchAsync_ConstrainedParameter_MiddleSegment_EndpointNotMatched() { // Arrange var endpoint1 = CreateEndpoint("a/b/d", 0); var endpoint2 = CreateEndpoint("a/{param:length(2)}/c", 0); var dataSource = new DefaultEndpointDataSource(new List <Endpoint> { endpoint1, endpoint2 }); var matcher = (DfaMatcher)CreateDfaMatcher(dataSource).CurrentMatcher; var buffer = new PathSegment[3]; var(context, path, count) = CreateMatchingContext("/a/b/c", buffer); // Act var set = matcher.FindCandidateSet(context, path, buffer.AsSpan().Slice(0, count)); // Assert // We expect no candidates here since we trimmed the branch (a -> b -> c = (a/{param:length(2)}/c)) for the parameter based on `b` not meeting the length(2) constraint. Assert.Empty(set.candidates); }
public async Task MatchAsync_RequireValuesAndDifferentPath_NoEndpointMatched() { // Arrange var endpoint = CreateEndpoint( "{controller}/{action}", 0, requiredValues: new { controller = "Home", action = "Index", area = (string)null, page = (string)null }); var dataSource = new DefaultEndpointDataSource(new List <Endpoint> { endpoint }); var matcher = CreateDfaMatcher(dataSource); var(httpContext, context) = CreateContext(); httpContext.Request.Path = "/Login/Index"; // Act await matcher.MatchAsync(httpContext, context); // Assert Assert.Null(context.Endpoint); }
public void ConfigureServices(IServiceCollection services) { services.AddRouting(); var endpointDataSource = new DefaultEndpointDataSource(new[] { new MatcherEndpoint( invoker: (next) => (httpContext) => { var response = httpContext.Response; var payloadLength = _helloWorldPayload.Length; response.StatusCode = 200; response.ContentType = "text/plain"; response.ContentLength = payloadLength; return(response.Body.WriteAsync(_helloWorldPayload, 0, payloadLength)); }, routePattern: RoutePatternFactory.Parse("/plaintext"), order: 0, metadata: EndpointMetadataCollection.Empty, displayName: "Plaintext"), }); services.TryAddEnumerable(ServiceDescriptor.Singleton <EndpointDataSource>(endpointDataSource)); }
public async Task MatchAsync_RunsApplicableEndpointSelectorPolicies() { // Arrange var dataSource = new DefaultEndpointDataSource(new List <Endpoint> { CreateEndpoint("/test/{id:alpha}", 0), CreateEndpoint("/test/{id:int}", 0), CreateEndpoint("/test/{id}", 0), }); var policy = new Mock <MatcherPolicy>(); policy .As <IEndpointSelectorPolicy>() .Setup(p => p.AppliesToEndpoints(It.IsAny <IReadOnlyList <Endpoint> >())).Returns(true); policy .As <IEndpointSelectorPolicy>() .Setup(p => p.ApplyAsync(It.IsAny <HttpContext>(), It.IsAny <CandidateSet>())) .Returns <HttpContext, CandidateSet>((c, cs) => { cs.SetValidity(1, false); return(Task.CompletedTask); }); var matcher = CreateDfaMatcher(dataSource, policies: new[] { policy.Object, }); var httpContext = CreateContext(); httpContext.Request.Path = "/test/17"; // Act await matcher.MatchAsync(httpContext); // Assert Assert.Same(dataSource.Endpoints[2], httpContext.GetEndpoint()); }
public DynamicControllerEndpointMatcherPolicyTest() { var dataSourceKey = new ControllerEndpointDataSourceIdMetadata(1); var actions = new ActionDescriptor[] { new ControllerActionDescriptor() { RouteValues = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase) { ["action"] = "Index", ["controller"] = "Home", }, }, new ControllerActionDescriptor() { RouteValues = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase) { ["action"] = "About", ["controller"] = "Home", }, }, new ControllerActionDescriptor() { RouteValues = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase) { ["action"] = "Index", ["controller"] = "Blog", }, } }; ControllerEndpoints = new[] { new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(actions[0]), "Test1"), new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(actions[1]), "Test2"), new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(actions[2]), "Test3"), }; DynamicEndpoint = new Endpoint( _ => Task.CompletedTask, new EndpointMetadataCollection(new object[] { new DynamicControllerRouteValueTransformerMetadata(typeof(CustomTransformer), State), dataSourceKey }), "dynamic"); DataSource = new DefaultEndpointDataSource(ControllerEndpoints); SelectorCache = new TestDynamicControllerEndpointSelectorCache(DataSource, 1); var services = new ServiceCollection(); services.AddRouting(); services.AddTransient <CustomTransformer>(s => { var transformer = new CustomTransformer(); transformer.Transform = (c, values, state) => Transform(c, values, state); transformer.Filter = (c, values, state, candidates) => Filter(c, values, state, candidates); return(transformer); }); Services = services.BuildServiceProvider(); Comparer = Services.GetRequiredService <EndpointMetadataComparer>(); }
public void ConfigureServices(IServiceCollection services) { services.AddTransient <EndsWithStringRouteConstraint>(); services.AddRouting(options => { options.ConstraintMap.Add("endsWith", typeof(EndsWithStringRouteConstraint)); }); var endpointDataSource = new DefaultEndpointDataSource(new[] { new RouteEndpoint((httpContext) => { var response = httpContext.Response; var payloadLength = _homePayload.Length; response.StatusCode = 200; response.ContentType = "text/plain"; response.ContentLength = payloadLength; return(response.Body.WriteAsync(_homePayload, 0, payloadLength)); }, RoutePatternFactory.Parse("/"), 0, EndpointMetadataCollection.Empty, "Home"), new RouteEndpoint((httpContext) => { var response = httpContext.Response; var payloadLength = _helloWorldPayload.Length; response.StatusCode = 200; response.ContentType = "text/plain"; response.ContentLength = payloadLength; return(response.Body.WriteAsync(_helloWorldPayload, 0, payloadLength)); }, RoutePatternFactory.Parse("/plaintext"), 0, EndpointMetadataCollection.Empty, "Plaintext"), new RouteEndpoint((httpContext) => { var response = httpContext.Response; response.StatusCode = 200; response.ContentType = "text/plain"; return(response.WriteAsync("WithConstraints")); }, RoutePatternFactory.Parse("/withconstraints/{id:endsWith(_001)}"), 0, EndpointMetadataCollection.Empty, "withconstraints"), new RouteEndpoint((httpContext) => { var response = httpContext.Response; response.StatusCode = 200; response.ContentType = "text/plain"; return(response.WriteAsync("withoptionalconstraints")); }, RoutePatternFactory.Parse("/withoptionalconstraints/{id:endsWith(_001)?}"), 0, EndpointMetadataCollection.Empty, "withoptionalconstraints"), new RouteEndpoint((httpContext) => { using (var writer = new StreamWriter(httpContext.Response.Body, Encoding.UTF8, 1024, leaveOpen: true)) { var graphWriter = httpContext.RequestServices.GetRequiredService <DfaGraphWriter>(); var dataSource = httpContext.RequestServices.GetRequiredService <CompositeEndpointDataSource>(); graphWriter.Write(dataSource, writer); } return(Task.CompletedTask); }, RoutePatternFactory.Parse("/graph"), 0, new EndpointMetadataCollection(new HttpMethodMetadata(new[] { "GET", })), "DFA Graph"), new RouteEndpoint((httpContext) => { var linkGenerator = httpContext.RequestServices.GetRequiredService <LinkGenerator>(); var response = httpContext.Response; response.StatusCode = 200; response.ContentType = "text/plain"; return(response.WriteAsync( "Link: " + linkGenerator.GetPathByRouteValues(httpContext, "WithSingleAsteriskCatchAll", new { }))); }, RoutePatternFactory.Parse("/WithSingleAsteriskCatchAll/{*path}"), 0, new EndpointMetadataCollection( new RouteValuesAddressMetadata( routeName: "WithSingleAsteriskCatchAll", requiredValues: new RouteValueDictionary())), "WithSingleAsteriskCatchAll"), new RouteEndpoint((httpContext) => { var linkGenerator = httpContext.RequestServices.GetRequiredService <LinkGenerator>(); var response = httpContext.Response; response.StatusCode = 200; response.ContentType = "text/plain"; return(response.WriteAsync( "Link: " + linkGenerator.GetPathByRouteValues(httpContext, "WithDoubleAsteriskCatchAll", new { }))); }, RoutePatternFactory.Parse("/WithDoubleAsteriskCatchAll/{**path}"), 0, new EndpointMetadataCollection( new RouteValuesAddressMetadata( routeName: "WithDoubleAsteriskCatchAll", requiredValues: new RouteValueDictionary())), "WithDoubleAsteriskCatchAll"), }); services.TryAddEnumerable(ServiceDescriptor.Singleton <EndpointDataSource>(endpointDataSource)); }
public DynamicPageEndpointMatcherPolicyTest() { var actions = new ActionDescriptor[] { new PageActionDescriptor() { RouteValues = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase) { ["page"] = "/Index", }, DisplayName = "/Index", }, new PageActionDescriptor() { RouteValues = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase) { ["page"] = "/About", }, DisplayName = "/About" }, }; PageEndpoints = new[] { new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(actions[0]), "Test1"), new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(actions[1]), "Test2"), }; DynamicEndpoint = new Endpoint( _ => Task.CompletedTask, new EndpointMetadataCollection(new object[] { new DynamicPageRouteValueTransformerMetadata(typeof(CustomTransformer), State), new PageEndpointDataSourceIdMetadata(1), }), "dynamic"); DataSource = new DefaultEndpointDataSource(PageEndpoints); SelectorCache = new TestDynamicPageEndpointSelectorCache(DataSource); var services = new ServiceCollection(); services.AddRouting(); services.AddTransient <CustomTransformer>(s => { var transformer = new CustomTransformer(); transformer.Transform = (c, values, state) => Transform(c, values, state); transformer.Filter = (c, values, state, endpoints) => Filter(c, values, state, endpoints); return(transformer); }); Services = services.BuildServiceProvider(); Comparer = Services.GetRequiredService <EndpointMetadataComparer>(); LoadedEndpoints = new[] { new Endpoint(_ => Task.CompletedTask, EndpointMetadataCollection.Empty, "Test1"), new Endpoint(_ => Task.CompletedTask, EndpointMetadataCollection.Empty, "Test2"), new Endpoint(_ => Task.CompletedTask, EndpointMetadataCollection.Empty, "ReplacedLoaded") }; var loader = new Mock <PageLoader>(); loader .Setup(l => l.LoadAsync(It.IsAny <PageActionDescriptor>(), It.IsAny <EndpointMetadataCollection>())) .Returns((PageActionDescriptor descriptor, EndpointMetadataCollection endpoint) => Task.FromResult(new CompiledPageActionDescriptor { Endpoint = descriptor.DisplayName switch { "/Index" => LoadedEndpoints[0], "/About" => LoadedEndpoints[1], "/ReplacedEndpoint" => LoadedEndpoints[2], _ => throw new InvalidOperationException($"Invalid endpoint '{descriptor.DisplayName}'.") } }));
public DynamicPageEndpointMatcherPolicyTest() { var actions = new ActionDescriptor[] { new PageActionDescriptor() { RouteValues = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase) { ["page"] = "/Index", }, }, new PageActionDescriptor() { RouteValues = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase) { ["page"] = "/About", }, }, }; PageEndpoints = new[] { new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(actions[0]), "Test1"), new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(actions[1]), "Test2"), }; DynamicEndpoint = new Endpoint( _ => Task.CompletedTask, new EndpointMetadataCollection(new object[] { new DynamicPageRouteValueTransformerMetadata(typeof(CustomTransformer)), }), "dynamic"); DataSource = new DefaultEndpointDataSource(PageEndpoints); Selector = new TestDynamicPageEndpointSelector(DataSource); var services = new ServiceCollection(); services.AddRouting(); services.AddScoped <CustomTransformer>(s => { var transformer = new CustomTransformer(); transformer.Transform = (c, values) => Transform(c, values); return(transformer); }); Services = services.BuildServiceProvider(); Comparer = Services.GetRequiredService <EndpointMetadataComparer>(); LoadedEndpoint = new Endpoint(_ => Task.CompletedTask, EndpointMetadataCollection.Empty, "Loaded"); var loader = new Mock <PageLoader>(); loader .Setup(l => l.LoadAsync(It.IsAny <PageActionDescriptor>())) .Returns(Task.FromResult(new CompiledPageActionDescriptor() { Endpoint = LoadedEndpoint, })); Loader = loader.Object; }