サブドメインに基づいてASP.NET MVCルートを作成することは可能ですか?


235

ルートを決定するためにサブドメイン情報を使用するASP.NET MVCルートを持つことは可能ですか?例えば:

  • user1 .domain.comは1つの場所に移動します
  • user2 .domain.comは別の場所に移動しますか?

または、これらの両方が同じコントローラー/ usernameパラメーターを持つアクションに移動するようにできますか?


マルチテナントアプリケーションにも同様の方法を実装しましたが、カスタムルートクラスではなく抽象ベースコントローラを使用しました。それに関する私のブログ投稿はここにあります
ルークサンプソン

6
このアプローチを検討してください:http://blog.tonywilliams.me.uk/asp-net-mvc-2-routing-subdomains-to-areas私はそれがより良い私のアプリにマルチテナントを導入するための他の回答よりであることが判明なぜなら、MVCエリアは、テナント固有のコントローラーとビューを体系的に導入するための優れた方法だからです。
trebormf

2
@trebormf-私はあなたがそれを答えとして追加するべきだと思います、これが私が結局私のソリューションの基礎として使用したものです。
Shagglez 2012

@Shagglez-ありがとう。それは答えでしたが、モデレーターが理解できない理由でコメントに変換しました。
trebormf 2012

5
トニーのようなものが壊れていました。私のために働いた。ここの1:blog.tonywilliams.me.uk/...
ロニーOverby

回答:


168

これを行うには、新しいルートを作成し、global.asaxのRegisterRoutesのルートコレクションに追加します。以下は、カスタムルートの非常に単純な例です。

public class ExampleRoute : RouteBase
{

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var url = httpContext.Request.Headers["HOST"];
        var index = url.IndexOf(".");

        if (index < 0)
            return null;

        var subDomain = url.Substring(0, index);

        if (subDomain == "user1")
        {
            var routeData = new RouteData(this, new MvcRouteHandler());
            routeData.Values.Add("controller", "User1"); //Goes to the User1Controller class
            routeData.Values.Add("action", "Index"); //Goes to the Index action on the User1Controller

            return routeData;
        }

        if (subDomain == "user2")
        {
            var routeData = new RouteData(this, new MvcRouteHandler());
            routeData.Values.Add("controller", "User2"); //Goes to the User2Controller class
            routeData.Values.Add("action", "Index"); //Goes to the Index action on the User2Controller

            return routeData;
        }

        return null;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        //Implement your formating Url formating here
        return null;
    }
}

1
詳細なサンプルに感謝しますが、Global.asaxから.Addを実行する方法に従っていません。
justSteve、2012年

4
ルートSubdomainRouteを呼び出して、次のように最初のルートとして追加しました。routes.Add(new SubdomainRoute());
ジェフハンド

6
このアプローチでは、可能なサブドメインのリストをハードコーディングする必要がありますか?
Maxim V. Pavlov

2
いいえ、「サブドメイン」のようなデータベースフィールドを追加できます。これは、特定のユーザーまたはその他のサブドメインに期待するサブドメインであり、サブドメインでルックアップを実行するだけです。
ライアンヘイズ

1
誰かがこれのwebformsバージョンを推奨できますか?
MatthewT

52

標準のMVC5ルーティング機能を維持しながらサブドメインキャプチャするにはSubdomainRouteから派生した次のクラスを使用しますRoute

また、SubdomainRouteサブドメインは、任意選択として指定することを可能にするクエリパラメータ、製作sub.example.com/foo/barexample.com/foo/bar?subdomain=sub等価。これにより、DNSサブドメインを構成する前にテストできます。クエリパラメータ(使用中の場合)はUrl.Action、などによって生成された新しいリンクを介して伝達されます。

クエリパラメーターは、netshで構成したり、管理者として実行したりすることなく、Visual Studio 2013でローカルデバッグも有効にします。既定では、IIS Expressは昇格されていない場合にのみローカルホストにバインドします。sub.localtest.meのような同義のホスト名にはバインドしません。

class SubdomainRoute : Route
{
    public SubdomainRoute(string url) : base(url, new MvcRouteHandler()) {}

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var routeData = base.GetRouteData(httpContext);
        if (routeData == null) return null; // Only look at the subdomain if this route matches in the first place.
        string subdomain = httpContext.Request.Params["subdomain"]; // A subdomain specified as a query parameter takes precedence over the hostname.
        if (subdomain == null) {
            string host = httpContext.Request.Headers["Host"];
            int index = host.IndexOf('.');
            if (index >= 0)
                subdomain = host.Substring(0, index);
        }
        if (subdomain != null)
            routeData.Values["subdomain"] = subdomain;
        return routeData;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        object subdomainParam = requestContext.HttpContext.Request.Params["subdomain"];
        if (subdomainParam != null)
            values["subdomain"] = subdomainParam;
        return base.GetVirtualPath(requestContext, values);
    }
}

便宜上、以下の呼び出しMapSubdomainRouteあなたの方法をRegisterRoutesちょうどあなたが、昔ながらのと同じようにする方法MapRoute

static void MapSubdomainRoute(this RouteCollection routes, string name, string url, object defaults = null, object constraints = null)
{
    routes.Add(name, new SubdomainRoute(url) {
        Defaults = new RouteValueDictionary(defaults),
        Constraints = new RouteValueDictionary(constraints),
        DataTokens = new RouteValueDictionary()
    });
}

最後に、サブドメインに(真のサブドメインまたはクエリパラメータから)簡単にアクセスするには、このSubdomainプロパティを使用してコントローラの基本クラスを作成すると便利です。

protected string Subdomain
{
    get { return (string)Request.RequestContext.RouteData.Values["subdomain"]; }
}

1
サブドメインを常にルート値として利用できるようにコードを更新しました。これにより、サブドメインへのアクセスが簡単になります。
Edward Brey 2013年

私はこれが好き。とてもシンプルで、私のプロジェクトには十分すぎるほどです。
SoonDead 2014

これは素晴らしい答えです。これがルート属性を処理する方法はありますか?「subdomain.domain.com/portal/register」のようなパスでこれを機能させようとしていますが、属性を使用するとこれが簡単になります。
perfect_element 2017年

@perfect_element-属性ルートは、従来のベースのルートのように拡張できません。このようなことを行う唯一の方法は、独自の属性ルーティングシステムを構築することです。
NightOwl888 2017年

23

これは私の仕事ではありませんが、この回答に追加する必要がありました。

この問題に対する優れた解決策を次に示します。Maartin Balliauwは、通常のルーティングと同様に使用できるDomainRouteクラスを作成するコードを記述しました。

http://blog.maartenballiauw.be/post/2009/05/20/ASPNET-MVC-Domain-Routing.aspx

サンプルの使用法は次のようになります...

routes.Add("DomainRoute", new DomainRoute( 
    "{customer}.example.com", // Domain with parameters 
    "{action}/{id}",    // URL with parameters 
    new { controller = "Home", action = "Index", id = "" }  // Parameter defaults 
))

;


5
このソリューションには問題があります。たとえば、異なるドメインとしてサブドメインを処理したいとします。routes.Add( "SD"、new DomainRoute( "user} .localhost"、 ""、new {controller = "Home"、action = "IndexForUser"、user = "u1 "})); ホームページもキャッシュします。これは、生成された正規表現が原因です。これを修正するには、DomainRoute.csでCreateRegexメソッドのコピーを作成し、CreateDomainRegexという名前を付け、この行の*を+に変更します。source = source.Replace( "}"、@ ">([a- zA-Z0-9 _] *)) "); GetRouteDataメソッドでドメインregxにこの新しいメソッドを使用します。domainRegex = CreateDomainRegex(Domain);
Gorkem Pacaci 2010年

なぜこのコードを実行できないのかわかりません... SERVER NOT FOUNDエラーが表示されるだけです...つまり、コードが機能していません...他の構成を設定していますか?
TJ博士2014


1
@IDisposable MvcApplication.DnsSuffixとは何ですか?
HaBo 2014年

基本DNSドメインをweb.configで公開するだけです。通常の値は.example.orgです
IDisposable

4

Web APIの使用時にサブドメインをキャプチャするには、アクションセレクターをオーバーライドしてsubdomainクエリパラメーターを挿入します。次に、次のようなコントローラーのアクションでサブドメインクエリパラメーターを使用します。

public string Get(string id, string subdomain)

この方法では、実際のホスト名の代わりにlocalhostを使用するときに手動でクエリパラメーターを指定できるため、デバッグが便利になります(詳細については、標準のMVC5ルーティングの回答を参照してください)。これはアクションセレクターのコードです:

class SubdomainActionSelector : IHttpActionSelector
{
    private readonly IHttpActionSelector defaultSelector;

    public SubdomainActionSelector(IHttpActionSelector defaultSelector)
    {
        this.defaultSelector = defaultSelector;
    }

    public ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor)
    {
        return defaultSelector.GetActionMapping(controllerDescriptor);
    }

    public HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
    {
        var routeValues = controllerContext.Request.GetRouteData().Values;
        if (!routeValues.ContainsKey("subdomain")) {
            string host = controllerContext.Request.Headers.Host;
            int index = host.IndexOf('.');
            if (index >= 0)
                controllerContext.Request.GetRouteData().Values.Add("subdomain", host.Substring(0, index));
        }
        return defaultSelector.SelectAction(controllerContext);
    }
}

これをWebApiConfig.Register以下に追加して、デフォルトのアクションセレクターを置き換えます:

config.Services.Replace(typeof(IHttpActionSelector), new SubdomainActionSelector(config.Services.GetActionSelector()));

ルートデータがWeb APIコントローラーに表示されない問題があり、コントローラー内のRequest.GetRouteDataを検査すると、値が表示されません。
アランマクドナルド

3

はい。ただし、独自のルートハンドラを作成する必要があります。

通常、ルートはドメインを認識しません。これは、アプリケーションを任意のドメインにデプロイでき、ルートが何らかの方法で問題にならないためです。しかし、あなたのケースでは、コントローラとアクションをドメインに基づいて設定したいので、ドメインを認識するカスタムルートを作成する必要があります。


3

このようなルートを作成できるサブドメインルーティング用のライブラリを作成しました。現在、.NET Core 1.1および.NET Framework 4.6.1で動作していますが、近い将来に更新される予定です。これはどのように機能するかです:
1)Startup.csでサブドメインルートをマップする

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    var hostnames = new[] { "localhost:54575" };

    app.UseMvc(routes =>
    {
        routes.MapSubdomainRoute(
            hostnames,
            "SubdomainRoute",
            "{username}",
            "{controller}/{action}",
            new { controller = "Home", action = "Index" });
    )};

2)Controllers / HomeController.cs

public IActionResult Index(string username)
{
    //code
}

3)そのlibでは、URLとフォームを生成することもできます。コード:

@Html.ActionLink("User home", "Index", "Home" new { username = "user1" }, null)

生成される<a href="http://user1.localhost:54575/Home/Index">User home</a> URLは、現在のホストの場所とスキーマにも依存します。 およびの
HTMLヘルパーを使用することもできます。タグヘルパー(、)と呼ばれる新機能を使用することもできます。 そのlibにはまだドキュメントがありませんが、いくつかのテストとサンプルプロジェクトがあるので、自由に探索してください。BeginFormUrlHelperFormTagHelperAnchorTagHelper


2

ではASP.NETコア、ホストは、を介して利用可能ですRequest.Host.Host。クエリパラメータによるホストのオーバーライドを許可する場合は、最初に確認してくださいRequest.Queryます。

ホストクエリパラメータを新しいルートベースのURLに伝達するには、次のコードをapp.UseMvcルート構成に追加します。

routes.Routes.Add(new HostPropagationRouter(routes.DefaultHandler));

そしてHostPropagationRouter、このように定義します:

/// <summary>
/// A router that propagates the request's "host" query parameter to the response.
/// </summary>
class HostPropagationRouter : IRouter
{
    readonly IRouter router;

    public HostPropagationRouter(IRouter router)
    {
        this.router = router;
    }

    public VirtualPathData GetVirtualPath(VirtualPathContext context)
    {
        if (context.HttpContext.Request.Query.TryGetValue("host", out var host))
            context.Values["host"] = host;
        return router.GetVirtualPath(context);
    }

    public Task RouteAsync(RouteContext context) => router.RouteAsync(context);
}

1

URL渡されたホストを調べる新しいルートハンドラーを定義した後、アクセス先のサイトを認識しているベースコントローラーのアイデアに進むことができます。次のようになります。

public abstract class SiteController : Controller {
    ISiteProvider _siteProvider;

    public SiteController() {
        _siteProvider = new SiteProvider();
    }

    public SiteController(ISiteProvider siteProvider) {
        _siteProvider = siteProvider;
    }

    protected override void Initialize(RequestContext requestContext) {
        string[] host = requestContext.HttpContext.Request.Headers["Host"].Split(':');

        _siteProvider.Initialise(host[0]);

        base.Initialize(requestContext);
    }

    protected override void OnActionExecuting(ActionExecutingContext filterContext) {
        ViewData["Site"] = Site;

        base.OnActionExecuting(filterContext);
    }

    public Site Site {
        get {
            return _siteProvider.GetCurrentSite();
        }
    }

}

ISiteProvider シンプルなインターフェースです:

public interface ISiteProvider {
    void Initialise(string host);
    Site GetCurrentSite();
}

ルーク・サンプソンのブログに行くことを紹介します


1

テナントごとに異なるドメイン/サブドメインを持つマルチテナンシー機能をプロジェクトに与えることを検討している場合は、SaasKitを確認する必要があります。

https://github.com/saaskit/saaskit

コード例はここにあります: http //benfoster.io/blog/saaskit-multi-tenancy-made-easy

ASP.NETコアを使用したいくつかの例: http //andrewlock.net/forking-the-pipeline-adding-tenant-specific-files-with-saaskit-in-asp-net-core/

編集:ASP.NETコアプロジェクトでSaasKitを使用したくない場合は、MaartenによるMVC6のドメインルーティングの実装を確認できます。 https://blog.maartenballiauw.be/post/2015/02/17/domain -routing-and-resolving-current-tenant-with-aspnet-mvc-6-aspnet-5.html

ただし、これらの要旨は維持されておらず、ASP.NETコアの最新リリースで動作するように調整する必要があります。

コードへの直接リンク:https : //gist.github.com/maartenba/77ca6f9cfef50efa96ec#file-domaintemplateroutebuilderextensions-cs


マルチテナンシーを探していませんが、ヒントをありがとう!
Dan Esparza 2016年

0

数か月前に、メソッドまたはコントローラーを特定のドメインに制限する属性を開発しました。

使い方はとても簡単です。

[IsDomain("localhost","example.com","www.example.com","*.t1.example.com")]
[HttpGet("RestrictedByHost")]
public IActionResult Test(){}

コントローラに直接適用することもできます。

public class IsDomainAttribute : Attribute, Microsoft.AspNetCore.Mvc.Filters.IAuthorizationFilter
{

    public IsDomainAttribute(params string[]  domains)
    {
        Domains = domains;
    }

    public string[] Domains { get; }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var host = context.HttpContext.Request.Host.Host;
        if (Domains.Contains(host))
            return;
        if (Domains.Any(d => d.EndsWith("*"))
                && Domains.Any(d => host.StartsWith(d.Substring(0, d.Length - 1))))
            return;
        if (Domains.Any(d => d.StartsWith("*"))
                && Domains.Any(d => host.EndsWith(d.Substring(1))))
            return;

        context.Result = new Microsoft.AspNetCore.Mvc.NotFoundResult();//.ChallengeResult
    }
}

制限:異なるフィルターを使用する異なるメソッドで2つの同じルートを使用できない場合があります。つまり、以下は重複ルートに対して例外をスローする可能性があります。

[IsDomain("test1.example.com")]
[HttpGet("/Test")]
public IActionResult Test1(){}

[IsDomain("test2.example.com")]
[HttpGet("/Test")]
public IActionResult Test2(){}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.