diff --git a/README.textile b/README.textile index b00ff92..9a53a0d 100644 --- a/README.textile +++ b/README.textile @@ -1,8 +1,76 @@ -h2. Please refer to "attributerouting.net":http://attributerouting.net/ for documentation. +h1. AttributeRouting -*If you encounter any issues using this library, please log them in the "issue tracker":https://github.com//issues. Thanks.* +h2. Getting Started -h3. Changelog +"Read the docs":http://mccalltd.github.io/AttributeRouting/. + +If you encounter any issues using this library, please log them in the "issue tracker":https://github.com//issues. Note that this repository has been unmaintained since Microsoft baked AttributeRouting into the framework. If you are interested in maintaining this project, please let me know. + +h2. Changelog + +_3.5.6_ + +* #219 - BUG FIX: with handling of querystring optionals. +* Now applying default constraints to matching query string params. + +_3.5.5_ + +* #218 - BUG FIX: fixed handling of querystring default values. + +_3.5.4_ + +* #217 - Improved perf of ObjectExtensions.SafeGet. Thanks youssefm! +* BUG FIX: fixed bug in handling of default values with querystring params. + +_3.5.3_ + +* #216 - testing optional constraint for mvc against null/empty in addition to UrlParameter.Optional. +* #211 - parsing defaults from complete url, including areas and prefixes. + +_3.5.2_ + +* #214 - BUG FIX: fixed thread safety issues encountered when dealing with query string route constraints." + +_3.5.1_ + +* #199 - when applying inbound http method constraint, now checking the unvalidated form/query collection. +* #206 - changed the name of the app start files to prevent ns conflicts. + +_3.5_ + +* #191 - Enabled in-memory hosting of web-hosted web api routes. +* Various perf enhancements and code reorganization. + +_3.4.2_ + +* #192 - BUG FIX: Optional parameters weren't working in asp.net web api action urls. +* #183 - Bllowing route conventions to be inherited from base controllers. +* #182 - Inlined scripts required by routes.axd +* #180 - New route constraint EnumValueConstraint for matching enums by value. + +_3.4.1_ + +* #175 - fixed bug in getting the default area name for a controller. + +_3.4_ + +* #124 - Now supporting custom IRouteHandler in web-host scenario. Also supporting custom HttpMessageHandler for Web API. +* #146 - BUG FIX: AR's custom http method constraint was interfering with IHostBufferPolicySelector, because it was checking for the incoming http method too early in the pipeline. +* #155 - Can now specify multiple route prefixes on a controller. Every action will get each prefix. You can control precedence of the prefixes by using the Precedence property. +* #156 - Added action method to route data tokens. +* #161 - BUG FIX: Url generation was bonking for routes that included a querystring route param constraint. +* #162 - Modified design of route convention attribute, adding facility for specifying an area attribute for a controller. Also opened up {controller} and {action} url params as special params that will be replaced with controller and action values from the defaults collection for the route. +* #164 - Added default ctors to RoutePrefixAttribute, RouteAreaAttribute, and the AttributeRouting.Web.Mvc route attributes. These default ctors will use a convention to get their URL components: RoutePrefix - will use the controller name; RouteArea - will use the last section of the controller's namespace; and the route attributes - will use the action name by default. +* #165 - Added two flags to the route attributes: IgnoreRoutePrefix and IgnoreAreaUrl. These flags control whether to prepend route prefixes or area urls to the generated route url. + +_3.3_ + +* #153 - fixed bad parsing of regex route constraint patterns that use a comma. +* #132 - basic support for constraints in the querystring. +* #152 - added support for specifying that query param simply exists, without constraining by anything else. +* #154 - added support for querystring constraint description in routes.axd. +* #159 - added compiled regex in RegexRouteConstraint to improve performance. +* #150 - changed IAttributeRouteFactory method to return multiple routes. _3.2_ @@ -205,4 +273,4 @@ _v0.6.4033_ * Added IsAbsoluteUrl property to the RouteAttributes, which prevents the RouteAreaAttribute and RoutePrefixAttribute from prefixing the URL for a route. This can be handy for legacy URL support. * Added Precedence property to the RouteAtrributes, which allows you to specify the precedence of a route among all the routes defined _for the controller_. * New and improved output from the LogRoutesHandler. -* New RouteConventionAttribute, which allows you to create your own convention-based route generator for actions within a controller. There is also a RestfulRouteConventionAttribute, which automatically creates RESTful routes for standard actions in your controller. \ No newline at end of file +* New RouteConventionAttribute, which allows you to create your own convention-based route generator for actions within a controller. There is also a RestfulRouteConventionAttribute, which automatically creates RESTful routes for standard actions in your controller. diff --git a/default.ps1 b/default.ps1 index a3054e2..9398a54 100644 --- a/default.ps1 +++ b/default.ps1 @@ -51,7 +51,7 @@ Task Test { Task NugetPack { Clean-Directory $nupkg_dir - Get-ChildItem $nuspec_dir -Directory | foreach { Create-Nupkg $_ } + Get-ChildItem "$nuspec_dir\*" -Include "*.nutrans" -Recurse | foreach { Create-Nupkg $_ } } Task NugetPush { @@ -69,7 +69,8 @@ function Clean-Directory ($dir) { New-Item $dir -ItemType Directory | Out-Null } -function Create-Nupkg ($name) { +function Create-Nupkg ($nutrans_path) { + $name = [System.IO.Path]::GetFileNameWithoutExtension($nutrans_path) $nuspec = Create-Nuspec $name Write-Host "Creating nupkg for $name" -ForegroundColor Green Exec { &$nuget pack $nuspec -Version $version -OutputDirectory $nupkg_dir } diff --git a/nuget/AttributeRouting.Shared.nuspec b/nuget/AttributeRouting.Shared.nuspec index 5ce361d..356533b 100644 --- a/nuget/AttributeRouting.Shared.nuspec +++ b/nuget/AttributeRouting.Shared.nuspec @@ -6,6 +6,6 @@ Tim McCall Tim McCall © 2012 Tim McCall - https://github.com/mccalltd/AttributeRouting/wiki + http://attributerouting.net \ No newline at end of file diff --git a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.WebApi.Hosted.nutrans b/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.WebApi.Hosted.nutrans index 36bc6ff..19844b3 100644 --- a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.WebApi.Hosted.nutrans +++ b/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.WebApi.Hosted.nutrans @@ -9,15 +9,15 @@ - + - - + + \ No newline at end of file diff --git a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.cs.pp b/nuget/AttributeRouting.WebApi.Hosted/AttributeRoutingConfig.cs.pp similarity index 86% rename from nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.cs.pp rename to nuget/AttributeRouting.WebApi.Hosted/AttributeRoutingConfig.cs.pp index d8a30d8..6ffbb81 100644 --- a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.cs.pp +++ b/nuget/AttributeRouting.WebApi.Hosted/AttributeRoutingConfig.cs.pp @@ -1,13 +1,14 @@ using System.Web.Http.SelfHost; using AttributeRouting.Web.Http.SelfHost; -namespace $rootnamespace$ { - public static class AttributeRouting { - +namespace $rootnamespace$ +{ + public static class AttributeRoutingConfig + { // Call this static method from a start up class in your applicaton (e.g.Program.cs) // Pass in the configuration you're using for your self-hosted Web API - public static void RegisterRoutes(HttpSelfHostConfiguration config) { - + public static void RegisterRoutes(HttpSelfHostConfiguration config) + { // See http://github.com/mccalltd/AttributeRouting/wiki for more options. // To debug routes locally, you can log to Console.Out (or any other TextWriter) like so: // config.Routes.Cast().LogTo(Console.Out); diff --git a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.vb.pp b/nuget/AttributeRouting.WebApi.Hosted/AttributeRoutingConfig.vb.pp similarity index 94% rename from nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.vb.pp rename to nuget/AttributeRouting.WebApi.Hosted/AttributeRoutingConfig.vb.pp index 870bf2d..5d1921c 100644 --- a/nuget/AttributeRouting.WebApi.Hosted/AttributeRouting.vb.pp +++ b/nuget/AttributeRouting.WebApi.Hosted/AttributeRoutingConfig.vb.pp @@ -2,7 +2,7 @@ Imports AttributeRouting.Web.Http.SelfHost Namespace $rootnamespace$ - Public Class AttributeRouting + Public Class AttributeRoutingConfig ' Call this static method from a start up class in your applicaton (e.g.Program.cs) ' Pass in the configuration you're using for your self-hosted Web API diff --git a/nuget/AttributeRouting.WebApi.Hosted/install.ps1 b/nuget/AttributeRouting.WebApi.Hosted/install.ps1 index 0871aa9..7ff35bd 100644 --- a/nuget/AttributeRouting.WebApi.Hosted/install.ps1 +++ b/nuget/AttributeRouting.WebApi.Hosted/install.ps1 @@ -2,7 +2,7 @@ # Remove the App_Start file for other languages if ($project.Type -eq "C#") { - $project.ProjectItems.Item("AttributeRouting.vb").Delete(); + $project.ProjectItems.Item("AttributeRoutingConfig.vb").Delete(); } elseif ($project.Type -eq "VB.NET") { - $project.ProjectItems.Item("AttributeRouting.cs").Delete(); + $project.ProjectItems.Item("AttributeRoutingConfig.cs").Delete(); } \ No newline at end of file diff --git a/nuget/AttributeRouting.WebApi/AttributeRouting.WebApi.nutrans b/nuget/AttributeRouting.WebApi/AttributeRouting.WebApi.nutrans index 9669fa3..6238f2d 100644 --- a/nuget/AttributeRouting.WebApi/AttributeRouting.WebApi.nutrans +++ b/nuget/AttributeRouting.WebApi/AttributeRouting.WebApi.nutrans @@ -10,7 +10,7 @@ - + @@ -18,8 +18,8 @@ - - + + diff --git a/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.cs.pp b/nuget/AttributeRouting.WebApi/AttributeRoutingHttpConfig.cs.pp similarity index 63% rename from nuget/AttributeRouting.WebApi/AttributeRoutingHttp.cs.pp rename to nuget/AttributeRouting.WebApi/AttributeRoutingHttpConfig.cs.pp index 03bdcfc..e0b9284 100644 --- a/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.cs.pp +++ b/nuget/AttributeRouting.WebApi/AttributeRoutingHttpConfig.cs.pp @@ -1,19 +1,22 @@ using System.Web.Http; using AttributeRouting.Web.Http.WebHost; -[assembly: WebActivator.PreApplicationStartMethod(typeof($rootnamespace$.App_Start.AttributeRoutingHttp), "Start")] +[assembly: WebActivatorEx.PreApplicationStartMethod(typeof($rootnamespace$.AttributeRoutingHttpConfig), "Start")] -namespace $rootnamespace$.App_Start { - public static class AttributeRoutingHttp { - public static void RegisterRoutes(HttpRouteCollection routes) { - +namespace $rootnamespace$ +{ + public static class AttributeRoutingHttpConfig + { + public static void RegisterRoutes(HttpRouteCollection routes) + { // See http://github.com/mccalltd/AttributeRouting/wiki for more options. // To debug routes locally using the built in ASP.NET development server, go to /routes.axd routes.MapHttpAttributeRoutes(); } - public static void Start() { + public static void Start() + { RegisterRoutes(GlobalConfiguration.Configuration.Routes); } } diff --git a/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.vb.pp b/nuget/AttributeRouting.WebApi/AttributeRoutingHttpConfig.vb.pp similarity index 73% rename from nuget/AttributeRouting.WebApi/AttributeRoutingHttp.vb.pp rename to nuget/AttributeRouting.WebApi/AttributeRoutingHttpConfig.vb.pp index 676e9fb..679154a 100644 --- a/nuget/AttributeRouting.WebApi/AttributeRoutingHttp.vb.pp +++ b/nuget/AttributeRouting.WebApi/AttributeRoutingHttpConfig.vb.pp @@ -1,10 +1,10 @@ Imports System.Web.Http Imports AttributeRouting.Web.Http.WebHost - + -Namespace $rootnamespace$.App_Start - Public Class AttributeRoutingHttp +Namespace $rootnamespace$ + Public Class AttributeRoutingHttpConfig Public Shared Sub RegisterRoutes(routes As HttpRouteCollection) ' See http://github.com/mccalltd/AttributeRouting/wiki for more options. diff --git a/nuget/AttributeRouting.WebApi/install.ps1 b/nuget/AttributeRouting.WebApi/install.ps1 index 93dbf73..b979922 100644 --- a/nuget/AttributeRouting.WebApi/install.ps1 +++ b/nuget/AttributeRouting.WebApi/install.ps1 @@ -4,7 +4,7 @@ $appStartTemplatesFolder = $project.ProjectItems.Item("App_Start"); # Remove the App_Start file for other languages if ($project.Type -eq "C#") { - $appStartTemplatesFolder.ProjectItems.Item("AttributeRoutingHttp.vb").Delete(); + $appStartTemplatesFolder.ProjectItems.Item("AttributeRoutingHttpConfig.vb").Delete(); } elseif ($project.Type -eq "VB.NET") { - $appStartTemplatesFolder.ProjectItems.Item("AttributeRoutingHttp.cs").Delete(); + $appStartTemplatesFolder.ProjectItems.Item("AttributeRoutingHttpConfig.cs").Delete(); } \ No newline at end of file diff --git a/nuget/AttributeRouting/AttributeRouting.nutrans b/nuget/AttributeRouting/AttributeRouting.nutrans index 6adfdb6..5492d23 100644 --- a/nuget/AttributeRouting/AttributeRouting.nutrans +++ b/nuget/AttributeRouting/AttributeRouting.nutrans @@ -8,7 +8,8 @@ - + + @@ -16,8 +17,8 @@ - - + + diff --git a/nuget/AttributeRouting/AttributeRouting.cs.pp b/nuget/AttributeRouting/AttributeRoutingConfig.cs.pp similarity index 53% rename from nuget/AttributeRouting/AttributeRouting.cs.pp rename to nuget/AttributeRouting/AttributeRoutingConfig.cs.pp index 85cda11..1bd8a72 100644 --- a/nuget/AttributeRouting/AttributeRouting.cs.pp +++ b/nuget/AttributeRouting/AttributeRoutingConfig.cs.pp @@ -1,19 +1,22 @@ using System.Web.Routing; using AttributeRouting.Web.Mvc; -[assembly: WebActivator.PreApplicationStartMethod(typeof($rootnamespace$.App_Start.AttributeRouting), "Start")] +[assembly: WebActivatorEx.PreApplicationStartMethod(typeof($rootnamespace$.AttributeRoutingConfig), "Start")] -namespace $rootnamespace$.App_Start { - public static class AttributeRouting { - public static void RegisterRoutes(RouteCollection routes) { - +namespace $rootnamespace$ +{ + public static class AttributeRoutingConfig + { + public static void RegisterRoutes(RouteCollection routes) + { // See http://github.com/mccalltd/AttributeRouting/wiki for more options. // To debug routes locally using the built in ASP.NET development server, go to /routes.axd routes.MapAttributeRoutes(); } - public static void Start() { + public static void Start() + { RegisterRoutes(RouteTable.Routes); } } diff --git a/nuget/AttributeRouting/AttributeRouting.vb.pp b/nuget/AttributeRouting/AttributeRoutingConfig.vb.pp similarity index 72% rename from nuget/AttributeRouting/AttributeRouting.vb.pp rename to nuget/AttributeRouting/AttributeRoutingConfig.vb.pp index 160e322..515416a 100644 --- a/nuget/AttributeRouting/AttributeRouting.vb.pp +++ b/nuget/AttributeRouting/AttributeRoutingConfig.vb.pp @@ -1,10 +1,10 @@ Imports System.Web.Routing Imports AttributeRouting.Web.Mvc - + -Namespace $rootnamespace$.App_Start - Public Class AttributeRouting +Namespace $rootnamespace$ + Public Class AttributeRoutingConfig Public Shared Sub RegisterRoutes(routes As RouteCollection) ' See http://github.com/mccalltd/AttributeRouting/wiki for more options. diff --git a/nuget/AttributeRouting/install.ps1 b/nuget/AttributeRouting/install.ps1 index b97e57d..d1b6d64 100644 --- a/nuget/AttributeRouting/install.ps1 +++ b/nuget/AttributeRouting/install.ps1 @@ -4,7 +4,7 @@ $appStartTemplatesFolder = $project.ProjectItems.Item("App_Start"); # Remove the App_Start file for other languages if ($project.Type -eq "C#") { - $appStartTemplatesFolder.ProjectItems.Item("AttributeRouting.vb").Delete(); + $appStartTemplatesFolder.ProjectItems.Item("AttributeRoutingConfig.vb").Delete(); } elseif ($project.Type -eq "VB.NET") { - $appStartTemplatesFolder.ProjectItems.Item("AttributeRouting.cs").Delete(); + $appStartTemplatesFolder.ProjectItems.Item("AttributeRoutingConfig.cs").Delete(); } \ No newline at end of file diff --git a/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj b/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj index 2c0000a..38851ae 100644 --- a/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj +++ b/src/AttributeRouting.Specs/AttributeRouting.Specs.csproj @@ -42,17 +42,19 @@ ..\packages\Moq.4.0.10827\lib\NET40\Moq.dll + False ..\packages\MvcContrib.Mvc3.TestHelper-ci.3.0.100.0\lib\MvcContrib.TestHelper.dll False - ..\packages\Newtonsoft.Json.4.5.9\lib\net40\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll - + False - ..\packages\NUnit.2.6.0.12054\lib\nunit.framework.dll + ..\packages\NUnit.2.6.2\lib\nunit.framework.dll + False ..\packages\RhinoMocks.3.6.1\lib\net\Rhino.Mocks.dll @@ -81,9 +83,9 @@ - + False - ..\packages\SpecFlow.1.8.1\lib\net35\TechTalk.SpecFlow.dll + ..\packages\SpecFlow.1.9.0\lib\net35\TechTalk.SpecFlow.dll @@ -140,17 +142,16 @@ - + + - - @@ -160,7 +161,7 @@ - + @@ -169,6 +170,7 @@ + @@ -221,37 +223,34 @@ SpecFlowSingleFileGenerator StandardUsage.feature.cs - - Designer - + - {246F7AEC-9429-4FBB-8747-92A3F025C711} + {246f7aec-9429-4fbb-8747-92a3f025c711} AttributeRouting.Web.Http.SelfHost - {A018FEC5-45F8-44FB-BB6C-33697B418434} + {a018fec5-45f8-44fb-bb6c-33697b418434} AttributeRouting.Web.Http.WebHost - {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D} + {ccde9ad7-3822-4b0b-aa19-df6698a85d3d} AttributeRouting.Web.Http - - {C91C065B-A821-4890-9F31-F9E245D804D1} - AttributeRouting.Web - - {4604C450-EBF8-4A7F-BD3A-A24655C41FA4} + {4604c450-ebf8-4a7f-bd3a-a24655c41fa4} AttributeRouting.Web.Mvc + + {c91c065b-a821-4890-9f31-f9e245d804d1} + AttributeRouting.Web + - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4} + {871a79cf-c705-4c6b-8938-f9aa1e02aea4} AttributeRouting - + \ No newline at end of file diff --git a/src/AttributeRouting.Specs/packages.config b/src/AttributeRouting.Specs/packages.config index 929efc5..e979864 100644 --- a/src/AttributeRouting.Specs/packages.config +++ b/src/AttributeRouting.Specs/packages.config @@ -1,15 +1,14 @@  - - - - - - - + + + + + + \ No newline at end of file diff --git a/src/AttributeRouting.Tests.SelfHost/AttributeRouting.Tests.SelfHost.csproj b/src/AttributeRouting.Tests.SelfHost/AttributeRouting.Tests.SelfHost.csproj index 41df9f5..ca39948 100644 --- a/src/AttributeRouting.Tests.SelfHost/AttributeRouting.Tests.SelfHost.csproj +++ b/src/AttributeRouting.Tests.SelfHost/AttributeRouting.Tests.SelfHost.csproj @@ -42,7 +42,8 @@ - ..\packages\Newtonsoft.Json.4.5.8\lib\net40\Newtonsoft.Json.dll + False + ..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll @@ -59,7 +60,7 @@ ..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll - ..\packages\Microsoft.AspNet.WebApi.SelfHost.4.0.20710.0\lib\net40\System.Web.Http.SelfHost.dll + ..\packages\Microsoft.AspNet.WebApi.SelfHost.4.0.20918.0\lib\net40\System.Web.Http.SelfHost.dll @@ -73,24 +74,24 @@ + + + + - {246F7AEC-9429-4FBB-8747-92A3F025C711} + {246f7aec-9429-4fbb-8747-92a3f025c711} AttributeRouting.Web.Http.SelfHost - {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D} + {ccde9ad7-3822-4b0b-aa19-df6698a85d3d} AttributeRouting.Web.Http - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4} + {871a79cf-c705-4c6b-8938-f9aa1e02aea4} AttributeRouting - - - - - - - - - - - - - - - - - - - - + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/AttributeRouting.Tests.Web/Web.config b/src/AttributeRouting.Tests.Web/Web.config index d729656..032c063 100644 --- a/src/AttributeRouting.Tests.Web/Web.config +++ b/src/AttributeRouting.Tests.Web/Web.config @@ -49,14 +49,14 @@ - + - + diff --git a/src/AttributeRouting.Tests.Web/packages.config b/src/AttributeRouting.Tests.Web/packages.config index ba9ece5..76f6b20 100644 --- a/src/AttributeRouting.Tests.Web/packages.config +++ b/src/AttributeRouting.Tests.Web/packages.config @@ -5,5 +5,5 @@ - + \ No newline at end of file diff --git a/src/AttributeRouting.VS2010.sln b/src/AttributeRouting.VS2010.sln deleted file mode 100644 index d7f2999..0000000 --- a/src/AttributeRouting.VS2010.sln +++ /dev/null @@ -1,155 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributeRouting", "AttributeRouting\AttributeRouting.csproj", "{871A79CF-C705-4C6B-8938-F9AA1E02AEA4}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributeRouting.Specs", "AttributeRouting.Specs\AttributeRouting.Specs.csproj", "{E832A82B-2302-4113-83E3-261446E22C87}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributeRouting.Tests.Web", "AttributeRouting.Tests.Web\AttributeRouting.Tests.Web.csproj", "{486087C6-E95B-4FE2-8263-7B6B2DF81888}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{38404E6F-56AC-458E-B0A6-3620FEA10A5C}" - ProjectSection(SolutionItems) = preProject - .nuget\NuGet.exe = .nuget\NuGet.exe - .nuget\NuGet.targets = .nuget\NuGet.targets - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributeRouting.Web.Http.WebHost", "AttributeRouting.Web.Http.WebHost\AttributeRouting.Web.Http.WebHost.csproj", "{A018FEC5-45F8-44FB-BB6C-33697B418434}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributeRouting.Web", "AttributeRouting.Web\AttributeRouting.Web.csproj", "{C91C065B-A821-4890-9F31-F9E245D804D1}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{840281A8-8870-417F-9B24-ED0E866608A2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributeRouting.Web.Mvc", "AttributeRouting.Web.Mvc\AttributeRouting.Web.Mvc.csproj", "{4604C450-EBF8-4A7F-BD3A-A24655C41FA4}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributeRouting.Web.Http.SelfHost", "AttributeRouting.Web.Http.SelfHost\AttributeRouting.Web.Http.SelfHost.csproj", "{246F7AEC-9429-4FBB-8747-92A3F025C711}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributeRouting.Tests.SelfHost", "AttributeRouting.Tests.SelfHost\AttributeRouting.Tests.SelfHost.csproj", "{0D2A13E6-A092-402E-B8FE-035F3A620B86}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AttributeRouting.Web.Http", "AttributeRouting.Web.Http\AttributeRouting.Web.Http.csproj", "{CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{DE34F753-6251-493F-902B-D520D655F69A}" - ProjectSection(SolutionItems) = preProject - AttributeRouting.Shared.nuspec = AttributeRouting.Shared.nuspec - SharedAssemblyInfo.cs = SharedAssemblyInfo.cs - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{01EC4F0A-A27A-45B8-9DB5-BA447A9F4A6F}" - ProjectSection(SolutionItems) = preProject - ..\build.bat = ..\build.bat - ..\build.xml = ..\build.xml - EndProjectSection -EndProject -Global - GlobalSection(SubversionScc) = preSolution - Svn-Managed = True - Manager = AnkhSVN - Subversion Support for Visual Studio - EndGlobalSection - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|Mixed Platforms = Release|Mixed Platforms - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4}.Debug|x86.ActiveCfg = Debug|Any CPU - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4}.Release|Any CPU.Build.0 = Release|Any CPU - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4}.Release|x86.ActiveCfg = Release|Any CPU - {E832A82B-2302-4113-83E3-261446E22C87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E832A82B-2302-4113-83E3-261446E22C87}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E832A82B-2302-4113-83E3-261446E22C87}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {E832A82B-2302-4113-83E3-261446E22C87}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {E832A82B-2302-4113-83E3-261446E22C87}.Debug|x86.ActiveCfg = Debug|Any CPU - {E832A82B-2302-4113-83E3-261446E22C87}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E832A82B-2302-4113-83E3-261446E22C87}.Release|Any CPU.Build.0 = Release|Any CPU - {E832A82B-2302-4113-83E3-261446E22C87}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {E832A82B-2302-4113-83E3-261446E22C87}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {E832A82B-2302-4113-83E3-261446E22C87}.Release|x86.ActiveCfg = Release|Any CPU - {486087C6-E95B-4FE2-8263-7B6B2DF81888}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {486087C6-E95B-4FE2-8263-7B6B2DF81888}.Debug|Any CPU.Build.0 = Debug|Any CPU - {486087C6-E95B-4FE2-8263-7B6B2DF81888}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {486087C6-E95B-4FE2-8263-7B6B2DF81888}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {486087C6-E95B-4FE2-8263-7B6B2DF81888}.Debug|x86.ActiveCfg = Debug|Any CPU - {486087C6-E95B-4FE2-8263-7B6B2DF81888}.Release|Any CPU.ActiveCfg = Release|Any CPU - {486087C6-E95B-4FE2-8263-7B6B2DF81888}.Release|Any CPU.Build.0 = Release|Any CPU - {486087C6-E95B-4FE2-8263-7B6B2DF81888}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {486087C6-E95B-4FE2-8263-7B6B2DF81888}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {486087C6-E95B-4FE2-8263-7B6B2DF81888}.Release|x86.ActiveCfg = Release|Any CPU - {A018FEC5-45F8-44FB-BB6C-33697B418434}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A018FEC5-45F8-44FB-BB6C-33697B418434}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A018FEC5-45F8-44FB-BB6C-33697B418434}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {A018FEC5-45F8-44FB-BB6C-33697B418434}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {A018FEC5-45F8-44FB-BB6C-33697B418434}.Debug|x86.ActiveCfg = Debug|Any CPU - {A018FEC5-45F8-44FB-BB6C-33697B418434}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A018FEC5-45F8-44FB-BB6C-33697B418434}.Release|Any CPU.Build.0 = Release|Any CPU - {A018FEC5-45F8-44FB-BB6C-33697B418434}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {A018FEC5-45F8-44FB-BB6C-33697B418434}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {A018FEC5-45F8-44FB-BB6C-33697B418434}.Release|x86.ActiveCfg = Release|Any CPU - {C91C065B-A821-4890-9F31-F9E245D804D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C91C065B-A821-4890-9F31-F9E245D804D1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C91C065B-A821-4890-9F31-F9E245D804D1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {C91C065B-A821-4890-9F31-F9E245D804D1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {C91C065B-A821-4890-9F31-F9E245D804D1}.Debug|x86.ActiveCfg = Debug|Any CPU - {C91C065B-A821-4890-9F31-F9E245D804D1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C91C065B-A821-4890-9F31-F9E245D804D1}.Release|Any CPU.Build.0 = Release|Any CPU - {C91C065B-A821-4890-9F31-F9E245D804D1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {C91C065B-A821-4890-9F31-F9E245D804D1}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {C91C065B-A821-4890-9F31-F9E245D804D1}.Release|x86.ActiveCfg = Release|Any CPU - {4604C450-EBF8-4A7F-BD3A-A24655C41FA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4604C450-EBF8-4A7F-BD3A-A24655C41FA4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4604C450-EBF8-4A7F-BD3A-A24655C41FA4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {4604C450-EBF8-4A7F-BD3A-A24655C41FA4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {4604C450-EBF8-4A7F-BD3A-A24655C41FA4}.Debug|x86.ActiveCfg = Debug|Any CPU - {4604C450-EBF8-4A7F-BD3A-A24655C41FA4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4604C450-EBF8-4A7F-BD3A-A24655C41FA4}.Release|Any CPU.Build.0 = Release|Any CPU - {4604C450-EBF8-4A7F-BD3A-A24655C41FA4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {4604C450-EBF8-4A7F-BD3A-A24655C41FA4}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {4604C450-EBF8-4A7F-BD3A-A24655C41FA4}.Release|x86.ActiveCfg = Release|Any CPU - {246F7AEC-9429-4FBB-8747-92A3F025C711}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {246F7AEC-9429-4FBB-8747-92A3F025C711}.Debug|Any CPU.Build.0 = Debug|Any CPU - {246F7AEC-9429-4FBB-8747-92A3F025C711}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {246F7AEC-9429-4FBB-8747-92A3F025C711}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {246F7AEC-9429-4FBB-8747-92A3F025C711}.Debug|x86.ActiveCfg = Debug|Any CPU - {246F7AEC-9429-4FBB-8747-92A3F025C711}.Release|Any CPU.ActiveCfg = Release|Any CPU - {246F7AEC-9429-4FBB-8747-92A3F025C711}.Release|Any CPU.Build.0 = Release|Any CPU - {246F7AEC-9429-4FBB-8747-92A3F025C711}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {246F7AEC-9429-4FBB-8747-92A3F025C711}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {246F7AEC-9429-4FBB-8747-92A3F025C711}.Release|x86.ActiveCfg = Release|Any CPU - {0D2A13E6-A092-402E-B8FE-035F3A620B86}.Debug|Any CPU.ActiveCfg = Debug|x86 - {0D2A13E6-A092-402E-B8FE-035F3A620B86}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {0D2A13E6-A092-402E-B8FE-035F3A620B86}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {0D2A13E6-A092-402E-B8FE-035F3A620B86}.Debug|x86.ActiveCfg = Debug|x86 - {0D2A13E6-A092-402E-B8FE-035F3A620B86}.Debug|x86.Build.0 = Debug|x86 - {0D2A13E6-A092-402E-B8FE-035F3A620B86}.Release|Any CPU.ActiveCfg = Release|x86 - {0D2A13E6-A092-402E-B8FE-035F3A620B86}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {0D2A13E6-A092-402E-B8FE-035F3A620B86}.Release|Mixed Platforms.Build.0 = Release|x86 - {0D2A13E6-A092-402E-B8FE-035F3A620B86}.Release|x86.ActiveCfg = Release|x86 - {0D2A13E6-A092-402E-B8FE-035F3A620B86}.Release|x86.Build.0 = Release|x86 - {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Debug|x86.ActiveCfg = Debug|Any CPU - {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Release|Any CPU.Build.0 = Release|Any CPU - {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D}.Release|x86.ActiveCfg = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {486087C6-E95B-4FE2-8263-7B6B2DF81888} = {840281A8-8870-417F-9B24-ED0E866608A2} - {E832A82B-2302-4113-83E3-261446E22C87} = {840281A8-8870-417F-9B24-ED0E866608A2} - {0D2A13E6-A092-402E-B8FE-035F3A620B86} = {840281A8-8870-417F-9B24-ED0E866608A2} - EndGlobalSection -EndGlobal diff --git a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj index 9e55fab..e2895c9 100644 --- a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj +++ b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.Web.Http.SelfHost.csproj @@ -1,102 +1,104 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {246F7AEC-9429-4FBB-8747-92A3F025C711} - Library - Properties - AttributeRouting.Web.Http.SelfHost - AttributeRouting.Web.Http.SelfHost - v4.0 - 512 - ..\ - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\AttributeRouting.Web.Http.SelfHost.xml - 1591, 1587 - - - - ..\packages\Newtonsoft.Json.4.5.8\lib\net40\Newtonsoft.Json.dll - - - - - ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll - - - ..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll - - - ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll - - - ..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll - - - ..\packages\Microsoft.AspNet.WebApi.SelfHost.4.0.20710.0\lib\net40\System.Web.Http.SelfHost.dll - - - - - - - - - - SharedAssemblyInfo.cs - - - - - - - - - - - - - - {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D} - AttributeRouting.Web.Http - - - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4} - AttributeRouting - - - - - - - + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {246F7AEC-9429-4FBB-8747-92A3F025C711} + Library + Properties + AttributeRouting.Web.Http.SelfHost + AttributeRouting.Web.Http.SelfHost + v4.0 + 512 + ..\ + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + bin\Release\AttributeRouting.Web.Http.SelfHost.xml + 1591, 1587 + + + true + + + AttributeRouting.snk + + + + False + ..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll + + + + + ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll + + + ..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll + + + ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll + + + ..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll + + + ..\packages\Microsoft.AspNet.WebApi.SelfHost.4.0.20918.0\lib\net40\System.Web.Http.SelfHost.dll + + + + + + + + + + SharedAssemblyInfo.cs + + + + + + + + + + + + {ccde9ad7-3822-4b0b-aa19-df6698a85d3d} + AttributeRouting.Web.Http + + + {871a79cf-c705-4c6b-8938-f9aa1e02aea4} + AttributeRouting + + + + + --> \ No newline at end of file diff --git a/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.snk b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.snk new file mode 100644 index 0000000..b5ab91c Binary files /dev/null and b/src/AttributeRouting.Web.Http.SelfHost/AttributeRouting.snk differ diff --git a/src/AttributeRouting.Web.Http.SelfHost/Constraints/RestfulHttpMethodConstraint.cs b/src/AttributeRouting.Web.Http.SelfHost/Constraints/RestfulHttpMethodConstraint.cs deleted file mode 100644 index c103c4c..0000000 --- a/src/AttributeRouting.Web.Http.SelfHost/Constraints/RestfulHttpMethodConstraint.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Net.Http; -using System.Web.Http.Routing; -using AttributeRouting.Constraints; - -namespace AttributeRouting.Web.Http.SelfHost.Constraints -{ - /// - /// Constrains a route by the specified allowed HTTP methods. - /// - public class RestfulHttpMethodConstraint : HttpMethodConstraint, IRestfulHttpMethodConstraint - { - public RestfulHttpMethodConstraint(params HttpMethod[] allowedMethods) - : base(allowedMethods) {} - - ICollection IRestfulHttpMethodConstraint.AllowedMethods - { - get { return new ReadOnlyCollection(AllowedMethods.Select(method => method.Method).ToList()); } - } - } -} \ No newline at end of file diff --git a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/AttributeRouteFactory.cs b/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/AttributeRouteFactory.cs deleted file mode 100644 index ecd5a9b..0000000 --- a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/AttributeRouteFactory.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using System.Web.Http.Routing; -using AttributeRouting.Framework; -using AttributeRouting.Framework.Factories; -using AttributeRouting.Web.Http.Framework; - -namespace AttributeRouting.Web.Http.SelfHost.Framework.Factories -{ - internal class AttributeRouteFactory : IAttributeRouteFactory - { - private readonly HttpAttributeRoutingConfiguration _configuration; - - public AttributeRouteFactory(HttpAttributeRoutingConfiguration configuration) - { - _configuration = configuration; - } - - public IAttributeRoute CreateAttributeRoute(string url, - IDictionary defaults, - IDictionary constraints, - IDictionary dataTokens) - { - return new HttpAttributeRoute(url, - new HttpRouteValueDictionary(defaults), - new HttpRouteValueDictionary(constraints), - new HttpRouteValueDictionary(dataTokens), - _configuration); - } - } -} diff --git a/src/AttributeRouting.Web.Http.SelfHost/HttpAttributeRoutingConfiguration.cs b/src/AttributeRouting.Web.Http.SelfHost/HttpAttributeRoutingConfiguration.cs deleted file mode 100644 index 2afee9b..0000000 --- a/src/AttributeRouting.Web.Http.SelfHost/HttpAttributeRoutingConfiguration.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Web.Http.Routing; -using AttributeRouting.Framework; -using AttributeRouting.Web.Http.Constraints; -using AttributeRouting.Web.Http.SelfHost.Framework.Factories; - -namespace AttributeRouting.Web.Http.SelfHost -{ - public class HttpAttributeRoutingConfiguration : HttpAttributeRoutingConfigurationBase - { - public HttpAttributeRoutingConfiguration() - { - AttributeRouteFactory = new AttributeRouteFactory(this); - RouteConstraintFactory = new RouteConstraintFactory(this); - ParameterFactory = new RouteParameterFactory(); - - RegisterDefaultInlineRouteConstraints(typeof(RegexRouteConstraint).Assembly); - - // Must turn on AutoGenerateRouteNames and use the Unique RouteNameBuilder for this to work out-of-the-box. - AutoGenerateRouteNames = true; - RouteNameBuilder = RouteNameBuilders.Unique; - } - - /// - /// Automatically applies the specified constaint against url parameters - /// with names that match the given regular expression. - /// - /// The regex used to match url parameter names - /// The constraint to apply to matched parameters - public void AddDefaultRouteConstraint(string keyRegex, IHttpRouteConstraint constraint) - { - base.AddDefaultRouteConstraint(keyRegex, constraint); - } - } -} diff --git a/src/AttributeRouting.Web.Http.SelfHost/HttpRouteCollectionExtensions.cs b/src/AttributeRouting.Web.Http.SelfHost/HttpRouteCollectionExtensions.cs index 8287df6..d051d2e 100644 --- a/src/AttributeRouting.Web.Http.SelfHost/HttpRouteCollectionExtensions.cs +++ b/src/AttributeRouting.Web.Http.SelfHost/HttpRouteCollectionExtensions.cs @@ -18,7 +18,7 @@ public static class HttpRouteCollectionExtensions /// public static void MapHttpAttributeRoutes(this HttpRouteCollection routes) { - var configuration = new HttpAttributeRoutingConfiguration(); + var configuration = new HttpConfiguration(); configuration.AddRoutesFromAssembly(Assembly.GetCallingAssembly()); routes.MapHttpAttributeRoutesInternal(configuration); @@ -30,9 +30,9 @@ public static void MapHttpAttributeRoutes(this HttpRouteCollection routes) /// /// /// The initialization action that builds the configuration object - public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, Action configurationAction) + public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, Action configurationAction) { - var configuration = new HttpAttributeRoutingConfiguration(); + var configuration = new HttpConfiguration(); configurationAction.Invoke(configuration); routes.MapHttpAttributeRoutesInternal(configuration); @@ -44,16 +44,17 @@ public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, Actio /// /// /// The configuration object - public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, HttpAttributeRoutingConfiguration configuration) + public static void MapHttpAttributeRoutes(this HttpRouteCollection routes, HttpConfiguration configuration) { routes.MapHttpAttributeRoutesInternal(configuration); } - private static void MapHttpAttributeRoutesInternal(this HttpRouteCollection routes, HttpAttributeRoutingConfiguration configuration) + private static void MapHttpAttributeRoutesInternal(this HttpRouteCollection routes, HttpConfiguration configuration) { - var generatedRoutes = new RouteBuilder(configuration).BuildAllRoutes(); - - generatedRoutes.ToList().ForEach(r => routes.Add(r.RouteName, (HttpAttributeRoute)r)); + new RouteBuilder(configuration).BuildAllRoutes() + .Cast() + .ToList() + .ForEach(r => routes.Add(r.RouteName, r)); } } } \ No newline at end of file diff --git a/src/AttributeRouting.Web.Http.SelfHost/Logging/LoggingExtensions.cs b/src/AttributeRouting.Web.Http.SelfHost/Logging/LoggingExtensions.cs index 4087659..d8c07ad 100644 --- a/src/AttributeRouting.Web.Http.SelfHost/Logging/LoggingExtensions.cs +++ b/src/AttributeRouting.Web.Http.SelfHost/Logging/LoggingExtensions.cs @@ -1,29 +1,35 @@ -using System.Collections.Generic; -using System.IO; +using System.IO; using System.Linq; -using System.Web.Http.Routing; -using AttributeRouting.Framework; +using System.Web.Http.Routing; +using AttributeRouting.Framework; +using AttributeRouting.Helpers; using AttributeRouting.Logging; namespace AttributeRouting.Web.Http.SelfHost.Logging { public static class LoggingExtensions { - public static void LogTo(this IEnumerable routes, TextWriter writer) + public static void LogTo(this HttpRoute[] routes, TextWriter writer) { LogWriter.LogNumberOfRoutes(routes.Count(), writer); - foreach (var route in routes) - route.LogTo(writer); + foreach (var route in routes) + { + route.LogTo(writer); + } } public static void LogTo(this HttpRoute route, TextWriter writer) { - string name = route is IAttributeRoute - ? ((IAttributeRoute)route).RouteName - : null; - - LogWriter.LogRoute(writer, route.RouteTemplate, AttributeRouteInfo.GetRouteInfo(route.RouteTemplate, route.Defaults, route.Constraints, route.DataTokens)); + var attributeRoute = route as IAttributeRoute; + var info = RouteLoggingInfo.GetRouteInfo(route.RouteTemplate, + route.Defaults, + attributeRoute.SafeGet(r => r.QueryStringDefaults), + route.Constraints, + attributeRoute.SafeGet(r => r.QueryStringConstraints), + route.DataTokens); + + LogWriter.LogRoute(writer, route.RouteTemplate, info); } } } \ No newline at end of file diff --git a/src/AttributeRouting.Web.Http.SelfHost/packages.config b/src/AttributeRouting.Web.Http.SelfHost/packages.config index 1ba9e00..6c98029 100644 --- a/src/AttributeRouting.Web.Http.SelfHost/packages.config +++ b/src/AttributeRouting.Web.Http.SelfHost/packages.config @@ -2,7 +2,7 @@ - + - + \ No newline at end of file diff --git a/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj b/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj index c169336..afa9d0f 100644 --- a/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj +++ b/src/AttributeRouting.Web.Http.WebHost/AttributeRouting.Web.Http.WebHost.csproj @@ -36,13 +36,20 @@ bin\Release\AttributeRouting.Web.Http.WebHost.xml 1591, 1587 + + true + + + AttributeRouting.snk + True ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - ..\packages\Newtonsoft.Json.4.5.8\lib\net40\Newtonsoft.Json.dll + False + ..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll @@ -72,31 +79,33 @@ SharedAssemblyInfo.cs - - + + + - - - + + + - - {C91C065B-A821-4890-9F31-F9E245D804D1} - AttributeRouting.Web - + + + + - {CCDE9AD7-3822-4B0B-AA19-DF6698A85D3D} + {ccde9ad7-3822-4b0b-aa19-df6698a85d3d} AttributeRouting.Web.Http + + {c91c065b-a821-4890-9f31-f9e245d804d1} + AttributeRouting.Web + - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4} + {871a79cf-c705-4c6b-8938-f9aa1e02aea4} AttributeRouting - - - + --> \ No newline at end of file diff --git a/src/AttributeRouting.Web.Http/AttributeRouting.snk b/src/AttributeRouting.Web.Http/AttributeRouting.snk new file mode 100644 index 0000000..b5ab91c Binary files /dev/null and b/src/AttributeRouting.Web.Http/AttributeRouting.snk differ diff --git a/src/AttributeRouting.Web.Http/Constraints/CompoundRouteConstraintWrapper.cs b/src/AttributeRouting.Web.Http/Constraints/CompoundRouteConstraint.cs similarity index 76% rename from src/AttributeRouting.Web.Http/Constraints/CompoundRouteConstraintWrapper.cs rename to src/AttributeRouting.Web.Http/Constraints/CompoundRouteConstraint.cs index 75dfbad..051c9dd 100644 --- a/src/AttributeRouting.Web.Http/Constraints/CompoundRouteConstraintWrapper.cs +++ b/src/AttributeRouting.Web.Http/Constraints/CompoundRouteConstraint.cs @@ -1,28 +1,28 @@ -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Web.Http.Routing; -using AttributeRouting.Constraints; - -namespace AttributeRouting.Web.Http.Constraints -{ - public class CompoundRouteConstraintWrapper : ICompoundRouteConstraintWrapper, IHttpRouteConstraint - { - private readonly IHttpRouteConstraint[] _constraints; - - public CompoundRouteConstraintWrapper(params IHttpRouteConstraint[] constraints) - { - _constraints = constraints; - } - - public IEnumerable Constraints - { - get { return _constraints; } - } - - public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) - { - return _constraints.All(c => c.Match(request, route, parameterName, values, routeDirection)); - } - } -} +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Web.Http.Routing; +using AttributeRouting.Constraints; + +namespace AttributeRouting.Web.Http.Constraints +{ + public class CompoundRouteConstraint : ICompoundRouteConstraint, IHttpRouteConstraint + { + private readonly IHttpRouteConstraint[] _constraints; + + public CompoundRouteConstraint(params IHttpRouteConstraint[] constraints) + { + _constraints = constraints; + } + + public IEnumerable Constraints + { + get { return _constraints; } + } + + public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) + { + return _constraints.All(c => c.Match(request, route, parameterName, values, routeDirection)); + } + } +} diff --git a/src/AttributeRouting.Web.Http/Constraints/EnumValueRouteConstraint.cs b/src/AttributeRouting.Web.Http/Constraints/EnumValueRouteConstraint.cs new file mode 100644 index 0000000..54aaeb2 --- /dev/null +++ b/src/AttributeRouting.Web.Http/Constraints/EnumValueRouteConstraint.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Web.Http.Routing; +using AttributeRouting.Constraints; + +namespace AttributeRouting.Web.Http.Constraints +{ + public class EnumValueRouteConstraint : EnumValueRouteConstraintBase, IHttpRouteConstraint + where T : struct + { + public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) + { + return IsMatch(parameterName, values); + } + } +} \ No newline at end of file diff --git a/src/AttributeRouting.Web.Http/Constraints/InboundHttpMethodConstraint.cs b/src/AttributeRouting.Web.Http/Constraints/InboundHttpMethodConstraint.cs new file mode 100644 index 0000000..f24b482 --- /dev/null +++ b/src/AttributeRouting.Web.Http/Constraints/InboundHttpMethodConstraint.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Net.Http; +using System.Web.Http.Routing; +using AttributeRouting.Constraints; + +namespace AttributeRouting.Web.Http.Constraints +{ + public class InboundHttpMethodConstraint : HttpMethodConstraint, IInboundHttpMethodConstraint + { + /// + /// Constrains an inbound route by HTTP method. + /// + public InboundHttpMethodConstraint(params HttpMethod[] allowedMethods) + : base(allowedMethods) + { + } + + ICollection IInboundHttpMethodConstraint.AllowedMethods + { + get { return new ReadOnlyCollection(AllowedMethods.Select(method => method.Method).ToList()); } + } + + protected override bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) + { + if (routeDirection == HttpRouteDirection.UriGeneration) + return true; + + return base.Match(request, route, parameterName, values, routeDirection); + } + } +} \ No newline at end of file diff --git a/src/AttributeRouting.Web.Http.SelfHost/Constraints/OptionalRouteConstraintWrapper.cs b/src/AttributeRouting.Web.Http/Constraints/OptionalRouteConstraint.cs similarity index 57% rename from src/AttributeRouting.Web.Http.SelfHost/Constraints/OptionalRouteConstraintWrapper.cs rename to src/AttributeRouting.Web.Http/Constraints/OptionalRouteConstraint.cs index d37a7f5..e8ee2ed 100644 --- a/src/AttributeRouting.Web.Http.SelfHost/Constraints/OptionalRouteConstraintWrapper.cs +++ b/src/AttributeRouting.Web.Http/Constraints/OptionalRouteConstraint.cs @@ -1,37 +1,44 @@ -using System.Collections.Generic; -using System.Net.Http; -using System.Web.Http; -using System.Web.Http.Routing; +using System.Collections.Generic; +using System.Net.Http; +using System.Web.Http; +using System.Web.Http.Routing; using AttributeRouting.Constraints; -using AttributeRouting.Helpers; - -namespace AttributeRouting.Web.Http.SelfHost.Constraints -{ - public class OptionalRouteConstraintWrapper : IOptionalRouteConstraintWrapper, IHttpRouteConstraint - { - private readonly IHttpRouteConstraint _constraint; - - public OptionalRouteConstraintWrapper(IHttpRouteConstraint constraint) +using AttributeRouting.Framework; +using AttributeRouting.Helpers; + +namespace AttributeRouting.Web.Http.Constraints +{ + public class OptionalRouteConstraint : IOptionalRouteConstraint, IHttpRouteConstraint + { + private readonly IHttpRouteConstraint _constraint; + + public OptionalRouteConstraint(IHttpRouteConstraint constraint) + { + _constraint = constraint; + } + + public object Constraint + { + get { return _constraint; } + } + + public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) { - _constraint = constraint; - } - - public object Constraint - { - get { return _constraint; } - } - - public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) - { - // If the param is optional and has no value, then pass the constraint - if (route.Defaults.ContainsKey(parameterName) - && route.Defaults[parameterName] == RouteParameter.Optional) - { + var attributeRoute = (IAttributeRoute)route; + var allDefaults = new Dictionary(); + allDefaults.Merge(attributeRoute.Defaults); + allDefaults.Merge(attributeRoute.QueryStringDefaults); + + // If the param is optional and has no value, then pass the constraint + if (allDefaults.ContainsKey(parameterName) && allDefaults[parameterName] == RouteParameter.Optional) + { if (values[parameterName].HasNoValue()) + { return true; - } - - return _constraint.Match(request, route, parameterName, values, routeDirection); - } - } + } + } + + return _constraint.Match(request, route, parameterName, values, routeDirection); + } + } } \ No newline at end of file diff --git a/src/AttributeRouting.Web.Http/Constraints/QueryStringRouteConstraint.cs b/src/AttributeRouting.Web.Http/Constraints/QueryStringRouteConstraint.cs new file mode 100644 index 0000000..c007901 --- /dev/null +++ b/src/AttributeRouting.Web.Http/Constraints/QueryStringRouteConstraint.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Web; +using System.Web.Http.Routing; +using AttributeRouting.Constraints; +using AttributeRouting.Helpers; + +namespace AttributeRouting.Web.Http.Constraints +{ + public class QueryStringRouteConstraint : IQueryStringRouteConstraint, IHttpRouteConstraint + { + private readonly IHttpRouteConstraint _constraint; + + public QueryStringRouteConstraint(IHttpRouteConstraint constraint) + { + _constraint = constraint; + } + + public object Constraint + { + get { return _constraint; } + } + + public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary values, HttpRouteDirection routeDirection) + { + // If the query param does not exist, then fail. + var queryString = HttpUtility.ParseQueryString(request.RequestUri.Query); + var value = queryString[parameterName] ?? values[parameterName]; + if (value.HasNoValue()) + { + return false; + } + + // Process the constraint. + var queryRouteValues = new Dictionary + { + { parameterName, queryString[parameterName] } + }; + + return _constraint == null // ie: Simply ensure that the query param exists. + || _constraint.Match(request, route, parameterName, queryRouteValues, routeDirection); + } + } +} \ No newline at end of file diff --git a/src/AttributeRouting.Web.Http/DefaultHttpRouteConventionAttribute.cs b/src/AttributeRouting.Web.Http/DefaultHttpRouteConventionAttribute.cs index 591352d..7e04104 100644 --- a/src/AttributeRouting.Web.Http/DefaultHttpRouteConventionAttribute.cs +++ b/src/AttributeRouting.Web.Http/DefaultHttpRouteConventionAttribute.cs @@ -67,9 +67,9 @@ public override IEnumerable GetRouteAttributes(MethodInfo actio } } - public override string GetDefaultRoutePrefix(MethodInfo actionMethod) + public override IEnumerable GetDefaultRoutePrefixes(Type controllerType) { - return actionMethod.DeclaringType.GetControllerName(); + yield return new RoutePrefixAttribute(controllerType.GetControllerName()); } private IRouteAttribute BuildRouteAttribute(HttpRouteConventionInfo convention) diff --git a/src/AttributeRouting.Web.Http/Framework/AttributeRouteFactory.cs b/src/AttributeRouting.Web.Http/Framework/AttributeRouteFactory.cs new file mode 100644 index 0000000..b1c6f68 --- /dev/null +++ b/src/AttributeRouting.Web.Http/Framework/AttributeRouteFactory.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.Web.Http.Routing; +using AttributeRouting.Framework; + +namespace AttributeRouting.Web.Http.Framework +{ + internal class AttributeRouteFactory : IAttributeRouteFactory + { + private readonly HttpConfigurationBase _configuration; + + public AttributeRouteFactory(HttpConfigurationBase configuration) + { + _configuration = configuration; + } + + public IEnumerable CreateAttributeRoutes(string url, IDictionary defaults, IDictionary constraints, IDictionary dataTokens) + { + yield return new HttpAttributeRoute(url, + new HttpRouteValueDictionary(defaults), + new HttpRouteValueDictionary(constraints), + new HttpRouteValueDictionary(dataTokens), + _configuration); + } + } +} diff --git a/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs b/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs index 09d3120..24b06ec 100644 --- a/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs +++ b/src/AttributeRouting.Web.Http/Framework/HttpAttributeRoute.cs @@ -1,114 +1,164 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Web.Http.Routing; -using AttributeRouting.Framework; -using AttributeRouting.Helpers; - -namespace AttributeRouting.Web.Http.Framework -{ - /// - /// Route to use for self-hosted Web API routes. - /// - public class HttpAttributeRoute : HttpRoute, IAttributeRoute - { - private readonly HttpAttributeRoutingConfigurationBase _configuration; - - /// - /// Route used by the AttributeRouting framework in self-host projects. - /// - public HttpAttributeRoute(string url, - HttpRouteValueDictionary defaults, - HttpRouteValueDictionary constraints, - HttpRouteValueDictionary dataTokens, - HttpAttributeRoutingConfigurationBase configuration) - : base(url, defaults, constraints, dataTokens) - { - _configuration = configuration; - } - - public string Url - { - get { return RouteTemplate; } - set { throw new NotImplementedException("IHttpRoute.RouteTemplate has no setter."); } - } - - public string RouteName { get; set; } - - public string CultureName { get; set; } - - public List MappedSubdomains { get; set; } - - public string Subdomain { get; set; } - - public bool? UseLowercaseRoute { get; set; } - - public bool? PreserveCaseForUrlParameters { get; set; } - - public bool? AppendTrailingSlash { get; set; } - - IDictionary IAttributeRoute.DataTokens - { - get { return DataTokens; } - set { throw new NotImplementedException("HttpRoute.DataTokens has no setter."); } - } - - IDictionary IAttributeRoute.Constraints - { - get { return Constraints; } - set { throw new NotImplementedException("HttpRoute.Constraints has no setter."); } - } - - IDictionary IAttributeRoute.Defaults - { - get { return Defaults; } - set { throw new NotImplementedException("HttpRoute.Defaults has no setter."); } - } - - public IEnumerable Translations { get; set; } - - public IAttributeRoute DefaultRouteContainer { get; set; } - - public override IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestMessage request) - { - // Let the underlying route match, and if it does, then add a few more constraints. - var routeData = base.GetRouteData(virtualPathRoot, request); - if (routeData == null) - return null; - - // Constrain by subdomain if configured - var host = request.SafeGet(r => r.Headers.Host); - if (!this.IsSubdomainMatched(host, _configuration)) - return null; - - // Constrain by culture name if configured - var currentUICultureName = _configuration.CurrentUICultureResolver(request, routeData); - if (!this.IsCultureNameMatched(currentUICultureName, _configuration)) - return null; - - return routeData; - } - - public override IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary values) - { - // Let the underlying route do its thing, and if it does, then add some functionality on top. - var virtualPathData = base.GetVirtualPath(request, values); - if (virtualPathData == null) - return null; - - // Translate this path if a translation is available. - if (_configuration.TranslationProviders.Any()) - { - virtualPathData = - this.GetTranslatedVirtualPath(t => ((HttpRoute)t).GetVirtualPath(request, values)) - ?? virtualPathData; - } - - // Lowercase, append trailing slash, etc. - var virtualPath = this.GetFinalVirtualPath(virtualPathData.VirtualPath, _configuration); - - return new HttpVirtualPathData(virtualPathData.Route, virtualPath); - } - } -} +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Web.Http.Routing; +using System.Web.Routing; +using AttributeRouting.Framework; +using AttributeRouting.Helpers; + +namespace AttributeRouting.Web.Http.Framework +{ + /// + /// Route to use for Web API routes. + /// + public class HttpAttributeRoute : HttpRoute, IAttributeRoute + { + private const string RequestedPathKey = "__AttributeRouting:RequestedPath"; + private const string RequestedSubdomainKey = "__AttributeRouting:RequestedSubdomain"; + private const string CurrentUICultureNameKey = "__AttributeRouting:CurrentUICulture"; + + private readonly HttpConfigurationBase _configuration; + private readonly AttributeRouteVisitor _visitor; + + /// + /// Route used by the AttributeRouting framework in self-host projects. + /// + public HttpAttributeRoute(string url, + HttpRouteValueDictionary defaults, + HttpRouteValueDictionary constraints, + HttpRouteValueDictionary dataTokens, + HttpConfigurationBase configuration) + : base(url, defaults, constraints, dataTokens, configuration.MessageHandler) + { + _configuration = configuration; + _visitor = new AttributeRouteVisitor(this, configuration); + QueryStringConstraints = new RouteValueDictionary(); + QueryStringDefaults = new RouteValueDictionary(); + } + + public bool? AppendTrailingSlash { get; set; } + + IDictionary IAttributeRoute.Constraints + { + get { return Constraints; } + set { throw new NotImplementedException("HttpRoute.Constraints has no setter."); } + } + + public string CultureName { get; set; } + + IDictionary IAttributeRoute.DataTokens + { + get { return DataTokens; } + set { throw new NotImplementedException("HttpRoute.DataTokens has no setter."); } + } + + IDictionary IAttributeRoute.Defaults + { + get { return Defaults; } + set { throw new NotImplementedException("HttpRoute.Defaults has no setter."); } + } + + public List MappedSubdomains { get; set; } + + public bool? PreserveCaseForUrlParameters { get; set; } + + public IDictionary QueryStringConstraints { get; set; } + + public IDictionary QueryStringDefaults { get; set; } + + public string RouteName { get; set; } + + public IAttributeRoute SourceLanguageRoute { get; set; } + + public string Subdomain { get; set; } + + public IEnumerable Translations { get; set; } + + public string Url + { + get { return RouteTemplate; } + set { throw new NotImplementedException("IHttpRoute.RouteTemplate has no setter."); } + } + + public bool? UseLowercaseRoute { get; set; } + + public override IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestMessage request) + { + // Optimize matching by comparing the static left part of the route url with the requested path. + var requestedPath = GetCachedValue(request, RequestedPathKey, () => request.RequestUri.AbsolutePath.Substring(virtualPathRoot.Length)); + if (!_visitor.IsStaticLeftPartOfUrlMatched(requestedPath)) + { + return null; + } + + // Let the underlying route match, and if it does, then add a few more constraints. + var routeData = base.GetRouteData(virtualPathRoot, request); + if (routeData == null) + { + return null; + } + + // Constrain by querystring param if there are any. + var routeValues = new HttpRouteValueDictionary(routeData.Values); + if (!_visitor.ProcessQueryStringConstraints((constraint, parameterName) => ProcessConstraint(request, constraint, parameterName, routeValues, HttpRouteDirection.UriResolution))) + { + return null; + } + + // Constrain by subdomain if configured + var requestedSubdomain = GetCachedValue(request, RequestedSubdomainKey, () => _configuration.SubdomainParser(request.SafeGet(r => r.Headers.Host))); + if (!_visitor.IsSubdomainMatched(requestedSubdomain)) + { + return null; + } + + // Constrain by culture name if configured + var currentUICultureName = GetCachedValue(request, CurrentUICultureNameKey, () => _configuration.CurrentUICultureResolver(request, routeData)); + if (!_visitor.IsCultureNameMatched(currentUICultureName)) + { + return null; + } + + return routeData; + } + + public override IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary values) + { + // Add querystring default values if applicable. + _visitor.AddQueryStringDefaultsToRouteValues(values); + + // Let the underlying route do its thing. + var virtualPathData = base.GetVirtualPath(request, values); + if (virtualPathData == null) + { + return null; + } + + // Translate this path if a translation is available. + var translatedVirtualPath = _visitor.GetTranslatedVirtualPath(t => ((HttpRoute)t).GetVirtualPath(request, values)); + if (translatedVirtualPath != null) + { + virtualPathData = translatedVirtualPath; + } + + // Lowercase, append trailing slash, etc. + var virtualPath = _visitor.GetFinalVirtualPath(virtualPathData.VirtualPath); + + return new HttpVirtualPathData(virtualPathData.Route, virtualPath); + } + + private static T GetCachedValue(HttpRequestMessage context, string key, Func initializeValue) + { + // Fetch the item from the http context if it's been stored for the request. + if (context.Properties.ContainsKey(key)) + { + return (T)context.Properties[key]; + } + + // Cache the value and return it. + var value = initializeValue(); + context.Properties.Add(key, value); + return value; + } + } +} diff --git a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Http/Framework/RouteConstraintFactory.cs similarity index 62% rename from src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs rename to src/AttributeRouting.Web.Http/Framework/RouteConstraintFactory.cs index df49312..9a1ff37 100644 --- a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteConstraintFactory.cs +++ b/src/AttributeRouting.Web.Http/Framework/RouteConstraintFactory.cs @@ -1,62 +1,65 @@ -using System; -using System.Linq; -using System.Net.Http; -using System.Text.RegularExpressions; -using System.Web.Http.Routing; -using AttributeRouting.Constraints; -using AttributeRouting.Framework; -using AttributeRouting.Framework.Factories; -using AttributeRouting.Helpers; -using AttributeRouting.Web.Http.Constraints; -using AttributeRouting.Web.Http.SelfHost.Constraints; - -namespace AttributeRouting.Web.Http.SelfHost.Framework.Factories -{ - public class RouteConstraintFactory : IRouteConstraintFactory - { - private readonly HttpAttributeRoutingConfiguration _configuration; - - public RouteConstraintFactory(HttpAttributeRoutingConfiguration configuration) - { - _configuration = configuration; - } - - public RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, RegexOptions options = RegexOptions.None) - { - return new RegexRouteConstraint(pattern, options); - } - - public IRestfulHttpMethodConstraint CreateRestfulHttpMethodConstraint(string[] httpMethods) - { - var allowedMethods = httpMethods.Select(m => new HttpMethod(m)).ToArray(); - return new RestfulHttpMethodConstraint(allowedMethods); - } - - public object CreateInlineRouteConstraint(string name, params object[] parameters) - { - var inlineRouteConstraints = _configuration.InlineRouteConstraints; - if (inlineRouteConstraints.ContainsKey(name)) - { - var type = inlineRouteConstraints[name]; - - if (!typeof(IHttpRouteConstraint).IsAssignableFrom(type)) - throw new AttributeRoutingException( - "The constraint \"{0}\" must implement System.Web.Http.Routing.IHttpRouteConstraint".FormatWith(type.FullName)); - - return Activator.CreateInstance(type, parameters); - } - - return null; - } - - public ICompoundRouteConstraintWrapper CreateCompoundRouteConstraint(params object[] constraints) - { - return new CompoundRouteConstraintWrapper(constraints.Cast().ToArray()); - } - - public IOptionalRouteConstraintWrapper CreateOptionalRouteConstraint(object constraint) - { - return new OptionalRouteConstraintWrapper((IHttpRouteConstraint)constraint); - } - } +using System; +using System.Linq; +using System.Net.Http; +using System.Text.RegularExpressions; +using System.Web.Http.Routing; +using AttributeRouting.Constraints; +using AttributeRouting.Framework; +using AttributeRouting.Helpers; +using AttributeRouting.Web.Http.Constraints; + +namespace AttributeRouting.Web.Http.Framework +{ + public class RouteConstraintFactory : IRouteConstraintFactory + { + private readonly HttpConfigurationBase _configuration; + + public RouteConstraintFactory(HttpConfigurationBase configuration) + { + _configuration = configuration; + } + + public RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, RegexOptions options = RegexOptions.None) + { + return new RegexRouteConstraint(pattern, options); + } + + public IInboundHttpMethodConstraint CreateInboundHttpMethodConstraint(string[] httpMethods) + { + var allowedMethods = httpMethods.Select(m => new HttpMethod(m)).ToArray(); + return new InboundHttpMethodConstraint(allowedMethods); + } + + public object CreateInlineRouteConstraint(string name, params object[] parameters) + { + var inlineRouteConstraints = _configuration.InlineRouteConstraints; + if (inlineRouteConstraints.ContainsKey(name)) + { + var type = inlineRouteConstraints[name]; + + if (!typeof(IHttpRouteConstraint).IsAssignableFrom(type)) + throw new AttributeRoutingException( + "The constraint \"{0}\" must implement System.Web.Http.Routing.IHttpRouteConstraint".FormatWith(type.FullName)); + + return Activator.CreateInstance(type, parameters); + } + + return null; + } + + public ICompoundRouteConstraint CreateCompoundRouteConstraint(params object[] constraints) + { + return new CompoundRouteConstraint(constraints.Cast().ToArray()); + } + + public IOptionalRouteConstraint CreateOptionalRouteConstraint(object constraint) + { + return new OptionalRouteConstraint((IHttpRouteConstraint)constraint); + } + + public IQueryStringRouteConstraint CreateQueryStringRouteConstraint(object constraint) + { + return new QueryStringRouteConstraint((IHttpRouteConstraint)constraint); + } + } } \ No newline at end of file diff --git a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteParameterFactory.cs b/src/AttributeRouting.Web.Http/Framework/RouteParameterFactory.cs similarity index 61% rename from src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteParameterFactory.cs rename to src/AttributeRouting.Web.Http/Framework/RouteParameterFactory.cs index 1d76c5c..c615801 100644 --- a/src/AttributeRouting.Web.Http.SelfHost/Framework/Factories/RouteParameterFactory.cs +++ b/src/AttributeRouting.Web.Http/Framework/RouteParameterFactory.cs @@ -1,13 +1,13 @@ -using System.Web.Http; -using AttributeRouting.Framework.Factories; - -namespace AttributeRouting.Web.Http.SelfHost.Framework.Factories -{ - internal class RouteParameterFactory : IParameterFactory - { - public object Optional() - { - return RouteParameter.Optional; - } - } +using System.Web.Http; +using AttributeRouting.Framework; + +namespace AttributeRouting.Web.Http.Framework +{ + internal class RouteParameterFactory : IParameterFactory + { + public object Optional() + { + return RouteParameter.Optional; + } + } } \ No newline at end of file diff --git a/src/AttributeRouting.Web.Http/HttpConfiguration.cs b/src/AttributeRouting.Web.Http/HttpConfiguration.cs new file mode 100644 index 0000000..adea2f5 --- /dev/null +++ b/src/AttributeRouting.Web.Http/HttpConfiguration.cs @@ -0,0 +1,26 @@ +using System.Web.Http.Routing; +using AttributeRouting.Framework; +using AttributeRouting.Web.Http.Constraints; +using AttributeRouting.Web.Http.Framework; + +namespace AttributeRouting.Web.Http +{ + public class HttpConfiguration : HttpConfigurationBase + { + public HttpConfiguration() + { + Init(); + } + + /// + /// Automatically applies the specified constaint against url parameters + /// with names that match the given regular expression. + /// + /// The regex used to match url parameter names + /// The constraint to apply to matched parameters + public void AddDefaultRouteConstraint(string keyRegex, IHttpRouteConstraint constraint) + { + base.AddDefaultRouteConstraint(keyRegex, constraint); + } + } +} diff --git a/src/AttributeRouting.Web.Http/HttpAttributeRoutingConfigurationBase.cs b/src/AttributeRouting.Web.Http/HttpConfigurationBase.cs similarity index 58% rename from src/AttributeRouting.Web.Http/HttpAttributeRoutingConfigurationBase.cs rename to src/AttributeRouting.Web.Http/HttpConfigurationBase.cs index 85604a7..b03e095 100644 --- a/src/AttributeRouting.Web.Http/HttpAttributeRoutingConfigurationBase.cs +++ b/src/AttributeRouting.Web.Http/HttpConfigurationBase.cs @@ -3,16 +3,42 @@ using System.Threading; using System.Web.Http.Controllers; using System.Web.Http.Routing; +using AttributeRouting.Framework; +using AttributeRouting.Web.Http.Constraints; +using AttributeRouting.Web.Http.Framework; namespace AttributeRouting.Web.Http { - public abstract class HttpAttributeRoutingConfigurationBase : AttributeRoutingConfigurationBase + public abstract class HttpConfigurationBase : ConfigurationBase { - protected HttpAttributeRoutingConfigurationBase() + protected HttpConfigurationBase() { CurrentUICultureResolver = (ctx, data) => Thread.CurrentThread.CurrentUICulture.Name; } + public void Init() + { + AttributeRouteFactory = new AttributeRouteFactory(this); + RouteConstraintFactory = new RouteConstraintFactory(this); + ParameterFactory = new RouteParameterFactory(); + + RegisterDefaultInlineRouteConstraints(typeof(RegexRouteConstraint).Assembly); + + // Must turn on AutoGenerateRouteNames and use the Unique RouteNameBuilder for this to work out-of-the-box. + AutoGenerateRouteNames = true; + RouteNameBuilder = RouteNameBuilders.Unique; + } + + /// + /// Defines whether the Web API pipeline will run in memory or not. + /// + public bool InMemory { get; set; } + + /// + /// The message handler that will be the recipient of the request. + /// + public HttpMessageHandler MessageHandler { get; set; } + /// /// The controller type applicable to this context. /// diff --git a/src/AttributeRouting.Web.Http/HttpRouteAttribute.cs b/src/AttributeRouting.Web.Http/HttpRouteAttribute.cs index 9b7f627..6a7053d 100644 --- a/src/AttributeRouting.Web.Http/HttpRouteAttribute.cs +++ b/src/AttributeRouting.Web.Http/HttpRouteAttribute.cs @@ -62,7 +62,11 @@ public int Precedence public string RouteName { get; set; } - public bool IsAbsoluteUrl { get; set; } + public bool IsAbsoluteUrl + { + get { return IgnoreAreaUrl && IgnoreRoutePrefix; } + set { IgnoreAreaUrl = IgnoreRoutePrefix = value; } + } public string TranslationKey { get; set; } @@ -89,5 +93,9 @@ public bool AppendTrailingSlash } public bool? AppendTrailingSlashFlag { get; private set; } + + public bool IgnoreRoutePrefix { get; set; } + + public bool IgnoreAreaUrl { get; set; } } } \ No newline at end of file diff --git a/src/AttributeRouting.Web.Http/packages.config b/src/AttributeRouting.Web.Http/packages.config index 6551089..5973fb4 100644 --- a/src/AttributeRouting.Web.Http/packages.config +++ b/src/AttributeRouting.Web.Http/packages.config @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj b/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj index 79a64f4..3445cce 100644 --- a/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj +++ b/src/AttributeRouting.Web.Mvc/AttributeRouting.Web.Mvc.csproj @@ -36,13 +36,43 @@ bin\Release\AttributeRouting.Web.Mvc.xml 1591, 1587 + + true + + + AttributeRouting.snk + + + True + ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll + + + True + ..\packages\Microsoft.AspNet.WebPages.1.0.20105.408\lib\net40\System.Web.Helpers.dll + + + True + ..\packages\Microsoft.AspNet.Razor.1.0.20105.408\lib\net40\System.Web.Razor.dll + + + True + ..\packages\Microsoft.AspNet.WebPages.1.0.20105.408\lib\net40\System.Web.WebPages.dll + + + True + ..\packages\Microsoft.AspNet.WebPages.1.0.20105.408\lib\net40\System.Web.WebPages.Deployment.dll + + + True + ..\packages\Microsoft.AspNet.WebPages.1.0.20105.408\lib\net40\System.Web.WebPages.Razor.dll + @@ -53,13 +83,15 @@ SharedAssemblyInfo.cs - - + + + + - - - + + + @@ -68,13 +100,17 @@ + + + + - {C91C065B-A821-4890-9F31-F9E245D804D1} + {c91c065b-a821-4890-9f31-f9e245d804d1} AttributeRouting.Web - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4} + {871a79cf-c705-4c6b-8938-f9aa1e02aea4} AttributeRouting diff --git a/src/AttributeRouting.Web.Mvc/AttributeRouting.snk b/src/AttributeRouting.Web.Mvc/AttributeRouting.snk new file mode 100644 index 0000000..b5ab91c Binary files /dev/null and b/src/AttributeRouting.Web.Mvc/AttributeRouting.snk differ diff --git a/src/AttributeRouting.Web.Mvc/AttributeRoutingConfiguration.cs b/src/AttributeRouting.Web.Mvc/Configuration.cs similarity index 90% rename from src/AttributeRouting.Web.Mvc/AttributeRoutingConfiguration.cs rename to src/AttributeRouting.Web.Mvc/Configuration.cs index df5cbd1..9d50c21 100644 --- a/src/AttributeRouting.Web.Mvc/AttributeRoutingConfiguration.cs +++ b/src/AttributeRouting.Web.Mvc/Configuration.cs @@ -4,13 +4,13 @@ using System.Web.Mvc; using System.Web.Routing; using AttributeRouting.Web.Constraints; -using AttributeRouting.Web.Mvc.Framework.Factories; +using AttributeRouting.Web.Mvc.Framework; namespace AttributeRouting.Web.Mvc { - public class AttributeRoutingConfiguration : AttributeRoutingConfigurationBase + public class Configuration : ConfigurationBase { - public AttributeRoutingConfiguration() + public Configuration() { AttributeRouteFactory = new AttributeRouteFactory(this); ParameterFactory = new RouteParameterFactory(); @@ -21,24 +21,15 @@ public AttributeRoutingConfiguration() RegisterDefaultInlineRouteConstraints(typeof(RegexRouteConstraint).Assembly); } - public Func RouteHandlerFactory { get; set; } - - /// - /// The controller type applicable to this context. - /// - public override Type FrameworkControllerType - { - get { return typeof(IController); } - } - /// - /// Specifies a function that returns an alternate route handler. - /// By default, the route handler is the default MvcRouteHandler. + /// Automatically applies the specified constraint against url parameters + /// with names that match the given regular expression. /// - /// The route hanlder to use. - public void UseRouteHandler(Func routeHandlerFactory) + /// The regex used to match url parameter names + /// The constraint to apply to matched parameters + public void AddDefaultRouteConstraint(string keyRegex, IRouteConstraint constraint) { - RouteHandlerFactory = routeHandlerFactory; + base.AddDefaultRouteConstraint(keyRegex, constraint); } /// @@ -60,21 +51,30 @@ public void AddRoutesFromControllersOfType() where T : IController } /// - /// Automatically applies the specified constraint against url parameters - /// with names that match the given regular expression. + /// This delegate returns the current UI culture name, + /// which is used when constraining inbound routes by culture. + /// The default delegate returns the CurrentUICulture name of the current thread. /// - /// The regex used to match url parameter names - /// The constraint to apply to matched parameters - public void AddDefaultRouteConstraint(string keyRegex, IRouteConstraint constraint) + public Func CurrentUICultureResolver { get; set; } + + /// + /// The controller type applicable to this context. + /// + public override Type FrameworkControllerType { - base.AddDefaultRouteConstraint(keyRegex, constraint); + get { return typeof(IController); } } /// - /// This delegate returns the current UI culture name, - /// which is used when constraining inbound routes by culture. - /// The default delegate returns the CurrentUICulture name of the current thread. + /// Specifies a function that returns an alternate route handler. + /// By default, the route handler is the default MvcRouteHandler. /// - public Func CurrentUICultureResolver { get; set; } + /// The route hanlder to use. + public void UseRouteHandler(Func routeHandlerFactory) + { + RouteHandlerFactory = routeHandlerFactory; + } + + internal Func RouteHandlerFactory { get; set; } } } diff --git a/src/AttributeRouting.Web.Mvc/Constraints/InboundHttpMethodConstraint.cs b/src/AttributeRouting.Web.Mvc/Constraints/InboundHttpMethodConstraint.cs new file mode 100644 index 0000000..610347c --- /dev/null +++ b/src/AttributeRouting.Web.Mvc/Constraints/InboundHttpMethodConstraint.cs @@ -0,0 +1,70 @@ +using System.Linq; +using System.Web; +using System.Web.Helpers; +using System.Web.Routing; +using AttributeRouting.Constraints; +using AttributeRouting.Helpers; + +namespace AttributeRouting.Web.Mvc.Constraints +{ + public class InboundHttpMethodConstraint : HttpMethodConstraint, IInboundHttpMethodConstraint + { + /// + /// Constrains an inbound route by HTTP method. + /// + public InboundHttpMethodConstraint(params string[] allowedMethods) + : base(allowedMethods) + { + } + + protected override bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) + { + if (routeDirection == RouteDirection.UrlGeneration) + return true; + + // Make sure to check for HTTP method overrides! + var httpMethod = GetUnvalidatedHttpMethodOverride(httpContext.Request); + + return AllowedMethods.Any(m => m.ValueEquals(httpMethod)); + } + + /// + /// The reason we have our own is to provide support for System.Web.Helpers.Validation.Unvalidated calls. + /// NOTE: This won't be needed once AR targets .NET 4.5. + /// + private static string GetUnvalidatedHttpMethodOverride(HttpRequestBase request) + { + var httpMethod = request.HttpMethod; + + // If not a post, method overrides don't apply. + if (!httpMethod.ValueEquals("POST")) + { + return httpMethod; + } + + // Get the method override and if it's not for a GET or POST, then apply it. + var methodOverride = request.SafeGet(r => r.Headers["X-HTTP-Method-Override"]) ?? + request.SafeGet(r => GetFormValue(r, "X-HTTP-Method-Override")) ?? + request.SafeGet(r => GetQueryStringValue(r, "X-HTTP-Method-Override")); + + if (methodOverride != null && + (!methodOverride.ValueEquals("GET") && !methodOverride.ValueEquals("POST"))) + { + return methodOverride; + } + + // Otherwise, just return the http method. + return httpMethod; + } + + private static string GetFormValue(HttpRequestBase request, string key) + { + return request.Unvalidated().QueryString[key] ?? request.Form[key]; + } + + private static string GetQueryStringValue(HttpRequestBase request, string key) + { + return request.Unvalidated().Form[key] ?? request.QueryString[key]; + } + } +} \ No newline at end of file diff --git a/src/AttributeRouting.Web.Mvc/Constraints/OptionalRouteConstraintWrapper.cs b/src/AttributeRouting.Web.Mvc/Constraints/OptionalRouteConstraint.cs similarity index 51% rename from src/AttributeRouting.Web.Mvc/Constraints/OptionalRouteConstraintWrapper.cs rename to src/AttributeRouting.Web.Mvc/Constraints/OptionalRouteConstraint.cs index 3da4d79..6d3d4e9 100644 --- a/src/AttributeRouting.Web.Mvc/Constraints/OptionalRouteConstraintWrapper.cs +++ b/src/AttributeRouting.Web.Mvc/Constraints/OptionalRouteConstraint.cs @@ -1,35 +1,44 @@ -using System.Web; -using System.Web.Mvc; -using System.Web.Routing; +using System.Collections.Generic; +using System.Web; +using System.Web.Mvc; +using System.Web.Routing; using AttributeRouting.Constraints; +using AttributeRouting.Framework; +using AttributeRouting.Helpers; -namespace AttributeRouting.Web.Mvc.Constraints -{ - public class OptionalRouteConstraintWrapper : IOptionalRouteConstraintWrapper, IRouteConstraint - { - private readonly IRouteConstraint _constraint; - - public OptionalRouteConstraintWrapper(IRouteConstraint constraint) - { - _constraint = constraint; - } - - public object Constraint - { - get { return _constraint; } - } - - public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) +namespace AttributeRouting.Web.Mvc.Constraints +{ + public class OptionalRouteConstraint : IOptionalRouteConstraint, IRouteConstraint + { + private readonly IRouteConstraint _constraint; + + public OptionalRouteConstraint(IRouteConstraint constraint) + { + _constraint = constraint; + } + + public object Constraint + { + get { return _constraint; } + } + + public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { + var attributeRoute = (IAttributeRoute)route; + var allDefaults = new Dictionary(); + allDefaults.Merge(attributeRoute.Defaults); + allDefaults.Merge(attributeRoute.QueryStringDefaults); + // If the param is optional and has no value, then pass the constraint - if (route.Defaults.ContainsKey(parameterName) - && route.Defaults[parameterName] == UrlParameter.Optional) + if (allDefaults.ContainsKey(parameterName) && allDefaults[parameterName] == UrlParameter.Optional) { - if (values[parameterName] == UrlParameter.Optional) + if (values[parameterName].HasNoValue()) + { return true; - } - - return _constraint.Match(httpContext, route, parameterName, values, routeDirection); - } - } + } + } + + return _constraint.Match(httpContext, route, parameterName, values, routeDirection); + } + } } \ No newline at end of file diff --git a/src/AttributeRouting.Web.Mvc/DELETEAttribute.cs b/src/AttributeRouting.Web.Mvc/DELETEAttribute.cs index b98eb03..d431a6b 100644 --- a/src/AttributeRouting.Web.Mvc/DELETEAttribute.cs +++ b/src/AttributeRouting.Web.Mvc/DELETEAttribute.cs @@ -7,6 +7,12 @@ namespace AttributeRouting.Web.Mvc /// public class DELETEAttribute : RouteAttribute { + /// + /// Specify a route for DELETE request. + /// The route URL will be the name of the action. + /// + public DELETEAttribute() : base(HttpVerbs.Delete) {} + /// /// Specify a route for DELETE request. /// diff --git a/src/AttributeRouting.Web.Mvc/Extensions/UrlHelperExtensions.cs b/src/AttributeRouting.Web.Mvc/Extensions/UrlHelperExtensions.cs new file mode 100644 index 0000000..239067b --- /dev/null +++ b/src/AttributeRouting.Web.Mvc/Extensions/UrlHelperExtensions.cs @@ -0,0 +1,276 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Web.Mvc; +using System.Web.Routing; +using AttributeRouting.Framework; +using AttributeRouting.Helpers; + +namespace AttributeRouting.Web.Mvc.Extensions +{ + public static class UrlHelperExtensions + { + public static string SubdomainAction(this UrlHelper urlHelper, string actionName, + RouteValueDictionary routeValues) + { + string baseUrl = GetDomainBase(urlHelper, null, actionName, null, routeValues); + return BuildUri(baseUrl, urlHelper.Action(actionName, routeValues)); + } + + public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName, + string areaName = "") + { + var routeValues = new RouteValueDictionary { { "area", areaName } }; + string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName, routeValues); + return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues)); + } + + public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName, + object routeValues) + { + string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName, + new RouteValueDictionary(routeValues)); + return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues)); + } + + public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName, + RouteValueDictionary routeValues) + { + string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName, routeValues); + return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues)); + } + + public static string SubdomainAction(this UrlHelper urlHelper, string actionName, string controllerName, + object routeValues, string protocol) + { + string baseUrl = GetDomainBase(urlHelper, null, actionName, controllerName, + new RouteValueDictionary(routeValues), protocol); + return BuildUri(baseUrl, urlHelper.Action(actionName, controllerName, routeValues)); + } + + public static string SubdomainRouteUrl(this UrlHelper urlHelper, object routeValues) + { + return SubdomainRouteUrl(urlHelper, null, routeValues); + } + + public static string SubdomainRouteUrl(this UrlHelper urlHelper, RouteValueDictionary routeValues) + { + return SubdomainRouteUrl(urlHelper, null, routeValues); + } + + public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName) + { + return SubdomainRouteUrl(urlHelper, routeName, (object)null); + } + + public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName, object routeValues) + { + return SubdomainRouteUrl(urlHelper, routeName, routeValues, null); + } + + public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName, + RouteValueDictionary routeValues) + { + string baseUrl = GetDomainBase(urlHelper, routeName, null, null, routeValues); + return BuildUri(baseUrl, urlHelper.RouteUrl(routeName, routeValues)); + } + + public static string SubdomainRouteUrl(this UrlHelper urlHelper, string routeName, object routeValues, + string protocol) + { + var r = new RouteValueDictionary(routeValues); + string baseUrl = GetDomainBase(urlHelper, routeName, null, null, r, protocol); + return BuildUri(baseUrl, urlHelper.RouteUrl(routeName, routeValues)); + } + + /// + /// Gets the domain base url. + /// + /// The URL helper. + /// Name of the route. + /// Name of the action. + /// Name of the controller. + /// The route values. + /// The schema. + /// + private static string GetDomainBase(UrlHelper urlHelper, string routeName, string actionName, + string controllerName, RouteValueDictionary routeValues, + string schema = null) + { + //baseUrl is the return value which by default is an empty string + string baseUrl = string.Empty; + + //just a shortcut variable so we don't have to have this the below line eight million times + Uri currentUrl = urlHelper.RequestContext.HttpContext.Request.Url; + + //get the desired route using a copy of MS internal methods + RouteValueDictionary values = MergeRouteValues(actionName, controllerName, urlHelper.RequestContext.RouteData.Values, routeValues, true); + VirtualPathData virtualPathForArea = urlHelper.RouteCollection.GetVirtualPathForArea(urlHelper.RequestContext, routeName, values); + if (virtualPathForArea == null) + { + return baseUrl; + } + + //if not a AttributeRoute or the current url is funny then nothing we can do so move on + var route = virtualPathForArea.Route as IAttributeRoute; + if (route != null && currentUrl != null && !string.IsNullOrWhiteSpace(currentUrl.OriginalString)) + { + //get the current domain via the current Uri. + string host = currentUrl.GetLeftPart(UriPartial.Authority).Replace(currentUrl.GetLeftPart(UriPartial.Scheme), string.Empty); + + IPAddress ip; + //if the port exists in the host remove it so that we don't run into trouble with the IPAddress parsing + if (host.Contains(":")) + { + host = host.Substring(0, host.IndexOf(":", StringComparison.Ordinal)); + } + + //if an ip then no point in building a subdomain for it + if (IPAddress.TryParse(host, out ip)) + { + return string.Empty; + } + + //save the current host for comparisons later + string currentHost = host; + + //which protocol schema to use. i.e. http, https + string scheme = schema ?? currentUrl.Scheme; + + //what is the current port. needed if non-standard + int port = currentUrl.Port; + + //is the port a standard port? + bool useDefaultPort = port == 80 || port == 443; + + //need the default subdomain incase we are going from one subdomain method to a non-subdomain method + string defaultSubdomain = string.Empty; + if (route.DataTokens.Any(x => x.Key.Equals("defaultSubdomain"))) + { + defaultSubdomain = route.DataTokens["defaultSubdomain"].ToString(); + } + + //if the host contains a dot we need to remove the subdomain if it is in the list of ones to remove + if (host.Contains(".")) + { + //get all registered subdomains + List subdomains = + urlHelper.RouteCollection.Where(x => x is IAttributeRoute) + .Cast() + .Where(x => x.Subdomain.HasValue()) + .Select(x => x.Subdomain) + .Distinct() + .ToList(); + + //also add the default subdomain from the current route + if (!string.IsNullOrWhiteSpace(defaultSubdomain) && !subdomains.Contains(defaultSubdomain)) + { + subdomains.Add(defaultSubdomain); + } + + //strips subdomain information off of current (if matching a current one) + string subDomainSection = host.Split('.')[0]; + foreach ( + string subdomain in + subdomains.Where( + subdomain => + subDomainSection.Equals(subdomain, StringComparison.InvariantCultureIgnoreCase))) + { + host = host.Replace(string.Format("{0}.", subdomain), string.Empty); + break; + } + } + + //if not a subdomain then don't build the url. instead build it to the default subdomain + if (!string.IsNullOrWhiteSpace(route.Subdomain)) + { + //if host already starts with subdomain then skip building the url + if (!currentHost.StartsWith(route.Subdomain)) + { + baseUrl = string.Format("{0}://{1}.{2}", scheme, route.Subdomain, host); + } + } + else + { + //no subdomain so we should add the default subdomain unless it is localhost + var gotoSubdomain = string.Empty; + if (!host.Equals("localhost", StringComparison.InvariantCultureIgnoreCase) && !string.IsNullOrWhiteSpace(defaultSubdomain)) + { + gotoSubdomain = string.Format("{0}.", defaultSubdomain); + } + baseUrl = string.Format("{0}://{1}{2}", scheme, gotoSubdomain, host); + } + + //not using a standard port so if the baseurl has a value then append on the port + if (!string.IsNullOrWhiteSpace(baseUrl) && !useDefaultPort) + { + baseUrl = string.Format("{0}:{1}", baseUrl, port); + } + } + return baseUrl; + } + + private static string BuildUri(string baseUrl, string relativeUrl) + { + if (string.IsNullOrWhiteSpace(baseUrl) && string.IsNullOrWhiteSpace(relativeUrl)) + { + return string.Empty; + } + if (string.IsNullOrWhiteSpace(baseUrl)) + { + return relativeUrl; + } + if (string.IsNullOrWhiteSpace(relativeUrl)) + { + return baseUrl; + } + if (!relativeUrl.StartsWith("/")) + { + relativeUrl = "/" + relativeUrl; + } + return string.Format("{0}{1}", baseUrl, relativeUrl); + } + + public static RouteValueDictionary GetRouteValues(RouteValueDictionary routeValues) + { + return routeValues == null ? new RouteValueDictionary() : new RouteValueDictionary(routeValues); + } + + public static RouteValueDictionary MergeRouteValues(string actionName, string controllerName, + RouteValueDictionary implicitRouteValues, + RouteValueDictionary routeValues, + bool includeImplicitMvcValues) + { + var routeValueDictionary = new RouteValueDictionary(); + if (includeImplicitMvcValues) + { + object obj; + if (implicitRouteValues != null && implicitRouteValues.TryGetValue("action", out obj)) + { + routeValueDictionary["action"] = obj; + } + if (implicitRouteValues != null && implicitRouteValues.TryGetValue("controller", out obj)) + { + routeValueDictionary["controller"] = obj; + } + } + if (routeValues != null) + { + foreach (var keyValuePair in GetRouteValues(routeValues)) + { + routeValueDictionary[keyValuePair.Key] = keyValuePair.Value; + } + } + if (actionName != null) + { + routeValueDictionary["action"] = actionName; + } + if (controllerName != null) + { + routeValueDictionary["controller"] = controllerName; + } + return routeValueDictionary; + } + } +} \ No newline at end of file diff --git a/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs b/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs index 92eba8b..ae5d950 100644 --- a/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs +++ b/src/AttributeRouting.Web.Mvc/Framework/AttributeRoute.cs @@ -1,113 +1,156 @@ -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Routing; -using AttributeRouting.Framework; -using AttributeRouting.Helpers; - -namespace AttributeRouting.Web.Mvc.Framework -{ - /// - /// Route to use for ASP.NET Mvc or Web API web-hosted routes. - /// - public class AttributeRoute : Route, IAttributeRoute - { - private readonly AttributeRoutingConfiguration _configuration; - - /// - /// Route used by the AttributeRouting framework in web projects. - /// - public AttributeRoute(string url, - RouteValueDictionary defaults, - RouteValueDictionary constraints, - RouteValueDictionary dataTokens, - AttributeRoutingConfiguration configuration) - : base(url, defaults, constraints, dataTokens, configuration.RouteHandlerFactory()) - { - _configuration = configuration; - } - - public string RouteName { get; set; } - - public string CultureName { get; set; } - - public List MappedSubdomains { get; set; } - - public string Subdomain { get; set; } - - public bool? UseLowercaseRoute { get; set; } - - public bool? PreserveCaseForUrlParameters { get; set; } - - public bool? AppendTrailingSlash { get; set; } - - IDictionary IAttributeRoute.DataTokens - { - get { return DataTokens; } - set { DataTokens = new RouteValueDictionary(value); } - } - - IDictionary IAttributeRoute.Constraints - { - get { return Constraints; } - set { Constraints = new RouteValueDictionary(value); } - } - - IDictionary IAttributeRoute.Defaults - { - get { return Defaults; } - set { Defaults = new RouteValueDictionary(value); } - } - - public IEnumerable Translations { get; set; } - - public IAttributeRoute DefaultRouteContainer { get; set; } - - public override RouteData GetRouteData(HttpContextBase httpContext) - { - // Optimize matching by comparing the static left part of the route url with the requested path. - var requestedPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo; - if (!this.IsLeftPartOfUrlMatched(requestedPath)) - return null; - - // Let the underlying route match, and if it does, then add a few more constraints. - var routeData = base.GetRouteData(httpContext); - if (routeData == null) - return null; - - // Constrain by subdomain if configured - var host = httpContext.SafeGet(ctx => ctx.Request.Headers["host"]); - if (!this.IsSubdomainMatched(host, _configuration)) - return null; - - // Constrain by culture name if configured - var currentUICultureName = _configuration.CurrentUICultureResolver(httpContext, routeData); - if (!this.IsCultureNameMatched(currentUICultureName, _configuration)) - return null; - - return routeData; - } - - public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) - { - // Let the underlying route do its thing, and if it does, then add some functionality on top. - var virtualPathData = base.GetVirtualPath(requestContext, values); - if (virtualPathData == null) - return null; - - // Translate this path if a translation is available. - if (_configuration.TranslationProviders.Any()) - { - virtualPathData = - this.GetTranslatedVirtualPath(t => ((Route)t).GetVirtualPath(requestContext, values)) - ?? virtualPathData; - } - - // Lowercase, append trailing slash, etc. - var virtualPath = this.GetFinalVirtualPath(virtualPathData.VirtualPath, _configuration); - virtualPathData.VirtualPath = virtualPath; - - return virtualPathData; - } - } -} +using System; +using System.Collections.Generic; +using System.Web; +using System.Web.Routing; +using AttributeRouting.Framework; +using AttributeRouting.Helpers; + +namespace AttributeRouting.Web.Mvc.Framework +{ + /// + /// Route to use for ASP.NET MVC routes. + /// + public class AttributeRoute : Route, IAttributeRoute + { + private const string RequestedPathKey = "__AttributeRouting:RequestedPath"; + private const string RequestedSubdomainKey = "__AttributeRouting:RequestedSubdomain"; + private const string CurrentUICultureNameKey = "__AttributeRouting:CurrentUICulture"; + + private readonly Configuration _configuration; + private readonly AttributeRouteVisitor _visitor; + + /// + /// Route used by the AttributeRouting framework in web projects. + /// + public AttributeRoute(string url, + RouteValueDictionary defaults, + RouteValueDictionary constraints, + RouteValueDictionary dataTokens, + Configuration configuration) + : base(url, defaults, constraints, dataTokens, configuration.RouteHandlerFactory()) + { + _configuration = configuration; + _visitor = new AttributeRouteVisitor(this, configuration); + QueryStringConstraints = new RouteValueDictionary(); + QueryStringDefaults = new RouteValueDictionary(); + } + + public bool? AppendTrailingSlash { get; set; } + + IDictionary IAttributeRoute.Constraints + { + get { return Constraints; } + set { Constraints = new RouteValueDictionary(value); } + } + + public string CultureName { get; set; } + + IDictionary IAttributeRoute.DataTokens + { + get { return DataTokens; } + set { DataTokens = new RouteValueDictionary(value); } + } + + IDictionary IAttributeRoute.Defaults + { + get { return Defaults; } + set { Defaults = new RouteValueDictionary(value); } + } + + public List MappedSubdomains { get; set; } + + public bool? PreserveCaseForUrlParameters { get; set; } + + public IDictionary QueryStringConstraints { get; set; } + + public IDictionary QueryStringDefaults { get; set; } + + public string RouteName { get; set; } + + public string Subdomain { get; set; } + + public bool? UseLowercaseRoute { get; set; } + + public IAttributeRoute SourceLanguageRoute { get; set; } + + public IEnumerable Translations { get; set; } + + public override RouteData GetRouteData(HttpContextBase httpContext) + { + // Optimize matching by comparing the static left part of the route url with the requested path. + var requestedPath = GetCachedValue(httpContext, RequestedPathKey, () => httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo); + if (!_visitor.IsStaticLeftPartOfUrlMatched(requestedPath)) + { + return null; + } + + // Let the underlying route match, and if it does, then add a few more constraints. + var routeData = base.GetRouteData(httpContext); + if (routeData == null) + { + return null; + } + + // Constrain by querystring param if there are any. + if (!_visitor.ProcessQueryStringConstraints((constraint, parameterName) => ProcessConstraint(httpContext, constraint, parameterName, routeData.Values, RouteDirection.IncomingRequest))) + { + return null; + } + + // Constrain by subdomain if configured. + var requestedSubdomain = GetCachedValue(httpContext, RequestedSubdomainKey, () => _configuration.SubdomainParser(httpContext.SafeGet(c => c.Request.Headers["host"]))); + if (!_visitor.IsSubdomainMatched(requestedSubdomain)) + { + return null; + } + + // Constrain by culture name if configured. + var currentUICultureName = GetCachedValue(httpContext, CurrentUICultureNameKey, () => _configuration.CurrentUICultureResolver(httpContext, routeData)); + if (!_visitor.IsCultureNameMatched(currentUICultureName)) + { + return null; + } + + return routeData; + } + + public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) + { + // Add querystring default values if applicable. + _visitor.AddQueryStringDefaultsToRouteValues(values); + + // Let the underlying route do its thing. + var virtualPathData = base.GetVirtualPath(requestContext, values); + if (virtualPathData == null) + { + return null; + } + + // Translate this path if a translation is available. + var translatedVirtualPath = _visitor.GetTranslatedVirtualPath(t => ((Route)t).GetVirtualPath(requestContext, values)); + if (translatedVirtualPath != null) + { + virtualPathData = translatedVirtualPath; + } + + // Lowercase, append trailing slash, etc. + virtualPathData.VirtualPath = _visitor.GetFinalVirtualPath(virtualPathData.VirtualPath); + + return virtualPathData; + } + + private static T GetCachedValue(HttpContextBase context, object key, Func initializeValue) + { + // Fetch the item from the http context if it's been stored for the request. + if (context.Items.Contains(key)) + { + return (T)context.Items[key]; + } + + // Cache the value and return it. + var value = initializeValue(); + context.Items.Add(key, value); + return value; + } + } +} diff --git a/src/AttributeRouting.Web.Mvc/Framework/AttributeRouteFactory.cs b/src/AttributeRouting.Web.Mvc/Framework/AttributeRouteFactory.cs new file mode 100644 index 0000000..5c07004 --- /dev/null +++ b/src/AttributeRouting.Web.Mvc/Framework/AttributeRouteFactory.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.Web.Routing; +using AttributeRouting.Framework; + +namespace AttributeRouting.Web.Mvc.Framework +{ + internal class AttributeRouteFactory : IAttributeRouteFactory + { + private readonly Configuration _configuration; + + public AttributeRouteFactory(Configuration configuration) + { + _configuration = configuration; + } + + public IEnumerable CreateAttributeRoutes(string url, IDictionary defaults, IDictionary constraints, IDictionary dataTokens) + { + yield return new AttributeRoute(url, + new RouteValueDictionary(defaults), + new RouteValueDictionary(constraints), + new RouteValueDictionary(dataTokens), + _configuration); + } + } +} diff --git a/src/AttributeRouting.Web.Mvc/Framework/Factories/AttributeRouteFactory.cs b/src/AttributeRouting.Web.Mvc/Framework/Factories/AttributeRouteFactory.cs deleted file mode 100644 index 0b384ec..0000000 --- a/src/AttributeRouting.Web.Mvc/Framework/Factories/AttributeRouteFactory.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Generic; -using System.Web.Routing; -using AttributeRouting.Framework; -using AttributeRouting.Framework.Factories; - -namespace AttributeRouting.Web.Mvc.Framework.Factories -{ - internal class AttributeRouteFactory : IAttributeRouteFactory - { - private readonly AttributeRoutingConfiguration _configuration; - - public AttributeRouteFactory(AttributeRoutingConfiguration configuration) - { - _configuration = configuration; - } - - public IAttributeRoute CreateAttributeRoute(string url, - IDictionary defaults, - IDictionary constraints, - IDictionary dataTokens) - { - return new AttributeRoute(url, - new RouteValueDictionary(defaults), - new RouteValueDictionary(constraints), - new RouteValueDictionary(dataTokens), - _configuration); - } - } -} diff --git a/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs b/src/AttributeRouting.Web.Mvc/Framework/RouteConstraintFactory.cs similarity index 62% rename from src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs rename to src/AttributeRouting.Web.Mvc/Framework/RouteConstraintFactory.cs index fc0180d..e3cf992 100644 --- a/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteConstraintFactory.cs +++ b/src/AttributeRouting.Web.Mvc/Framework/RouteConstraintFactory.cs @@ -1,60 +1,64 @@ -using System; -using System.Linq; -using System.Text.RegularExpressions; -using System.Web.Routing; -using AttributeRouting.Constraints; -using AttributeRouting.Framework; -using AttributeRouting.Framework.Factories; -using AttributeRouting.Helpers; -using AttributeRouting.Web.Constraints; -using AttributeRouting.Web.Mvc.Constraints; - -namespace AttributeRouting.Web.Mvc.Framework.Factories -{ - internal class RouteConstraintFactory : IRouteConstraintFactory - { - private readonly AttributeRoutingConfiguration _configuration; - - public RouteConstraintFactory(AttributeRoutingConfiguration configuration) - { - _configuration = configuration; - } - - public RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, RegexOptions options = RegexOptions.None) - { - return new RegexRouteConstraint(pattern, options); - } - - public IRestfulHttpMethodConstraint CreateRestfulHttpMethodConstraint(string[] httpMethods) - { - return new RestfulHttpMethodConstraint(httpMethods); - } - - public object CreateInlineRouteConstraint(string name, params object[] parameters) - { - var inlineRouteConstraints = _configuration.InlineRouteConstraints; - if (inlineRouteConstraints.ContainsKey(name)) - { - var type = inlineRouteConstraints[name]; - - if (!typeof(IRouteConstraint).IsAssignableFrom(type)) - throw new AttributeRoutingException( - "The constraint \"{0}\" must implement System.Web.Routing.IRouteConstraint".FormatWith(type.FullName)); - - return Activator.CreateInstance(type, parameters); - } - - return null; - } - - public ICompoundRouteConstraintWrapper CreateCompoundRouteConstraint(params object[] constraints) - { - return new CompoundRouteConstraintWrapper(constraints.Cast().ToArray()); - } - - public IOptionalRouteConstraintWrapper CreateOptionalRouteConstraint(object constraint) - { - return new OptionalRouteConstraintWrapper((IRouteConstraint)constraint); - } - } +using System; +using System.Linq; +using System.Text.RegularExpressions; +using System.Web.Routing; +using AttributeRouting.Constraints; +using AttributeRouting.Framework; +using AttributeRouting.Helpers; +using AttributeRouting.Web.Constraints; +using AttributeRouting.Web.Mvc.Constraints; + +namespace AttributeRouting.Web.Mvc.Framework +{ + internal class RouteConstraintFactory : IRouteConstraintFactory + { + private readonly Configuration _configuration; + + public RouteConstraintFactory(Configuration configuration) + { + _configuration = configuration; + } + + public RegexRouteConstraintBase CreateRegexRouteConstraint(string pattern, RegexOptions options = RegexOptions.None) + { + return new RegexRouteConstraint(pattern, options); + } + + public IInboundHttpMethodConstraint CreateInboundHttpMethodConstraint(string[] httpMethods) + { + return new InboundHttpMethodConstraint(httpMethods); + } + + public object CreateInlineRouteConstraint(string name, params object[] parameters) + { + var inlineRouteConstraints = _configuration.InlineRouteConstraints; + if (inlineRouteConstraints.ContainsKey(name)) + { + var type = inlineRouteConstraints[name]; + + if (!typeof(IRouteConstraint).IsAssignableFrom(type)) + throw new AttributeRoutingException( + "The constraint \"{0}\" must implement System.Web.Routing.IRouteConstraint".FormatWith(type.FullName)); + + return Activator.CreateInstance(type, parameters); + } + + return null; + } + + public ICompoundRouteConstraint CreateCompoundRouteConstraint(params object[] constraints) + { + return new CompoundRouteConstraint(constraints.Cast().ToArray()); + } + + public IOptionalRouteConstraint CreateOptionalRouteConstraint(object constraint) + { + return new OptionalRouteConstraint((IRouteConstraint)constraint); + } + + public IQueryStringRouteConstraint CreateQueryStringRouteConstraint(object constraint) + { + return new QueryStringRouteConstraint((IRouteConstraint)constraint); + } + } } \ No newline at end of file diff --git a/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteParameterFactory.cs b/src/AttributeRouting.Web.Mvc/Framework/RouteParameterFactory.cs similarity index 63% rename from src/AttributeRouting.Web.Mvc/Framework/Factories/RouteParameterFactory.cs rename to src/AttributeRouting.Web.Mvc/Framework/RouteParameterFactory.cs index 187de2a..7ae9dfe 100644 --- a/src/AttributeRouting.Web.Mvc/Framework/Factories/RouteParameterFactory.cs +++ b/src/AttributeRouting.Web.Mvc/Framework/RouteParameterFactory.cs @@ -1,13 +1,13 @@ -using System.Web.Mvc; -using AttributeRouting.Framework.Factories; - -namespace AttributeRouting.Web.Mvc.Framework.Factories -{ - internal class RouteParameterFactory : IParameterFactory - { - public object Optional() - { - return UrlParameter.Optional; - } - } -} +using System.Web.Mvc; +using AttributeRouting.Framework; + +namespace AttributeRouting.Web.Mvc.Framework +{ + internal class RouteParameterFactory : IParameterFactory + { + public object Optional() + { + return UrlParameter.Optional; + } + } +} diff --git a/src/AttributeRouting.Web.Mvc/GETAttribute.cs b/src/AttributeRouting.Web.Mvc/GETAttribute.cs index 903982f..5adae1b 100644 --- a/src/AttributeRouting.Web.Mvc/GETAttribute.cs +++ b/src/AttributeRouting.Web.Mvc/GETAttribute.cs @@ -7,6 +7,12 @@ namespace AttributeRouting.Web.Mvc /// public class GETAttribute : RouteAttribute { + /// + /// Specify a route for a GET request. + /// The route URL will be the name of the action. + /// + public GETAttribute() : base(HttpVerbs.Get, HttpVerbs.Head) {} + /// /// Specify a route for a GET request. /// diff --git a/src/AttributeRouting.Web.Mvc/POSTAttribute.cs b/src/AttributeRouting.Web.Mvc/POSTAttribute.cs index 5abc877..eb6bf39 100644 --- a/src/AttributeRouting.Web.Mvc/POSTAttribute.cs +++ b/src/AttributeRouting.Web.Mvc/POSTAttribute.cs @@ -7,6 +7,12 @@ namespace AttributeRouting.Web.Mvc /// public class POSTAttribute : RouteAttribute { + /// + /// Specify a route for a POST request. + /// The route URL will be the name of the action. + /// + public POSTAttribute() : base(HttpVerbs.Post) {} + /// /// Specify a route for a POST request. /// diff --git a/src/AttributeRouting.Web.Mvc/PUTAttribute.cs b/src/AttributeRouting.Web.Mvc/PUTAttribute.cs index f179bbf..71f9726 100644 --- a/src/AttributeRouting.Web.Mvc/PUTAttribute.cs +++ b/src/AttributeRouting.Web.Mvc/PUTAttribute.cs @@ -7,6 +7,12 @@ namespace AttributeRouting.Web.Mvc /// public class PUTAttribute : RouteAttribute { + /// + /// Specify a route for a PUT request. + /// The route URL will be the name of the action. + /// + public PUTAttribute() : base(HttpVerbs.Put) {} + /// /// Specify a route for a PUT request. /// diff --git a/src/AttributeRouting.Web.Mvc/RestfulRouteConventionAttribute.cs b/src/AttributeRouting.Web.Mvc/RestfulRouteConventionAttribute.cs index 5a10329..4ef35b1 100644 --- a/src/AttributeRouting.Web.Mvc/RestfulRouteConventionAttribute.cs +++ b/src/AttributeRouting.Web.Mvc/RestfulRouteConventionAttribute.cs @@ -1,72 +1,72 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using AttributeRouting.Framework; -using AttributeRouting.Helpers; - -namespace AttributeRouting.Web.Mvc -{ - /// - /// Automatically generates RESTful-style routes for controller actions matching - /// Index, New, Create, Show, Edit, Update, Delete, and Destroy. - /// - public class RestfulRouteConventionAttribute : RouteConventionAttributeBase - { - // Setup conventions - private static readonly List Conventions = new List - { - new RestfulRouteConventionInfo("Index", "GET", ""), - new RestfulRouteConventionInfo("New", "GET", "New"), - new RestfulRouteConventionInfo("Create", "POST", ""), - new RestfulRouteConventionInfo("Show", "GET", "{id}"), - new RestfulRouteConventionInfo("Edit", "GET", "{id}/Edit"), - new RestfulRouteConventionInfo("Update", "PUT", "{id}"), - new RestfulRouteConventionInfo("Delete", "GET", "{id}/Delete"), - new RestfulRouteConventionInfo("Destroy", "DELETE", "{id}") - }; - - public override IEnumerable GetRouteAttributes(MethodInfo actionMethod) - { - var convention = Conventions.SingleOrDefault(c => c.ActionName == actionMethod.Name); - if (convention != null) - yield return BuildRouteAttribute(convention); - } - - public override string GetDefaultRoutePrefix(MethodInfo actionMethod) - { - return actionMethod.DeclaringType.GetControllerName(); - } - - private IRouteAttribute BuildRouteAttribute(RestfulRouteConventionInfo convention) - { - switch (convention.HttpMethod) - { - case "GET": - return new GETAttribute(convention.Url); - case "POST": - return new POSTAttribute(convention.Url); - case "PUT": - return new PUTAttribute(convention.Url); - case "DELETE": - return new DELETEAttribute(convention.Url); - default: - throw new AttributeRoutingException("Unknown HTTP method \"{0}\".".FormatWith(convention.HttpMethod)); - } - } - - private class RestfulRouteConventionInfo - { - public RestfulRouteConventionInfo(string actionName, string httpMethod, string url) - { - ActionName = actionName; - HttpMethod = httpMethod; - Url = url; - } - - public string ActionName { get; private set; } - public string HttpMethod { get; private set; } - public string Url { get; private set; } - } - } -} +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using AttributeRouting.Framework; +using AttributeRouting.Helpers; + +namespace AttributeRouting.Web.Mvc +{ + /// + /// Automatically generates RESTful-style routes for controller actions matching + /// Index, New, Create, Show, Edit, Update, Delete, and Destroy. + /// + public class RestfulRouteConventionAttribute : RouteConventionAttributeBase + { + // Setup conventions + private static readonly List Conventions = new List + { + new RestfulRouteConventionInfo("Index", "GET", ""), + new RestfulRouteConventionInfo("New", "GET", "New"), + new RestfulRouteConventionInfo("Create", "POST", ""), + new RestfulRouteConventionInfo("Show", "GET", "{id}"), + new RestfulRouteConventionInfo("Edit", "GET", "{id}/Edit"), + new RestfulRouteConventionInfo("Update", "PUT", "{id}"), + new RestfulRouteConventionInfo("Delete", "GET", "{id}/Delete"), + new RestfulRouteConventionInfo("Destroy", "DELETE", "{id}") + }; + + public override IEnumerable GetRouteAttributes(MethodInfo actionMethod) + { + var convention = Conventions.SingleOrDefault(c => c.ActionName == actionMethod.Name); + if (convention != null) + yield return BuildRouteAttribute(convention); + } + + public override IEnumerable GetDefaultRoutePrefixes(Type controllerType) + { + yield return new RoutePrefixAttribute(controllerType.GetControllerName()); + } + + private IRouteAttribute BuildRouteAttribute(RestfulRouteConventionInfo convention) + { + switch (convention.HttpMethod) + { + case "GET": + return new GETAttribute(convention.Url); + case "POST": + return new POSTAttribute(convention.Url); + case "PUT": + return new PUTAttribute(convention.Url); + case "DELETE": + return new DELETEAttribute(convention.Url); + default: + throw new AttributeRoutingException("Unknown HTTP method \"{0}\".".FormatWith(convention.HttpMethod)); + } + } + + private class RestfulRouteConventionInfo + { + public RestfulRouteConventionInfo(string actionName, string httpMethod, string url) + { + ActionName = actionName; + HttpMethod = httpMethod; + Url = url; + } + + public string ActionName { get; private set; } + public string HttpMethod { get; private set; } + public string Url { get; private set; } + } + } +} diff --git a/src/AttributeRouting.Web.Mvc/RouteAttribute.cs b/src/AttributeRouting.Web.Mvc/RouteAttribute.cs index bc5e180..7f73314 100644 --- a/src/AttributeRouting.Web.Mvc/RouteAttribute.cs +++ b/src/AttributeRouting.Web.Mvc/RouteAttribute.cs @@ -12,19 +12,39 @@ namespace AttributeRouting.Web.Mvc [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] public class RouteAttribute : ActionMethodSelectorAttribute, IRouteAttribute { + /// + /// Specify the route information for an action. + /// The route URL will be the name of the action. + /// + public RouteAttribute() + { + HttpMethods = new string[0]; + ActionPrecedence = int.MaxValue; + ControllerPrecedence = int.MaxValue; + SitePrecedence = int.MaxValue; + } + /// /// Specify the route information for an action. /// /// The url that is associated with this action public RouteAttribute(string routeUrl) + : this() { if (routeUrl == null) throw new ArgumentNullException("routeUrl"); RouteUrl = routeUrl; - HttpMethods = new string[0]; - ActionPrecedence = int.MaxValue; - ControllerPrecedence = int.MaxValue; - SitePrecedence = int.MaxValue; + } + + /// + /// Specify the route information for an action. + /// The route URL will be the name of the action. + /// + /// The httpMethods against which to constrain the route + public RouteAttribute(params HttpVerbs[] allowedMethods) + : this() + { + HttpMethods = allowedMethods.Select(m => m.ToString().ToUpperInvariant()).ToArray(); } /// @@ -64,7 +84,11 @@ public int Precedence public string RouteName { get; set; } - public bool IsAbsoluteUrl { get; set; } + public bool IsAbsoluteUrl + { + get { return IgnoreAreaUrl && IgnoreRoutePrefix; } + set { IgnoreAreaUrl = IgnoreRoutePrefix = value; } + } public string TranslationKey { get; set; } @@ -91,14 +115,22 @@ public bool AppendTrailingSlash } public bool? AppendTrailingSlashFlag { get; private set; } + + public bool IgnoreRoutePrefix { get; set; } + + public bool IgnoreAreaUrl { get; set; } public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { + if (controllerContext == null) throw new ArgumentNullException("controllerContext"); + + // If not constrained by a method, then accept always! if (!HttpMethods.Any()) return true; - var method = controllerContext.HttpContext.Request.GetHttpMethodOverride(); - return HttpMethods.Any(m => m.ValueEquals(method)); + var httpMethod = controllerContext.HttpContext.Request.GetHttpMethodOverride(); + + return HttpMethods.Any(m => m.ValueEquals(httpMethod)); } } } \ No newline at end of file diff --git a/src/AttributeRouting.Web.Mvc/RouteCollectionExtensions.cs b/src/AttributeRouting.Web.Mvc/RouteCollectionExtensions.cs index 5a511cc..0f55047 100644 --- a/src/AttributeRouting.Web.Mvc/RouteCollectionExtensions.cs +++ b/src/AttributeRouting.Web.Mvc/RouteCollectionExtensions.cs @@ -18,7 +18,7 @@ public static class RouteCollectionExtensions /// public static void MapAttributeRoutes(this RouteCollection routes) { - var configuration = new AttributeRoutingConfiguration(); + var configuration = new Configuration(); configuration.AddRoutesFromAssembly(Assembly.GetCallingAssembly()); routes.MapAttributeRoutesInternal(configuration); @@ -30,9 +30,9 @@ public static void MapAttributeRoutes(this RouteCollection routes) /// /// /// The initialization action that builds the configuration object - public static void MapAttributeRoutes(this RouteCollection routes, Action configurationAction) + public static void MapAttributeRoutes(this RouteCollection routes, Action configurationAction) { - var configuration = new AttributeRoutingConfiguration(); + var configuration = new Configuration(); configurationAction.Invoke(configuration); routes.MapAttributeRoutesInternal(configuration); @@ -44,16 +44,17 @@ public static void MapAttributeRoutes(this RouteCollection routes, Action /// /// The configuration object - public static void MapAttributeRoutes(this RouteCollection routes, AttributeRoutingConfiguration configuration) + public static void MapAttributeRoutes(this RouteCollection routes, Configuration configuration) { routes.MapAttributeRoutesInternal(configuration); } - private static void MapAttributeRoutesInternal(this RouteCollection routes, AttributeRoutingConfiguration configuration) + private static void MapAttributeRoutesInternal(this RouteCollection routes, Configuration configuration) { - var generatedRoutes = new RouteBuilder(configuration).BuildAllRoutes(); - - generatedRoutes.ToList().ForEach(r => routes.Add(r.RouteName, (AttributeRoute)r)); + new RouteBuilder(configuration).BuildAllRoutes() + .Cast() + .ToList() + .ForEach(r => routes.Add(r.RouteName, r)); } } } \ No newline at end of file diff --git a/src/AttributeRouting.Web.Mvc/packages.config b/src/AttributeRouting.Web.Mvc/packages.config new file mode 100644 index 0000000..77411dd --- /dev/null +++ b/src/AttributeRouting.Web.Mvc/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/AttributeRouting.Web/AttributeRouting.Web.csproj b/src/AttributeRouting.Web/AttributeRouting.Web.csproj index b8c46b7..9bb3a21 100644 --- a/src/AttributeRouting.Web/AttributeRouting.Web.csproj +++ b/src/AttributeRouting.Web/AttributeRouting.Web.csproj @@ -36,6 +36,12 @@ bin\Release\AttributeRouting.Web.xml 1591, 1587 + + true + + + AttributeRouting.snk + @@ -54,32 +60,38 @@ SharedAssemblyInfo.cs - + + + - - + + + + + + + + + + - {871A79CF-C705-4C6B-8938-F9AA1E02AEA4} + {871a79cf-c705-4c6b-8938-f9aa1e02aea4} AttributeRouting - - - -