เป็นไปได้หรือไม่ที่จะสร้างเส้นทาง ASP.NET MVC ตามโดเมนย่อย?


235

เป็นไปได้หรือไม่ที่มีเส้นทาง ASP.NET MVC ที่ใช้ข้อมูลโดเมนย่อยเพื่อกำหนดเส้นทาง ตัวอย่างเช่น:

  • user1 .domain.comไปที่เดียว
  • user2 .domain.comไปที่อื่นหรือไม่

หรือฉันสามารถทำให้มันทั้งสองอย่างนี้ไปที่ตัวควบคุม / การกระทำเดียวกันกับusernameพารามิเตอร์ได้หรือไม่?


ฉันใช้สิ่งที่คล้ายกันสำหรับแอพพลิเคชั่นที่มีผู้เช่าหลายคน แต่ใช้คอนโทรลเลอร์พื้นฐานที่เป็นนามธรรมมากกว่าคลาสรูทแบบกำหนดเอง โพสต์บล็อกของฉันที่มันเป็นที่นี่
ลุคจอห์น

6
โปรดพิจารณาวิธีนี้: http://blog.tonywilliams.me.uk/asp-net-mvc-2-routing-subdomains-to-areasฉันพบว่ามันจะดีกว่าสำหรับการแนะนำ multitenancy แอพของฉันมากกว่าคำตอบอื่น ๆ เนื่องจากพื้นที่ MVC เป็นวิธีที่ดีในการแนะนำตัวควบคุมและมุมมองเฉพาะของผู้เช่าในวิธีการจัดระเบียบ
trebormf

2
@trebormf - ฉันคิดว่าคุณควรจะเพิ่มมันเป็นคำตอบนี่คือสิ่งที่ฉันลงเอยด้วยการใช้เป็นพื้นฐานสำหรับการแก้ปัญหาของฉัน
Shagglez

@Shagglez - ขอบคุณ มันเป็นคำตอบ แต่ผู้ดำเนินรายการเปลี่ยนเป็นความคิดเห็นด้วยเหตุผลที่ฉันไม่เข้าใจ
trebormf

5
โทนี่ถูกทำลาย นี่คือหนึ่งที่ทำงานสำหรับฉัน: blog.tonywilliams.me.uk/…
Ronnie Overby

คำตอบ:


168

คุณสามารถทำได้โดยการสร้างเส้นทางใหม่และเพิ่มลงในคอลเลกชันเส้นทางใน RegisterRoutes ใน global.asax ของคุณ ด้านล่างเป็นตัวอย่างง่ายๆของเส้นทางที่กำหนดเอง:

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
justSteve

4
ฉันเรียกเส้นทาง SubdomainRoute และเพิ่มเป็นเส้นทางแรกดังนี้: route.Add (new SubdomainRoute ());
Jeff Handley

6
วิธีนี้ต้องใช้การเข้ารหัสรายการโดเมนย่อยที่เป็นไปได้หรือไม่
Maxim V. Pavlov

2
ไม่คุณสามารถเพิ่มเขตข้อมูลฐานข้อมูลที่เรียกว่า "โดเมนย่อย" ที่คุณจะได้รับในสิ่งที่คุณคาดหวังว่าโดเมนย่อยจะเป็นของผู้ใช้รายใดรายหนึ่งหรืออย่างอื่นจากนั้นเพียงค้นหาโดเมนย่อย
Ryan Hayes

1
ใครช่วยแนะนำเว็บฟอร์มเวอร์ชันนี้ได้บ้าง
MatthewT

52

การจับโดเมนย่อยขณะที่การรักษามาตรฐานการกำหนดเส้นทาง MVC5 คุณสมบัติใช้ต่อไปเรียนมาจากSubdomainRouteRoute

นอกจากนี้ยังSubdomainRouteอนุญาตให้ระบุโดเมนย่อยแบบเป็นทางเลือกเป็นพารามิเตอร์ข้อความค้นหาสร้างsub.example.com/foo/barและexample.com/foo/bar?subdomain=subเทียบเท่า สิ่งนี้ช่วยให้คุณทดสอบก่อนที่จะกำหนดค่าโดเมนย่อย DNS พารามิเตอร์การสืบค้น (เมื่อใช้งาน) ถูกเผยแพร่ผ่านลิงค์ใหม่ที่สร้างโดยUrl.Actionฯลฯ

พารามิเตอร์แบบสอบถามยังช่วยให้การแก้จุดบกพร่องในท้องถิ่นกับ Visual Studio 2013 โดยไม่ต้องกำหนดค่ากับ netsh หรือเรียกใช้ในฐานะผู้ดูแล โดยค่าเริ่มต้น 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คุณสมบัตินี้มีประโยชน์:

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

1
ฉันอัปเดตรหัสเพื่อให้โดเมนย่อยพร้อมใช้งานเสมอเป็นค่าเส้นทาง ทำให้การเข้าถึงโดเมนย่อยง่ายขึ้น
Edward Brey

ฉันชอบสิ่งนี้. ง่ายมากและมากเกินพอสำหรับโครงการของฉัน
SoonDead

นี่คือคำตอบที่ดี มีวิธีการนี้ในการทำงานกับคุณลักษณะเส้นทางหรือไม่? ฉันกำลังพยายามทำให้เส้นทางนี้เป็น "subdomain.domain.com/portal/register" และการใช้แอตทริบิวต์จะทำให้การทำงานง่ายขึ้น
perfect_element

@perfect_element - เส้นทางแอตทริบิวต์ไม่สามารถขยายได้เช่นเส้นทางตามการประชุมคือ วิธีเดียวที่จะทำอะไรเช่นนั้นคือการสร้างระบบการกำหนดเส้นทางของคุณเอง
NightOwl888

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
มีปัญหากับวิธีนี้ กล่าวว่าคุณต้องการจัดการโดเมนย่อยในฐานะผู้ใช้ที่แตกต่างกัน: route.Add ("SD", DomainRoute ใหม่ ("user} .localhost", "", ใหม่ {controller = "Home", action = "IndexForUser", user = "u1 "})); มันแคชโฮมเพจเช่นกัน นี่เป็นเพราะ regex ที่สร้างขึ้น ในการแก้ไขปัญหานี้คุณสามารถสร้างสำเนาของเมธอด CreateRegex ใน DomainRoute.cs ตั้งชื่อ CreateDomainRegex เปลี่ยน * ในบรรทัดนี้เป็น +: source = source.Replace ("}", @ "> ([a- zA-Z0-9 _] *)) "); และใช้วิธีการใหม่นี้สำหรับ domain regx ในวิธี GetRouteData: domainRegex = CreateDomainRegex (Domain);
Gorkem Pacaci

ฉันไม่รู้ว่าทำไมฉันไม่สามารถเรียกใช้รหัสนี้ ... ฉันเพิ่งได้รับSERVER NOT FOUNDข้อผิดพลาด ... หมายความว่ารหัสไม่ทำงานสำหรับฉัน ... คุณกำลังตั้งค่าการกำหนดค่าอื่นหรืออะไร?!
ดร. TJ

ฉันได้สร้างส่วนสำคัญในเวอร์ชันของฉันของgist.github.com/IDisposable/77f11c6f7693f9d181bb
IDisposable

1
@IDisposable MvcApplication.DnsSuffix คืออะไร
HaBo

เราเพิ่งเปิดเผยโดเมน DNS พื้นฐานใน web.config ... ค่าปกติน่าจะเป็น. example.org
IDisposable

4

หากต้องการจับโดเมนย่อยเมื่อใช้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:

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

ทุกคนมีปัญหาที่ข้อมูลเส้นทางไม่ปรากฏบนตัวควบคุมเว็บ API และตรวจสอบ Request.GetRouteData ภายในตัวควบคุมไม่แสดงค่าใด ๆ
Alan Macdonald

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 สำหรับและBeginForm UrlHelperหากคุณชอบคุณยังสามารถใช้คุณสมบัติใหม่ที่เรียกว่า tag helpers ( FormTagHelper, AnchorTagHelper)
lib นั้นยังไม่มีเอกสารใด ๆ แต่มีโครงการทดสอบและตัวอย่างบางส่วนดังนั้นอย่าลังเลที่จะสำรวจ


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

หากคุณกำลังมองหาการให้ความสามารถ MultiTenancy ให้กับโครงการของคุณด้วยโดเมน / โดเมนย่อยที่แตกต่างกันสำหรับผู้เช่าแต่ละรายคุณควรดูที่ SaasKit:

https://github.com/saaskit/saaskit

ตัวอย่างรหัสสามารถดูได้ที่นี่: http://benfoster.io/blog/saaskit-multi-tenancy-made-easy

ตัวอย่างการใช้ ASP.NET core: http://andrewlock.net/forking-the-pipeline-adding-tenant-specific-files-with-saaskit-in-asp-net-core/

แก้ไข: หากคุณไม่ต้องการใช้ SaasKit ในโครงการหลัก ASP.NET ของคุณคุณสามารถดูการใช้งานการกำหนดเส้นทางโดเมนของ Maarten สำหรับ MVC6: https://blog.maartenballiauw.be/post/2015/02/17/domain -routing และการแก้ปัญหาในปัจจุบันผู้เช่าที่มี aspnet-MVC-6-aspnet-5.html

อย่างไรก็ตาม Gists เหล่านั้นไม่ได้รับการบำรุงรักษาและจำเป็นต้องมีการปรับแต่งเพื่อให้ทำงานกับ ASP.NET core รุ่นล่าสุดได้

ลิงค์โดยตรงไปยังรหัส: https://gist.github.com/maartenba/77ca6f9cfef50efa96ec#file-domaintemplateroutebuilderextensions-cs


ไม่ได้มองหา multitenancy - แต่ขอบคุณสำหรับเคล็ดลับ!
Dan Esparza

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
    }
}

ข้อ จำกัด : คุณอาจไม่สามารถมีเส้นทางเดียวกันสองเส้นทางในวิธีการต่าง ๆ พร้อมตัวกรองที่แตกต่างกันฉันหมายถึงสิ่งต่อไปนี้อาจทำให้เกิดข้อยกเว้นสำหรับเส้นทางที่ซ้ำกัน:

[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.