web-dev-qa-db-ja.com

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

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

  • user1。domain.com一箇所
  • user2。domain.comに行く別の?

または、これらの両方がusernameパラメーターを使用して同じコントローラー/アクションに送られるようにすることはできますか?

232
Dan Esparza

それを行うには、新しいルートを作成し、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;
    }
}
167
Jon Cahill

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

また、SubdomainRouteを使用すると、オプションでクエリパラメーターとしてサブドメインを指定できるため、sub.example.com/foo/barexample.com/foo/bar?subdomain=subが同等になります。これにより、DNSサブドメインを構成する前にテストできます。クエリパラメータ(使用中)は、Url.Actionなどによって生成された新しいリンクを介して伝播されます。

クエリパラメーターは、 netshで構成するか、管理者として実行する を必要とせずに、Visual Studio 2013でのローカルデバッグも有効にします。デフォルトでは、IIS Expressは、昇格されていない場合にのみlocalhostにバインドします。 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プロパティを使用してController基本クラスを作成すると役立ちます。

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

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

この問題の優れた解決策を次に示します。 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 
))

;

23
Jim Blake

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

public string Get(string id, string subdomain)

このアプローチにより、実際のホスト名の代わりにlocalhostを使用するときにクエリパラメーターを手動で指定できるため、デバッグが便利になります( 標準のMVC5ルーティング応答 詳細)。これは、Action Selectorのコードです。

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に追加して、デフォルトのAction Selectorを置き換えます。

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

サブドメインルーティング用のライブラリ を作成しました。このようなルートを作成できます。現在、.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)そのライブラリでは、URLとフォームを生成することもできます。コード:

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

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

3
Mariusz

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

通常、ルートはドメインを認識しません。これは、アプリケーションを任意のドメインにデプロイでき、ルートが何らかの形で気にしないためです。しかし、あなたの場合、コントローラーとアクションのベースをドメインから外したいので、ドメインを認識するカスタムルートを作成する必要があります。

3
Nick Berardi

ASP.NET Coreでは、ホストは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);
}
2
Edward Brey

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();
}

Luke Sampson Blog にアクセスすることをお勧めします

テナントごとに異なるドメイン/サブドメインを使用してプロジェクトにMultiTenancy機能を提供する場合は、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を使用したくない場合は、MVC6のドメインルーティングのMaartenの実装を確認できます。 https://blog.maartenballiauw.be/post/2015/02 /17/domain-routing-and-resolving-current-tenant-with-aspnet-mvc-6-aspnet-5.html

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

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

1
Darxtar

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

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

[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(){}
0
Jean