diff --git a/NuGetPackageVerifier.json b/NuGetPackageVerifier.json index e2ec6646ab..787d2fec4b 100644 --- a/NuGetPackageVerifier.json +++ b/NuGetPackageVerifier.json @@ -10,6 +10,7 @@ ], "packages": { "Microsoft.AspNet.Routing.Abstractions": { }, + "Microsoft.AspNet.Routing.Extensions": { }, "Microsoft.AspNet.Routing": { } } }, diff --git a/Routing.sln b/Routing.sln index b0596d1297..cebcc0d01f 100644 --- a/Routing.sln +++ b/Routing.sln @@ -28,6 +28,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Routing.Ab EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Routing.Abstractions.Tests", "test\Microsoft.AspNet.Mvc.Routing.Abstractions.Tests\Microsoft.AspNet.Mvc.Routing.Abstractions.Tests.xproj", "{741B0B05-CE96-473B-B962-6B0A347DF79A}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Routing.Extensions", "src\Microsoft.AspNet.Routing.Extensions\Microsoft.AspNet.Routing.Extensions.xproj", "{579C3FD2-DFFA-4B64-BFC3-130C11F3568F}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Routing.Extensions.Tests", "test\Microsoft.AspNet.Routing.Extensions.Tests\Microsoft.AspNet.Routing.Extensions.Tests.xproj", "{FD0151B4-7153-49A8-9D90-62A252420230}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -116,6 +120,30 @@ Global {741B0B05-CE96-473B-B962-6B0A347DF79A}.Release|Mixed Platforms.Build.0 = Release|Any CPU {741B0B05-CE96-473B-B962-6B0A347DF79A}.Release|x86.ActiveCfg = Release|Any CPU {741B0B05-CE96-473B-B962-6B0A347DF79A}.Release|x86.Build.0 = Release|Any CPU + {579C3FD2-DFFA-4B64-BFC3-130C11F3568F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {579C3FD2-DFFA-4B64-BFC3-130C11F3568F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {579C3FD2-DFFA-4B64-BFC3-130C11F3568F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {579C3FD2-DFFA-4B64-BFC3-130C11F3568F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {579C3FD2-DFFA-4B64-BFC3-130C11F3568F}.Debug|x86.ActiveCfg = Debug|Any CPU + {579C3FD2-DFFA-4B64-BFC3-130C11F3568F}.Debug|x86.Build.0 = Debug|Any CPU + {579C3FD2-DFFA-4B64-BFC3-130C11F3568F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {579C3FD2-DFFA-4B64-BFC3-130C11F3568F}.Release|Any CPU.Build.0 = Release|Any CPU + {579C3FD2-DFFA-4B64-BFC3-130C11F3568F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {579C3FD2-DFFA-4B64-BFC3-130C11F3568F}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {579C3FD2-DFFA-4B64-BFC3-130C11F3568F}.Release|x86.ActiveCfg = Release|Any CPU + {579C3FD2-DFFA-4B64-BFC3-130C11F3568F}.Release|x86.Build.0 = Release|Any CPU + {FD0151B4-7153-49A8-9D90-62A252420230}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD0151B4-7153-49A8-9D90-62A252420230}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD0151B4-7153-49A8-9D90-62A252420230}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FD0151B4-7153-49A8-9D90-62A252420230}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {FD0151B4-7153-49A8-9D90-62A252420230}.Debug|x86.ActiveCfg = Debug|Any CPU + {FD0151B4-7153-49A8-9D90-62A252420230}.Debug|x86.Build.0 = Debug|Any CPU + {FD0151B4-7153-49A8-9D90-62A252420230}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD0151B4-7153-49A8-9D90-62A252420230}.Release|Any CPU.Build.0 = Release|Any CPU + {FD0151B4-7153-49A8-9D90-62A252420230}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {FD0151B4-7153-49A8-9D90-62A252420230}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {FD0151B4-7153-49A8-9D90-62A252420230}.Release|x86.ActiveCfg = Release|Any CPU + {FD0151B4-7153-49A8-9D90-62A252420230}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -128,5 +156,7 @@ Global {09C2933C-23AC-41B7-994D-E8A5184A629C} = {95359B4B-4C85-4B44-A75B-0621905C4CF6} {ED253B01-24F1-43D1-AA0B-079391E105A9} = {0E966C37-7334-4D96-AAF6-9F49FBD166E3} {741B0B05-CE96-473B-B962-6B0A347DF79A} = {95359B4B-4C85-4B44-A75B-0621905C4CF6} + {579C3FD2-DFFA-4B64-BFC3-130C11F3568F} = {0E966C37-7334-4D96-AAF6-9F49FBD166E3} + {FD0151B4-7153-49A8-9D90-62A252420230} = {95359B4B-4C85-4B44-A75B-0621905C4CF6} EndGlobalSection EndGlobal diff --git a/samples/RoutingSample.Web/Startup.cs b/samples/RoutingSample.Web/Startup.cs index 416d1b4c2d..6ee715cead 100644 --- a/samples/RoutingSample.Web/Startup.cs +++ b/samples/RoutingSample.Web/Startup.cs @@ -1,6 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Globalization; +using System.Threading; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; using Microsoft.AspNet.Routing; @@ -15,7 +17,7 @@ namespace RoutingSample.Web services.AddRouting(); } - public void Configure(IApplicationBuilder builder) + public void Configure(IApplicationBuilder app) { var endpoint1 = new RouteHandler((c) => { @@ -24,12 +26,30 @@ namespace RoutingSample.Web var endpoint2 = new RouteHandler((c) => c.Response.WriteAsync("Hello, World!")); - var routeBuilder = new RouteBuilder() + var routeBuilder = new RouteBuilder(app) { DefaultHandler = endpoint1, - ServiceProvider = builder.ApplicationServices, }; + routeBuilder.MapRoute("api/status/{item}", c => c.Response.WriteAsync($"{c.GetRouteValue("item")} is just fine.")); + routeBuilder.MapRoute("localized/{lang=en-US}", b => + { + b.Use(next => async (c) => + { + var culture = new CultureInfo((string)c.GetRouteValue("lang")); +#if DNX451 + Thread.CurrentThread.CurrentCulture = culture; + Thread.CurrentThread.CurrentUICulture = culture; +#else + CultureInfo.CurrentCulture = culture; + CultureInfo.CurrentUICulture = culture; +#endif + await next(c); + }); + + b.Run(c => c.Response.WriteAsync($"What would you do with {1000000m:C}?")); + }); + routeBuilder.AddPrefixRoute("api/store", endpoint1); routeBuilder.AddPrefixRoute("hello/world", endpoint2); @@ -38,7 +58,7 @@ namespace RoutingSample.Web routeBuilder.AddPrefixRoute("", endpoint2); - builder.UseRouter(routeBuilder.Build()); + app.UseRouter(routeBuilder.Build()); } } } \ No newline at end of file diff --git a/samples/RoutingSample.Web/project.json b/samples/RoutingSample.Web/project.json index c82801ee6d..bdd39222cd 100644 --- a/samples/RoutingSample.Web/project.json +++ b/samples/RoutingSample.Web/project.json @@ -1,16 +1,15 @@ { - "dependencies": { - "Microsoft.AspNet.Server.Kestrel": "1.0.0-*", - "Microsoft.AspNet.Server.WebListener": "1.0.0-*", - "Microsoft.AspNet.Routing": "1.0.0-*" - }, - "commands": { - "web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001", - "kestrel": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://localhost:5000" - }, - "frameworks": { - "dnx451": { }, - "dnxcore50": { } - }, - "webroot": "wwwroot" + "dependencies": { + "Microsoft.AspNet.Server.Kestrel": "1.0.0-*", + "Microsoft.AspNet.Server.WebListener": "1.0.0-*", + "Microsoft.AspNet.Routing.Extensions": "1.0.0-*" + }, + "commands": { + "web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001", + "kestrel": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://localhost:5000" + }, + "frameworks": { + "dnx451": { }, + "dnxcore50": { } + } } diff --git a/samples/RoutingSample.Web/wwwroot/ReadMe.md b/samples/RoutingSample.Web/wwwroot/ReadMe.md deleted file mode 100644 index 6fcb35323e..0000000000 --- a/samples/RoutingSample.Web/wwwroot/ReadMe.md +++ /dev/null @@ -1 +0,0 @@ -RoutingSample.Web \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing.Extensions/Microsoft.AspNet.Routing.Extensions.xproj b/src/Microsoft.AspNet.Routing.Extensions/Microsoft.AspNet.Routing.Extensions.xproj new file mode 100644 index 0000000000..5ad562b4eb --- /dev/null +++ b/src/Microsoft.AspNet.Routing.Extensions/Microsoft.AspNet.Routing.Extensions.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 579c3fd2-dffa-4b64-bfc3-130c11f3568f + Microsoft.AspNet.Routing.Extensions + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + diff --git a/src/Microsoft.AspNet.Routing.Extensions/Properties/AssemblyInfo.cs b/src/Microsoft.AspNet.Routing.Extensions/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..e0f545c6b5 --- /dev/null +++ b/src/Microsoft.AspNet.Routing.Extensions/Properties/AssemblyInfo.cs @@ -0,0 +1,8 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Reflection; +using System.Resources; + +[assembly: AssemblyMetadata("Serviceable", "True")] +[assembly: NeutralResourcesLanguage("en-us")] \ No newline at end of file diff --git a/src/Microsoft.AspNet.Routing.Extensions/RequestDelegateRouteBuilderExtensions.cs b/src/Microsoft.AspNet.Routing.Extensions/RequestDelegateRouteBuilderExtensions.cs new file mode 100644 index 0000000000..4624bf6d66 --- /dev/null +++ b/src/Microsoft.AspNet.Routing.Extensions/RequestDelegateRouteBuilderExtensions.cs @@ -0,0 +1,207 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Routing; +using Microsoft.AspNet.Routing.Constraints; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNet.Builder +{ + public static class RequestDelegateRouteBuilderExtensions + { + /// + /// Adds a route to the for the given , and + /// . + /// + /// The . + /// The route template. + /// The route handler. + /// A reference to the after this operation has completed. + public static IRouteBuilder MapRoute(this IRouteBuilder builder, string template, RequestDelegate handler) + { + var route = new Route( + new RouteHandler(handler), + template, + defaults: null, + constraints: null, + dataTokens: null, + inlineConstraintResolver: GetConstraintResolver(builder)); + + builder.Routes.Add(route); + return builder; + } + + /// + /// Adds a route to the for the given , and + /// . + /// + /// The . + /// The action to apply to the . + /// The route handler. + /// A reference to the after this operation has completed. + public static IRouteBuilder MapRoute(this IRouteBuilder builder, string template, Action action) + { + var nested = builder.ApplicationBuilder.New(); + action(nested); + return builder.MapRoute(template, nested.Build()); + } + + /// + /// Adds a route to the that only matches HTTP DELETE requests for the given + /// , and . + /// + /// The . + /// The route template. + /// The route handler. + /// A reference to the after this operation has completed. + public static IRouteBuilder MapDelete(this IRouteBuilder builder, string template, RequestDelegate handler) + { + return builder.MapVerb("DELETE", template, handler); + } + + /// + /// Adds a route to the that only matches HTTP DELETE requests for the given + /// , and . + /// + /// The . + /// The route template. + /// The action to apply to the . + /// A reference to the after this operation has completed. + public static IRouteBuilder MapDelete(this IRouteBuilder builder, string template, Action action) + { + return builder.MapVerb("DELETE", template, action); + } + + /// + /// Adds a route to the that only matches HTTP DELETE requests for the given + /// , and . + /// + /// The . + /// The route template. + /// The route handler. + /// A reference to the after this operation has completed. + public static IRouteBuilder MapGet(this IRouteBuilder builder, string template, RequestDelegate handler) + { + return builder.MapVerb("GET", template, handler); + } + + /// + /// Adds a route to the that only matches HTTP DELETE requests for the given + /// , and . + /// + /// The . + /// The route template. + /// The action to apply to the . + /// A reference to the after this operation has completed. + public static IRouteBuilder MapGet(this IRouteBuilder builder, string template, Action action) + { + return builder.MapVerb("GET", template, action); + } + + /// + /// Adds a route to the that only matches HTTP DELETE requests for the given + /// , and . + /// + /// The . + /// The route template. + /// The route handler. + /// A reference to the after this operation has completed. + public static IRouteBuilder MapPost(this IRouteBuilder builder, string template, RequestDelegate handler) + { + return builder.MapVerb("POST", template, handler); + } + + /// + /// Adds a route to the that only matches HTTP DELETE requests for the given + /// , and . + /// + /// The . + /// The route template. + /// The action to apply to the . + /// A reference to the after this operation has completed. + public static IRouteBuilder MapPost(this IRouteBuilder builder, string template, Action action) + { + return builder.MapVerb("POST", template, action); + } + + /// + /// Adds a route to the that only matches HTTP DELETE requests for the given + /// , and . + /// + /// The . + /// The route template. + /// The route handler. + /// A reference to the after this operation has completed. + public static IRouteBuilder MapPut(this IRouteBuilder builder, string template, RequestDelegate handler) + { + return builder.MapVerb("PUT", template, handler); + } + + /// + /// Adds a route to the that only matches HTTP DELETE requests for the given + /// , and . + /// + /// The . + /// The route template. + /// The action to apply to the . + /// A reference to the after this operation has completed. + public static IRouteBuilder MapPut(this IRouteBuilder builder, string template, Action action) + { + return builder.MapVerb("PUT", template, action); + } + + /// + /// Adds a route to the that only matches HTTP requests for the given + /// , , and . + /// + /// The . + /// The HTTP verb allowed by the route. + /// The route template. + /// The route handler. + /// A reference to the after this operation has completed. + public static IRouteBuilder MapVerb( + this IRouteBuilder builder, + string verb, + string template, + RequestDelegate handler) + { + var route = new Route( + new RouteHandler(handler), + template, + defaults: null, + constraints: new RouteValueDictionary(new { httpMethod = new HttpMethodRouteConstraint(verb) }), + dataTokens: null, + inlineConstraintResolver: GetConstraintResolver(builder)); + + builder.Routes.Add(route); + return builder; + } + + /// + /// Adds a route to the that only matches HTTP requests for the given + /// , , and . + /// + /// The . + /// The HTTP verb allowed by the route. + /// The route template. + /// The action to apply to the . + /// A reference to the after this operation has completed. + public static IRouteBuilder MapVerb( + this IRouteBuilder builder, + string verb, + string template, + Action action) + { + var nested = builder.ApplicationBuilder.New(); + action(nested); + return builder.MapVerb(verb, template, nested.Build()); + } + + private static IInlineConstraintResolver GetConstraintResolver(IRouteBuilder builder) + { + return builder.ServiceProvider.GetRequiredService(); + } + } +} diff --git a/src/Microsoft.AspNet.Routing.Extensions/project.json b/src/Microsoft.AspNet.Routing.Extensions/project.json new file mode 100644 index 0000000000..6d1132e6d4 --- /dev/null +++ b/src/Microsoft.AspNet.Routing.Extensions/project.json @@ -0,0 +1,19 @@ +{ + "description": "ASP.NET 5 extension methods for routing requests to application logic and for generating links.", + "version": "1.0.0-*", + "repository": { + "type": "git", + "url": "git://github.com/aspnet/routing" + }, + "compilationOptions": { + "warningsAsErrors": true, + "keyFile": "../../tools/Key.snk" + }, + "dependencies": { + "Microsoft.AspNet.Routing": "1.0.0-*" + }, + "frameworks": { + "net451": { }, + "dotnet5.4": { } + } +} diff --git a/src/Microsoft.AspNet.Routing/Constraints/HttpMethodRouteConstraint.cs b/src/Microsoft.AspNet.Routing/Constraints/HttpMethodRouteConstraint.cs index e6a248189a..d211f06ce5 100644 --- a/src/Microsoft.AspNet.Routing/Constraints/HttpMethodRouteConstraint.cs +++ b/src/Microsoft.AspNet.Routing/Constraints/HttpMethodRouteConstraint.cs @@ -11,14 +11,14 @@ namespace Microsoft.AspNet.Routing.Constraints /// /// Constrains the HTTP method of request or a route. /// - public class HttpRouteMethodConstraint : IRouteConstraint + public class HttpMethodRouteConstraint : IRouteConstraint { /// - /// Creates a new that accepts the HTTP methods specified + /// Creates a new that accepts the HTTP methods specified /// by . /// /// The allowed HTTP methods. - public HttpRouteMethodConstraint(params string[] allowedMethods) + public HttpMethodRouteConstraint(params string[] allowedMethods) { if (allowedMethods == null) { @@ -69,8 +69,8 @@ namespace Microsoft.AspNet.Routing.Constraints case RouteDirection.UrlGeneration: // We need to see if the user specified the HTTP method explicitly. Consider these two routes: // - // a) Route: template = "/{foo}", Constraints = { httpMethod = new HttpRouteMethodConstraint("GET") } - // b) Route: template = "/{foo}", Constraints = { httpMethod = new HttpRouteMethodConstraint("POST") } + // a) Route: template = "/{foo}", Constraints = { httpMethod = new HttpMethodRouteConstraint("GET") } + // b) Route: template = "/{foo}", Constraints = { httpMethod = new HttpMethodRouteConstraint("POST") } // // A user might know ahead of time that a URI he/she is generating might be used with a particular HTTP // method. If a URI will be used for an HTTP POST but we match on (a) while generating the URI, then diff --git a/src/Microsoft.AspNet.Routing/IRouteBuilder.cs b/src/Microsoft.AspNet.Routing/IRouteBuilder.cs index 37e1076b28..ed36d59d07 100644 --- a/src/Microsoft.AspNet.Routing/IRouteBuilder.cs +++ b/src/Microsoft.AspNet.Routing/IRouteBuilder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using Microsoft.AspNet.Builder; namespace Microsoft.AspNet.Routing { @@ -12,6 +13,11 @@ namespace Microsoft.AspNet.Routing /// public interface IRouteBuilder { + /// + /// Gets the . + /// + IApplicationBuilder ApplicationBuilder { get; } + /// /// Gets or sets the default that is used as a handler if an /// is added to the list of routes but does not specify its own. diff --git a/src/Microsoft.AspNet.Routing/RouteBuilderExtensions.cs b/src/Microsoft.AspNet.Routing/MapRouteRouteBuilderExtensions.cs similarity index 97% rename from src/Microsoft.AspNet.Routing/RouteBuilderExtensions.cs rename to src/Microsoft.AspNet.Routing/MapRouteRouteBuilderExtensions.cs index c825f4b4d1..21f2c26e70 100644 --- a/src/Microsoft.AspNet.Routing/RouteBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Routing/MapRouteRouteBuilderExtensions.cs @@ -2,9 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using Microsoft.AspNet.Routing; -using Microsoft.AspNet.Routing.Template; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNet.Builder @@ -12,7 +10,7 @@ namespace Microsoft.AspNet.Builder /// /// Provides extension methods for to add routes. /// - public static class RouteBuilderExtensions + public static class MapRouteRouteBuilderExtensions { /// /// Adds a route to the with the specified name and template. diff --git a/src/Microsoft.AspNet.Routing/RouteBuilder.cs b/src/Microsoft.AspNet.Routing/RouteBuilder.cs index 666add5418..5e54be1037 100644 --- a/src/Microsoft.AspNet.Routing/RouteBuilder.cs +++ b/src/Microsoft.AspNet.Routing/RouteBuilder.cs @@ -3,19 +3,35 @@ using System; using System.Collections.Generic; +using Microsoft.AspNet.Builder; namespace Microsoft.AspNet.Routing { public class RouteBuilder : IRouteBuilder { - public RouteBuilder() + public RouteBuilder(IApplicationBuilder applicationBuilder) + : this(applicationBuilder, defaultHandler: null) { + } + + public RouteBuilder(IApplicationBuilder applicationBuilder, IRouter defaultHandler) + { + if (applicationBuilder == null) + { + throw new ArgumentNullException(nameof(applicationBuilder)); + } + + ApplicationBuilder = applicationBuilder; + ServiceProvider = applicationBuilder.ApplicationServices; + Routes = new List(); } + public IApplicationBuilder ApplicationBuilder { get; } + public IRouter DefaultHandler { get; set; } - public IServiceProvider ServiceProvider { get; set; } + public IServiceProvider ServiceProvider { get; } public IList Routes { get; } diff --git a/test/Microsoft.AspNet.Routing.Extensions.Tests/Microsoft.AspNet.Routing.Extensions.Tests.xproj b/test/Microsoft.AspNet.Routing.Extensions.Tests/Microsoft.AspNet.Routing.Extensions.Tests.xproj new file mode 100644 index 0000000000..b3b50c729b --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Extensions.Tests/Microsoft.AspNet.Routing.Extensions.Tests.xproj @@ -0,0 +1,21 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + fd0151b4-7153-49a8-9d90-62a252420230 + Microsoft.AspNet.Routing.Extensions.Tests + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Routing.Extensions.Tests/RequestDelegateRouteBuilderExtensionsTest.cs b/test/Microsoft.AspNet.Routing.Extensions.Tests/RequestDelegateRouteBuilderExtensionsTest.cs new file mode 100644 index 0000000000..de8622ffe1 --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Extensions.Tests/RequestDelegateRouteBuilderExtensionsTest.cs @@ -0,0 +1,157 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Internal; +using Microsoft.AspNet.Routing; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Builder +{ + // These are really more like integration tests. They verify that these extensions + // add routes that behave as advertised. + public class RequestDelegateRouteBuilderExtensionsTest + { + private static readonly RequestDelegate NullHandler = (c) => Task.FromResult(0); + + public static TheoryData, Action> MatchingActions + { + get + { + return new TheoryData, Action>() + { + { b => { b.MapRoute("api/{id}", NullHandler); }, null }, + { b => { b.MapRoute("api/{id}", app => { }); }, null }, + + { b => { b.MapDelete("api/{id}", NullHandler); }, c => { c.Request.Method = "DELETE"; } }, + { b => { b.MapDelete("api/{id}", app => { }); }, c => { c.Request.Method = "DELETE"; } }, + { b => { b.MapGet("api/{id}", NullHandler); }, c => { c.Request.Method = "GET"; } }, + { b => { b.MapGet("api/{id}", app => { }); }, c => { c.Request.Method = "GET"; } }, + { b => { b.MapPost("api/{id}", NullHandler); }, c => { c.Request.Method = "POST"; } }, + { b => { b.MapPost("api/{id}", app => { }); }, c => { c.Request.Method = "POST"; } }, + { b => { b.MapPut("api/{id}", NullHandler); }, c => { c.Request.Method = "PUT"; } }, + { b => { b.MapPut("api/{id}", app => { }); }, c => { c.Request.Method = "PUT"; } }, + + { b => { b.MapVerb("PUT", "api/{id}", NullHandler); }, c => { c.Request.Method = "PUT"; } }, + { b => { b.MapVerb("PUT", "api/{id}", app => { }); }, c => { c.Request.Method = "PUT"; } }, + }; + } + } + + [Theory] + [MemberData(nameof(MatchingActions))] + public async Task Map_MatchesRequest( + Action routeSetup, + Action requestSetup) + { + // Arrange + var services = CreateServices(); + + var context = CreateRouteContext(services); + context.HttpContext.Request.Path = new PathString("/api/5"); + requestSetup?.Invoke(context.HttpContext); + + var builder = CreateRouteBuilder(services); + routeSetup(builder); + var route = builder.Build(); + + // Act + await route.RouteAsync(context); + + // Assert + Assert.Same(NullHandler, context.Handler); + } + + public static TheoryData, Action> NonmatchingActions + { + get + { + return new TheoryData, Action>() + { + { b => { b.MapRoute("api/{id}/extra", NullHandler); }, null }, + { b => { b.MapRoute("api/{id}/extra", app => { }); }, null }, + + { b => { b.MapDelete("api/{id}", NullHandler); }, c => { c.Request.Method = "GET"; } }, + { b => { b.MapDelete("api/{id}", app => { }); }, c => { c.Request.Method = "PUT"; } }, + { b => { b.MapDelete("api/{id}/extra", NullHandler); }, c => { c.Request.Method = "DELETE"; } }, + { b => { b.MapDelete("api/{id}/extra", app => { }); }, c => { c.Request.Method = "DELETE"; } }, + { b => { b.MapGet("api/{id}", NullHandler); }, c => { c.Request.Method = "PUT"; } }, + { b => { b.MapGet("api/{id}", app => { }); }, c => { c.Request.Method = "POST"; } }, + { b => { b.MapGet("api/{id}/extra", NullHandler); }, c => { c.Request.Method = "GET"; } }, + { b => { b.MapGet("api/{id}/extra", app => { }); }, c => { c.Request.Method = "GET"; } }, + { b => { b.MapPost("api/{id}", NullHandler); }, c => { c.Request.Method = "MEH"; } }, + { b => { b.MapPost("api/{id}", app => { }); }, c => { c.Request.Method = "DELETE"; } }, + { b => { b.MapPost("api/{id}/extra", NullHandler); }, c => { c.Request.Method = "POST"; } }, + { b => { b.MapPost("api/{id}/extra", app => { }); }, c => { c.Request.Method = "POST"; } }, + { b => { b.MapPut("api/{id}", NullHandler); }, c => { c.Request.Method = "BLEH"; } }, + { b => { b.MapPut("api/{id}", app => { }); }, c => { c.Request.Method = "HEAD"; } }, + { b => { b.MapPut("api/{id}/extra", NullHandler); }, c => { c.Request.Method = "PUT"; } }, + { b => { b.MapPut("api/{id}/extra", app => { }); }, c => { c.Request.Method = "PUT"; } }, + + { b => { b.MapVerb("PUT", "api/{id}", NullHandler); }, c => { c.Request.Method = "POST"; } }, + { b => { b.MapVerb("PUT", "api/{id}", app => { }); }, c => { c.Request.Method = "HEAD"; } }, + { b => { b.MapVerb("PUT", "api/{id}/extra", NullHandler); }, c => { c.Request.Method = "PUT"; } }, + { b => { b.MapVerb("PUT", "api/{id}/extra", app => { }); }, c => { c.Request.Method = "PUT"; } }, + }; + } + } + + [Theory] + [MemberData(nameof(NonmatchingActions))] + public async Task Map_DoesNotMatchRequest( + Action routeSetup, + Action requestSetup) + { + // Arrange + var services = CreateServices(); + + var context = CreateRouteContext(services); + context.HttpContext.Request.Path = new PathString("/api/5"); + requestSetup?.Invoke(context.HttpContext); + + var builder = CreateRouteBuilder(services); + routeSetup(builder); + var route = builder.Build(); + + // Act + await route.RouteAsync(context); + + // Assert + Assert.Null(context.Handler); + } + + private static IServiceProvider CreateServices() + { + var services = new ServiceCollection(); + services.AddRouting(); + services.AddLogging(); + return services.BuildServiceProvider(); + } + + private static RouteContext CreateRouteContext(IServiceProvider services) + { + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = services; + return new RouteContext(httpContext); + } + + private static IRouteBuilder CreateRouteBuilder(IServiceProvider services) + { + var applicationBuilder = new Mock(); + applicationBuilder.SetupAllProperties(); + + applicationBuilder + .Setup(b => b.New().Build()) + .Returns(NullHandler); + + applicationBuilder.Object.ApplicationServices = services; + + var routeBuilder = new RouteBuilder(applicationBuilder.Object); + return routeBuilder; + } + } +} diff --git a/test/Microsoft.AspNet.Routing.Extensions.Tests/project.json b/test/Microsoft.AspNet.Routing.Extensions.Tests/project.json new file mode 100644 index 0000000000..deed7b001e --- /dev/null +++ b/test/Microsoft.AspNet.Routing.Extensions.Tests/project.json @@ -0,0 +1,28 @@ +{ + "compilationOptions": { + "warningsAsErrors": true + }, + "dependencies": { + "Microsoft.AspNet.Http": "1.0.0-*", + "Microsoft.AspNet.Routing.Extensions": "1.0.0-*", + "Microsoft.AspNet.Testing": "1.0.0-*", + "Microsoft.Extensions.DependencyInjection": "1.0.0-*", + "Microsoft.Extensions.Logging.Testing": "1.0.0-*", + "xunit.runner.aspnet": "2.0.0-aspnet-*" + }, + "frameworks": { + "dnxcore50": { + "dependencies": { + "moq.netcore": "4.4.0-beta8" + } + }, + "dnx451": { + "dependencies": { + "Moq": "4.2.1312.1622" + } + } + }, + "commands": { + "test": "xunit.runner.aspnet" + } +} diff --git a/test/Microsoft.AspNet.Routing.Tests/Constraints/HttpMethodRouteConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/Constraints/HttpMethodRouteConstraintTests.cs index 33f580518b..649351fbc6 100644 --- a/test/Microsoft.AspNet.Routing.Tests/Constraints/HttpMethodRouteConstraintTests.cs +++ b/test/Microsoft.AspNet.Routing.Tests/Constraints/HttpMethodRouteConstraintTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Routing.Constraints public void HttpMethodRouteConstraint_IncomingRequest_AcceptsAllowedMethods(string httpMethod) { // Arrange - var constraint = new HttpRouteMethodConstraint("GET", "post"); + var constraint = new HttpMethodRouteConstraint("GET", "post"); var httpContext = new DefaultHttpContext(); httpContext.Request.Method = httpMethod; @@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Routing.Constraints public void HttpMethodRouteConstraint_IncomingRequest_RejectsOtherMethods(string httpMethod) { // Arrange - var constraint = new HttpRouteMethodConstraint("GET", "post"); + var constraint = new HttpMethodRouteConstraint("GET", "post"); var httpContext = new DefaultHttpContext(); httpContext.Request.Method = httpMethod; @@ -57,7 +57,7 @@ namespace Microsoft.AspNet.Routing.Constraints public void HttpMethodRouteConstraint_UrlGeneration_AcceptsAllowedMethods(string httpMethod) { // Arrange - var constraint = new HttpRouteMethodConstraint("GET", "post"); + var constraint = new HttpMethodRouteConstraint("GET", "post"); var httpContext = new DefaultHttpContext(); var route = Mock.Of(); @@ -77,7 +77,7 @@ namespace Microsoft.AspNet.Routing.Constraints public void HttpMethodRouteConstraint_UrlGeneration_RejectsOtherMethods(string httpMethod) { // Arrange - var constraint = new HttpRouteMethodConstraint("GET", "post"); + var constraint = new HttpMethodRouteConstraint("GET", "post"); var httpContext = new DefaultHttpContext(); var route = Mock.Of(); diff --git a/test/Microsoft.AspNet.Routing.Tests/RouteTest.cs b/test/Microsoft.AspNet.Routing.Tests/RouteTest.cs index db1d594020..13b21d78c8 100644 --- a/test/Microsoft.AspNet.Routing.Tests/RouteTest.cs +++ b/test/Microsoft.AspNet.Routing.Tests/RouteTest.cs @@ -9,8 +9,9 @@ using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; using Microsoft.AspNet.Routing.Constraints; using Microsoft.AspNet.Testing; -using Microsoft.Extensions.Logging.Testing; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Testing; using Moq; using Xunit; @@ -1490,15 +1491,14 @@ namespace Microsoft.AspNet.Routing private static IRouteBuilder CreateRouteBuilder() { - var routeBuilder = new RouteBuilder(); + var services = new ServiceCollection(); + services.AddSingleton(_inlineConstraintResolver); + var applicationBuilder = Mock.Of(); + applicationBuilder.ApplicationServices = services.BuildServiceProvider(); + + var routeBuilder = new RouteBuilder(applicationBuilder); routeBuilder.DefaultHandler = new RouteHandler(NullHandler); - - var serviceProviderMock = new Mock(); - serviceProviderMock.Setup(o => o.GetService(typeof(IInlineConstraintResolver))) - .Returns(_inlineConstraintResolver); - routeBuilder.ServiceProvider = serviceProviderMock.Object; - return routeBuilder; } diff --git a/test/Microsoft.AspNet.Routing.Tests/TemplateParserDefaultValuesTests.cs b/test/Microsoft.AspNet.Routing.Tests/TemplateParserDefaultValuesTests.cs index fbea8c950d..b4af45c8ad 100644 --- a/test/Microsoft.AspNet.Routing.Tests/TemplateParserDefaultValuesTests.cs +++ b/test/Microsoft.AspNet.Routing.Tests/TemplateParserDefaultValuesTests.cs @@ -96,26 +96,28 @@ namespace Microsoft.AspNet.Routing.Tests var routeBuilder = CreateRouteBuilder(); // Act & Assert - var ex = Assert.Throws( - () => routeBuilder.MapRoute("mockName", - "{controller}/{action}/{id:int=12?}", - defaults: new { id = 13 }, - constraints: null)); + var ex = Assert.Throws(() => + { + routeBuilder.MapRoute( + "mockName", + "{controller}/{action}/{id:int=12?}", + defaults: new { id = 13 }, + constraints: null); + }); Assert.Equal(message, ex.Message); } private static IRouteBuilder CreateRouteBuilder() { - var routeBuilder = new RouteBuilder(); + var services = new ServiceCollection(); + services.AddSingleton(_inlineConstraintResolver); - routeBuilder.DefaultHandler = new Mock().Object; - - var serviceProviderMock = new Mock(); - serviceProviderMock.Setup(o => o.GetService(typeof(IInlineConstraintResolver))) - .Returns(_inlineConstraintResolver); - routeBuilder.ServiceProvider = serviceProviderMock.Object; + var applicationBuilder = Mock.Of(); + applicationBuilder.ApplicationServices = services.BuildServiceProvider(); + var routeBuilder = new RouteBuilder(applicationBuilder); + routeBuilder.DefaultHandler = Mock.Of(); return routeBuilder; }