asp.net-mvc 是否可以基于子域创建 ASP.NET MVC 路由?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/278668/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it possible to make an ASP.NET MVC route based on a subdomain?
提问by Dan Esparza
Is it possible to have an ASP.NET MVC route that uses subdomain information to determine its route? For example:
是否可以使用子域信息来确定其路由的 ASP.NET MVC 路由?例如:
- user1.domain.comgoes to one place
- user2.domain.comgoes to another?
- user1.domain.com到一个地方
- user2.domain.com转到另一个?
Or, can I make it so both of these go to the same controller/action with a usernameparameter?
或者,我可以让它们都使用username参数转到同一个控制器/动作吗?
采纳答案by Jon Cahill
You can do it by creating a new route and adding it to the routes collection in RegisterRoutes in your global.asax. Below is a very simple example of a custom Route:
您可以通过创建一个新路由并将其添加到 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;
}
}
回答by Edward Brey
To capture the subdomain while retaining the standard MVC5 routing features, use the following SubdomainRouteclass derived from Route.
要在保留标准 MVC5 路由功能的同时捕获子域,请使用以下SubdomainRoute派生自Route.
Additionally, SubdomainRouteallows the subdomain optionally to be specified as a query parameter, making sub.example.com/foo/barand example.com/foo/bar?subdomain=subequivalent. This allows you to test before the DNS subdomains are configured. The query parameter (when in use) is propagated through new links generated by Url.Action, etc.
此外,SubdomainRoute允许将子域选择性地指定为查询参数,制作sub.example.com/foo/bar和example.com/foo/bar?subdomain=sub等效。这允许您在配置 DNS 子域之前进行测试。查询参数(使用时)通过由Url.Action等生成的新链接传播。
The query parameter also enables local debugging with Visual Studio 2013 without having to configure with netsh or run as Administrator. By default, IIS Express only binds to localhostwhen non-elevated; it won't bind to synonymous hostnames like sub.localtest.me.
查询参数还可以使用 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);
}
}
For convenience, call the following MapSubdomainRoutemethod from your RegisterRoutesmethod just as you would plain old MapRoute:
为方便起见,MapSubdomainRoute从您的RegisterRoutes方法中调用以下方法,就像您使用普通 old 一样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()
});
}
Finally, to conveniently access the subdomain (either from a true subdomain or a query parameter), it is helpful to create a Controller base class with this Subdomainproperty:
最后,为了方便地访问子域(从真正的子域或查询参数),创建具有此Subdomain属性的 Controller 基类很有帮助:
protected string Subdomain
{
get { return (string)Request.RequestContext.RouteData.Values["subdomain"]; }
}
回答by Jim Blake
This is not my work, but I had to add it on this answer.
这不是我的工作,但我必须将其添加到此答案中。
Here is a great solution to this problem. Maartin Balliauw wrote code that creates a DomainRoute class that can be used very similarly to the normal routing.
这是这个问题的一个很好的解决方案。Maartin Balliauw 编写的代码创建了一个 DomainRoute 类,该类的使用与普通路由非常相似。
http://blog.maartenballiauw.be/post/2009/05/20/ASPNET-MVC-Domain-Routing.aspx
http://blog.maartenballiauw.be/post/2009/05/20/ASPNET-MVC-Domain-Routing.aspx
Sample use would be like this...
示例使用将是这样的......
routes.Add("DomainRoute", new DomainRoute(
"{customer}.example.com", // Domain with parameters
"{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
))
;
;
回答by Edward Brey
To capture the subdomain when using Web API, override the Action Selector to inject a subdomainquery parameter. Then use the subdomain query parameter in your controllers' actions like this:
要在使用Web API时捕获子域,请覆盖操作选择器以注入subdomain查询参数。然后在控制器的操作中使用子域查询参数,如下所示:
public string Get(string id, string subdomain)
This approach makes debugging convenient since you can specify the query parameter by hand when using localhostinstead of the actual host name (see the standard MVC5 routing answerfor details). This is the code for Action Selector:
这种方法使调试更方便,因为您可以在使用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);
}
}
Replace the default Action Selector by adding this to WebApiConfig.Register:
通过将其添加到以下来替换默认的操作选择器WebApiConfig.Register:
config.Services.Replace(typeof(IHttpActionSelector), new SubdomainActionSelector(config.Services.GetActionSelector()));
回答by Nick Berardi
Yes but you have to create your own route handler.
是的,但您必须创建自己的路由处理程序。
Typically the route is not aware of the domain because the application could be deployed to any domain and the route would not care one way or another. But in your case you want to base the controller and action off the domain, so you will have to create a custom route that is aware of the domain.
通常路由不知道域,因为应用程序可以部署到任何域并且路由不会关心任何一种方式。但是在您的情况下,您希望将控制器和操作置于域之外,因此您必须创建一个了解域的自定义路由。
回答by Mariusz
I created library for subdomain routingwhich you can create such a route. It is working currently for a .NET Core 1.1 and .NET Framework 4.6.1 but will be updated in near future. This is how is it working:
1) Map subdomain route in Startup.cs
我为子域路由创建了库,您可以创建这样的路由。它目前适用于 .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
2) 控制器/HomeController.cs
public IActionResult Index(string username)
{
//code
}
3) That lib will also allow you to generate URLs and forms. Code:
3) 该库还允许您生成 URL 和表单。代码:
@Html.ActionLink("User home", "Index", "Home" new { username = "user1" }, null)
Will generate <a href="http://user1.localhost:54575/Home/Index">User home</a>Generated URL will also depend on current host location and schema.
You can also use html helpers for BeginFormand UrlHelper. If you like you can also use new feature called tag helpers (FormTagHelper, AnchorTagHelper)
That lib does not have any documentation yet, but there are some tests and samples project so feel free to explore it.
生成<a href="http://user1.localhost:54575/Home/Index">User home</a>生成的 URL 也将取决于当前主机位置和架构。
您还可以将 html 帮助程序用于BeginForm和UrlHelper。如果您愿意,还可以使用称为标签助手 ( FormTagHelper, AnchorTagHelper) 的新功能,
该库还没有任何文档,但有一些测试和示例项目,因此请随意探索。
回答by Edward Brey
In ASP.NET Core, the host is available via Request.Host.Host. If you want to allow overriding the host via a query parameter, first check Request.Query.
在ASP.NET Core 中,主机可通过Request.Host.Host. 如果要允许通过查询参数覆盖主机,请首先检查Request.Query.
To cause a host query parameter to propagate into to new route-based URLs, add this code to the app.UseMvcroute configuration:
要使主机查询参数传播到新的基于路由的 URL,请将此代码添加到app.UseMvc路由配置中:
routes.Routes.Add(new HostPropagationRouter(routes.DefaultHandler));
And define HostPropagationRouterlike this:
并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);
}
回答by Amirhossein Mehrvarzi
After defining a new Route handler that would look at the host passed in the URL, you can go with the idea of a base Controller that is aware of the Site it's being accessed for. It looks like this:
在定义了一个新的 Route 处理程序来查看在 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();
}
}
}
ISiteProvideris a simple interface:
ISiteProvider是一个简单的界面:
public interface ISiteProvider {
void Initialise(string host);
Site GetCurrentSite();
}
I refer you go to Luke Sampson Blog
我推荐你去 卢克桑普森博客
回答by Darxtar
If you are looking at giving MultiTenancy capabilities to your project with different domains/subdomains for each tenant, you should have a look at SaasKit:
如果您正在考虑为您的项目提供 MultiTenancy 功能,为每个租户提供不同的域/子域,您应该看看 SaasKit:
https://github.com/saaskit/saaskit
https://github.com/saaskit/saaskit
Code examples can be seen here: http://benfoster.io/blog/saaskit-multi-tenancy-made-easy
代码示例可以在这里看到:http: //benfoster.io/blog/saaskit-multi-tenancy-made-easy
Some examples using ASP.NET core: http://andrewlock.net/forking-the-pipeline-adding-tenant-specific-files-with-saaskit-in-asp-net-core/
使用 ASP.NET 核心的一些示例:http: //andrewlock.net/forking-the-pipeline-adding-tenant-specific-files-with-saaskit-in-asp-net-core/
EDIT: If you do no want to use SaasKit in your ASP.NET core project you can have a look at Maarten's implementation of domain routing for MVC6: https://blog.maartenballiauw.be/post/2015/02/17/domain-routing-and-resolving-current-tenant-with-aspnet-mvc-6-aspnet-5.html
编辑:如果您不想在 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
However those Gists are not maintained and need to be tweaked to work with the latest release of ASP.NET core.
然而,这些 Gist 并未得到维护,需要进行调整才能与最新版本的 ASP.NET 核心一起使用。
Direct link to the code: https://gist.github.com/maartenba/77ca6f9cfef50efa96ec#file-domaintemplateroutebuilderextensions-cs
代码直接链接:https: //gist.github.com/maartenba/77ca6f9cfef50efa96ec#file-domaintemplateroutebuilderextensions-cs
回答by Jean
Few month ago I have developed an attribute that restricts methods or controllers to specific domains.
几个月前,我开发了一个将方法或控制器限制在特定域的属性。
It is quite easy to use:
它很容易使用:
[IsDomain("localhost","example.com","www.example.com","*.t1.example.com")]
[HttpGet("RestrictedByHost")]
public IActionResult Test(){}
You can also apply it directly on a controller.
您也可以直接在控制器上应用它。
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
}
}
Restriction: you may not be able to have two same routes on different methods with different filters I mean the following may throw an exception for duplicate route:
限制:您可能无法在具有不同过滤器的不同方法上拥有两条相同的路由我的意思是以下内容可能会引发重复路由的异常:
[IsDomain("test1.example.com")]
[HttpGet("/Test")]
public IActionResult Test1(){}
[IsDomain("test2.example.com")]
[HttpGet("/Test")]
public IActionResult Test2(){}

