Существует несколько подходов в зависимости от ваших потребностей.
Как это сделать - user1.domain.com
переходит к user1 / index (не внутри области) - user2.domain.com
переходит к user2 / index ( не внутри области)
Переписать / Перенаправить
Один из подходов - переписать / перенаправить URL-адрес. Если вам не нравится делать это с помощью nginx / iis, вы можете создать правило перезаписи уровня приложения. Например, я создаю пример правила маршрута для вашей справки:
internal enum RouteSubDomainBehavior{ Redirect, Rewrite, }
internal class RouteSubDomainRule : IRule
{
private readonly string _domainWithPort;
private readonly RouteSubDomainBehavior _behavior;
public RouteSubDomainRule(string domain, RouteSubDomainBehavior behavior)
{
this._domainWithPort = domain;
this._behavior = behavior;
}
// custom this method according to your needs
protected bool ShouldRewrite(RewriteContext context)
{
var req = context.HttpContext.Request;
// only rewrite the url when it ends with target doamin
if (!req.Host.Value.EndsWith(this._domainWithPort, StringComparison.OrdinalIgnoreCase)) { return false; }
// if already rewrite, skip
if(req.Host.Value.Length == this._domainWithPort.Length) { return false; }
// ... add other condition to make sure only rewrite for the routes you wish, for example, skip the Hub
return true;
}
public void ApplyRule(RewriteContext context)
{
if(!this.ShouldRewrite(context)) {
context.Result = RuleResult.ContinueRules;
return;
}
var req = context.HttpContext.Request;
if(this._behavior == RouteSubDomainBehavior.Redirect){
var newUrl = UriHelper.BuildAbsolute( req.Scheme, new HostString(this._domainWithPort), req.PathBase, req.Path, req.QueryString);
var resp = context.HttpContext.Response;
context.Logger.LogInformation($"redirect {req.Scheme}://{req.Host}{req.Path}?{req.QueryString} to {newUrl}");
resp.StatusCode = 301;
resp.Headers[HeaderNames.Location] = newUrl;
context.Result = RuleResult.EndResponse;
}
else if (this._behavior == RouteSubDomainBehavior.Rewrite)
{
var host = req.Host.Value;
var userStr = req.Host.Value.Substring(0, host.Length - this._domainWithPort.Length - 1);
req.Host= new HostString(this._domainWithPort);
var oldPath = req.Path;
req.Path = $"/{userStr}{oldPath}";
context.Logger.LogInformation($"rewrite {oldPath} as {req.Path}");
context.Result = RuleResult.SkipRemainingRules;
}
else{
throw new Exception($"unknow SubDomainBehavoir={this._behavior}");
}
}
}
(обратите внимание, что здесь я использую Rewrite
. Если хотите, можете сменить его на RouteSubDomainBehavior.Redirect
.)
А затем вызвать промежуточное программное обеспечение перезаписывающего устройства сразу после app.UseStaticFiles()
:
app.UseStaticFiles();
// note : the invocation order matters!
app.UseRewriter(new RewriteOptions().Add(new RouteSubDomainRule("domain.com:5001",RouteSubDomainBehavior.Rewrite)));
app.UseMvc(...)
Таким образом,
user1.domain.com:5001/
будет перезаписано как (или перенаправлено на) domain.com:5001/user1
user1.domain.com:5001/Index
будет перезаписано как (или перенаправлено на) domain.com:5001/user1/Index
user1.domain.com:5001/Home/Index
будет переписано как (или перенаправлено на) domain.com:5001/user1//HomeIndex
- stati c файлы типа
user1.domain.com:5001/lib/jquery/dist/jquery.min.js
не будут переписываться / перенаправляться, потому что они обслуживаются UseStaticFiles
.
Другой подход с использованием IModelBinder
Хотя вы можете направить его переписывая / перенаправляя, как указано выше, я подозреваю, что ваши реальные потребности - это параметры привязки из Request.Host
. Если это так, я бы посоветовал вам использовать IModelBinder
. Например, создайте новый [FromHost]
BindingSource:
internal class FromHostAttribute : Attribute, IBindingSourceMetadata
{
public static readonly BindingSource Instance = new BindingSource( "FromHostBindingSource", "From Host Binding Source", true, true);
public BindingSource BindingSource {get{ return FromHostAttribute.Instance; }}
}
public class MyFromHostModelBinder : IModelBinder
{
private readonly string _domainWithPort;
public MyFromHostModelBinder()
{
this._domainWithPort = "domain.com:5001"; // in real project, use by Configuration/Options
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var req = bindingContext.HttpContext.Request;
var host = req.Host.Value;
var name = bindingContext.FieldName;
var userStr = req.Host.Value.Substring(0, host.Length - this._domainWithPort.Length - 1);
if (userStr == null) {
bindingContext.ModelState.AddModelError(name, $"cannot get {name} from Host Domain");
} else {
var result = Convert.ChangeType(userStr, bindingContext.ModelType);
bindingContext.Result = ModelBindingResult.Success(result);
}
return Task.CompletedTask;
}
}
public class FromHostBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
var has = context.BindingInfo?.BindingSource == FromHostAttribute.Instance;
if(has){
return new BinderTypeModelBinder(typeof(MyFromHostModelBinder));
}
return null;
}
}
Наконец, вставьте этот FromHostBinderProvider
в своих MVC провайдеров переплетов.
services.AddMvc(otps =>{
otps.ModelBinderProviders.Insert(0, new FromHostBinderProvider());
});
Теперь вы можете получить user1.domain.com
автоматически:
public IActionResult Index([FromHost] string username)
{
...
return View(view_model_by_username);
}
public IActionResult Edit([FromHost] string username, string id)
{
...
return View(view_model_by_username);
}