Обновлено 24 марта 2016
Кравченко Виктор

Маршрутизация (роутинг) поддоменов в ASP.NET MVC (Subdomain Routing)

VB.NET MVC ASP.NET Visual Studio 2013 Web MVC 5
01

Речь в статье пойдет о поддоменах в ASP.NET MVC5, о том какие цели может преследовать использование данного приема и какие варианты реализации маршрутизации (роутинга) поддоменов существуют. Также будут указаны особенности использования каждого из методов.

02

В каждом из предложенных способов будет использоваться объект HttpContextBase, который содержит входную строку URL, и который будет использоваться для получения имени поддомена. Для получения поддомена будем использовать общую для всех методов функцию:

03 VB.NET
1
2
3
4
5
6
7
8
Public Function ПолучитьПоддомен(httpContext As HttpContextBase) As String Dim subdomain As String = httpContext.Request.Url.Host.Split("."c).First() Dim blacklist As String() = {"wwww", "codius", "localhost"} If Not String.IsNullOrEmpty(subdomain) AndAlso Not blacklist.Contains(subdomain) Then Return subdomain End If Return "" End Function
04

Вариант 1. В лоб.

Вариант, имеющий мало общего с темой статьи — это скорее костыль, при помощи которого можно организовать схожую функциональность. Он подразумевает получение и обработку поддомена в каждом методе (там где это нужно) при помощи функции ПолучитьПоддомен(HttpContextBase) и использование его для выполнения каких-либо действий:

05 VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<RequireHttps>
Public Class HomeController Inherits System.Web.Mvc.Controller
Function Index() As ActionResult Dim subdomain As String = ПолучитьПоддомен(HttpContext) If Not String.IsNullOrEmpty(subdomain) Then Select Case subdomain.ToLower Case "shop" ' Action Case "admin" ' Action Case "user" ' Action Case Else '... End Select End If Return View() End Function
Function About() As ActionResult ' ... End Function
End Class
06

Этот метод подойдет для тех случаев, в которых необходимо реализовать простую логику, принципиально не влияющую на работу приложения. Плюс этого метода также заключается в том, что вне зависимости от поддомена, статус авторизации сохраняется для всех поддоменов — это означает, что авторизовавшись один раз, вам не нужно будет авторизовываться снова в зоне действия другого поддомена.

07 На заметку:
Предложенные далее в статье подходы имеют особенность — применяемая в проекте система аутентификации будет воспринимать поддомены как разные сайты, вынуждая отдельно авторизовываться в каждом поддомене. Это может быть удобно в случаях использования имени пользователя в качестве поддомена, но крайне неприятно, если единый проект делится поддоменами на обособленные группы.
08

Вариант 2. Роутинг на основе валидности значений URL.

Здесь воспользуемся методом MapRoute, а именно его перегруженной версией — RouteCollectionExtensions.MapRoute (RouteCollection, String, String, Object, Object). Теперь вместо стандартного правила:

09 VB.NET
1
2
3
4
5
routes.MapRoute( _ name:="ArticlesList", _ url:="articles", _ defaults:=New With {.controller = "Articles", .action = "Index"} _ )
10

Мы будем использовать правило с проверкой названия контроллера, в которой будем определять поддомен, и в случае наличия такового — передавать его имя в качестве параметра в метод заданного контроллера:

Основано на коде RyannosaurusRex
11 VB.NET
1
2
3
4
5
6
7
8
9
10
Public Class SubdomainRouteConstraint Implements IRouteConstraint
Public Function Match(httpContext As HttpContextBase, route As Route, parameterName As String, values As RouteValueDictionary, routeDirection As RouteDirection) As Boolean Implements IRouteConstraint.Match Dim subdomain As String = ПолучитьПоддомен(httpContext) If String.IsNullOrEmpty(subdomain) Then Return False values.Add("subdomain", subdomain) ' Добавляем параметр Return True End Function
End Class
SubdomainRouteConstraint.vb
12

Измененное правило роутинга:

13 VB.NET
1
2
3
4
5
6
7
8
9
10
11
Public Sub RegisterRoutes(ByVal routes As RouteCollection) routes.IgnoreRoute("{resource}.axd/{*pathInfo}") ' ... routes.MapRoute( _ name:="ArticlesList", _ url:="articles", _ defaults:=New With {.controller = "Articles", .action = "Index"}, _
constraints:=New With {.controller = New SubdomainRouteConstraint()}
) ' ... End Sub
14

Значение полученного поддомена передается в метод контроллера:

15 VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Public Class ArticlesController Inherits System.Web.Mvc.Controller
Function Index(subdomain As String) As ActionResult ' Данные об имени поддомена в переменной subdomain ' Либо так: Dim subdomain As String = RouteData.Values("subdomain").ToString If Not String.IsNullOrEmpty(subdomain) Then Select Case subdomain.ToLower Case "shop" ' ... Case Else ' ... End Select End If Return View() End Function
End Class
16

Вариант 3. Кастомный маршрутизатор.

Этот способ может пригодиться, когда требуется реализовать более глобальную логику, включая функционал по генерации URL-адресов, либо когда в проекте предусмотрена глубокая иерархия поддоменов.

На основе статьи ASP.NET MVC Domain Routing
17

Для этого мы создадим свой обработчик маршрутов SubdomainRoute:

18 VB.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Public Class SubomainRoute Inherits Route Public Property Domain As String Public Sub New(domain As String, url As String, defaults As RouteValueDictionary) Me.New(domain, url, defaults, New MvcRouteHandler()) End Sub
Public Sub New(domain As String, url As String, defaults As Object) Me.New(domain, url, New RouteValueDictionary(defaults), New MvcRouteHandler()) End Sub
Public Sub New(domain As String, url As String, defaults As Object, routeHandler As IRouteHandler) Me.New(domain, url, New RouteValueDictionary(defaults), routeHandler) End Sub
Public Sub New(domain As String, url As String, defaults As RouteValueDictionary, routeHandler As IRouteHandler) MyBase.New(url, defaults, routeHandler) Me.Domain = domain End Sub
Public Overrides Function GetRouteData(httpContext As HttpContextBase) As RouteData Dim routeData As RouteData = Nothing
' Исходя из логики приложения формируем коллекцию параметров routeData ' либо, если правило не подходит - возвращаем Nothing
Return routeData End Function
Public Overrides Function GetVirtualPath(requestContext As RequestContext, values As RouteValueDictionary) As VirtualPathData ' Здесь программируем логику формирования URL-строки Return MyBase.GetVirtualPath(requestContext, values) End Function
End Class
19

Регистрируем правило в RouteConfig.vb:

20 VB.NET
1
2
routes.Add(New SubdomainRoute("{subdomain}.localhost", "articles", New With {.controller = "Articles", .action = "Index"}))
21

Запуск и тестирование на локальном компьютере — localhost.

При попытке запустить и протестировать проект на локальном компьютере, вас ждет разочарование в виде ошибки:

Описанная ниже процедура в целом дублирует, хотя и немного дополняет, информацию представленную в статье IIS Express – HTTP Error 400. The request hostname is invalid.
22

Bad Request - Invalid Hostname
HTTP Error 400. The request hostname is invalid.

23 На заметку:
Важно! Большинство описанных далее манипуляций потребуют прав администратора!
24

Во-первых, необходимо добавить указатели на поддомены в файле hosts (в Windows 7 — C:\Windows\System32\drivers\etc):

25 На заметку:
На компьютерах с установленным антивирусом, возможно стоит защита на изменение этого файла. Например, для Dr.Web Security Space 11.0, необходимо настроить реакцию на изменения в разделе Превентивная защита (Настройки → Компоненты защиты → Превентивная защита → Изменить параметры блокировки подозрительных действий → Файл HOSTS → Спрашивать/Разрешать), а также добавить файл HOSTS в Исключения ([i]Настройки → Исключения → Файлы и папки) для SpIDer Guard и Сканера.
26
1
2
3
4
127.0.0.1 localhost
127.0.0.1 user1.localhost
127.0.0.1 user2.localhost
# 127.0.0.1 ...
27

Во-вторых, нужно найти в файле applicationhost.config (%USERPROFILE%\My Documents\IISExpress\config) фрагмент, отвечающий за ваш проект, и добавить необходимые строки:

28 На заметку:
В случае, если проект новый, необходимо создать запись в файле applicationhost.config — для этого необходимо создать виртуальную папку проекта — нажать кнопку Create Virtual Directory (Project → Properties → Web)
29 XML
1
2
3
4
5
6
7
8
9
10
<site name="MyProject_v1.0" id="7"> <application path="/" applicationPool="Clr4IntegratedAppPool"> <virtualDirectory path="/" physicalPath="D:\Visual Studio 2013\Projects\MyProject_v1.0\MyProject_v1.0" /> </application> <bindings> <binding protocol="http" bindingInformation="*:33683:localhost" />
<binding protocol="http" bindingInformation="*:33683:user1.localhost" />
<binding protocol="http" bindingInformation="*:33683:user2.localhost" />
</bindings> </site>
30

Чтобы не перечислять все варианты (user1, user2, ...) можно обойтись одной универсальной строкой:

31 XML
1
2
3
4
5
6
7
8
9
<site name="MyProject_v1.0" id="7"> <application path="/" applicationPool="Clr4IntegratedAppPool"> <virtualDirectory path="/" physicalPath="D:\Visual Studio 2013\Projects\MyProject_v1.0\MyProject_v1.0" /> </application> <bindings> <binding protocol="http" bindingInformation="*:33683:localhost" />
<binding protocol="http" bindingInformation="*:33683:*" />
</bindings> </site>
32

Теперь, если запустить студию с правами администратора, то все будет работать.

33

Для того, чтобы все работало из студии, запущенной без прав администратора, нужно в командной строке зарезервировать для простых пользователей (non-administrator users and accounts) необходимые URL-адреса следующей командой:

34
netsh http add urlacl url="http://user1.localhost:33683/" user=everyone
Кавычки в URL можно опустить.
35

Здесь также можно зарегистрировать паттерн вместо конкретного адреса:

36
netsh http add urlacl url=http://*:33683/ user=everyone
37 На заметку:
В случае возникновения ошибки:
Сбой создания SDDL. Ошибка: 1332
Параметр задан неверно.
нужно просто заменить параметр user=everyone на русскоязычную версию user=Все:
netsh http add urlacl url="http://user1.localhost:33683/" user=Все
38

Результат: Резервирование URL-адрес добавлено успешно.

40

Похожие запросы:

  • Creating a subdomain
  • Simple multitenancy with ASP.NET MVC
  • Subdomains for a single application with ASP.NET MVC
  • ASP.NET MVC 5 routing with subdomain
comments powered by HyperComments