Привет, ребята. Я пытаюсь создать на моем веб-сайте поддомен c, подобный этому
- user1.domain.com ---> данные для user1
- user2 .domain.com ---> данные для user2
то же представление, но зависят от пользователя {0}
, оно отлично работает на локальном, но когда я пытаюсь загрузить проект на хост всегда 404 не найден.
Setup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public static Microsoft.AspNetCore.DataProtection.IDataProtectionBuilder dataProtectionBuilder;
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = ".AspNetCore.Identity.Application";
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("MyConnection")));
services.AddDefaultIdentity<ExtendIdentityUser>(options =>
{
options.Password.RequiredLength = 8;
options.Password.RequireUppercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequiredUniqueChars = 0;
options.Password.RequireLowercase = false;
})
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddScoped<ILoggingExceptionRepository, LoggingExceptionRepository>();
services.AddScoped<IUserClaimsPrincipalFactory<ExtendIdentityUser>, CustomClaimsPrincipalFactory>();
services.AddScoped<MyCustomRouter>();
services.AddSubdomains();
services.AddMvc(opts =>
{
opts.ModelBinderProviders.Insert(0, new FromHostBinderProvider());
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseStatusCodePagesWithRedirects("/Error/{0}");
}
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
FromHostBinderProvider.cs этот класс, чтобы получить поддомен с URL
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"; // 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);
if (string.IsNullOrEmpty(userStr))
{
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;
}
}
HomeController.cs
public IActionResult UserPage([FromHost] string pageId, int? currentpage)
{
//[FromHost] this custom attribute to get sub-domain name from url
pageId = pageId.Split('.')[0];
//view data or any business code on depend on sub-domain
return View(model);
}
После долгих поисков я нашел эти идеи, но не знаю, использую их в своем коде ...
Первая идея:
этот метод RouteAsyn c никогда не выполняется со мной
public class MyCustomRouter : MvcRouteHandler, IRouter
{
private IActionContextAccessor _actionContextAccessor;
private IActionInvokerFactory _actionInvokerFactory;
private IActionSelector _actionSelector;
private ILogger _logger;
private DiagnosticSource _diagnosticSource;
public MyCustomRouter(
IActionInvokerFactory actionInvokerFactory,
IActionSelector actionSelector,
DiagnosticSource diagnosticSource,
ILoggerFactory loggerFactory)
: this(actionInvokerFactory, actionSelector, diagnosticSource, loggerFactory, actionContextAccessor: null)
{
}
public MyCustomRouter(IActionInvokerFactory actionInvokerFactory, IActionSelector actionSelector, DiagnosticSource diagnosticSource,
ILoggerFactory loggerFactory, IActionContextAccessor actionContextAccessor)
: base(actionInvokerFactory, actionSelector, diagnosticSource,
loggerFactory, actionContextAccessor)
{
_actionContextAccessor = actionContextAccessor;
_actionInvokerFactory = actionInvokerFactory;
_actionSelector = actionSelector;
_diagnosticSource = diagnosticSource;
_logger = loggerFactory.CreateLogger<MvcRouteHandler>();
}
public new Task RouteAsync(RouteContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
// *****
// ⚠️ This is the important part! ⚠️
// *****
string Host = context.HttpContext.Request.Host.Host;
if (Host == "localhost") // Change this the to your usual host
{
// Do nothing, normal routing
}
else
{
// You can do pretty much anything here, but I chose to switch
// to a different controller. ✅
context.RouteData.Values["controller"] = "Home";
context.RouteData.Values.Add("Host", Host); // Add a variable for fun
}
// All the next code is copied from base class
var candidates = _actionSelector.SelectCandidates(context);
if (candidates == null || candidates.Count == 0)
{
return Task.CompletedTask;
}
var actionDescriptor = _actionSelector.SelectBestCandidate(context, candidates);
if (actionDescriptor == null)
{
return Task.CompletedTask;
}
context.Handler = (c) =>
{
var routeData = c.GetRouteData();
var actionContext = new ActionContext(context.HttpContext, routeData, actionDescriptor);
if (_actionContextAccessor != null)
{
_actionContextAccessor.ActionContext = actionContext;
}
var invoker = _actionInvokerFactory.CreateInvoker(actionContext);
if (invoker == null)
{
throw new InvalidOperationException();
}
return invoker.InvokeAsync();
};
return Task.CompletedTask;
}
}
Вторая идея:
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}/{req.Host}{oldPath}";
context.Logger.LogInformation($"rewrite {oldPath} as {req.Path}");
context.Result = RuleResult.SkipRemainingRules;
}
else
{
throw new Exception($"unknow SubDomainBehavoir={this._behavior}");
}
}
}
Пожалуйста, мне нужна помощь решить эту проблему, используя первую идею или вторую идею или что-то еще. запомнить эту проблему только на хосте. на localhost работает нормально.