Как сделать маршрут на основе субдомена MVC Core - PullRequest
0 голосов
/ 19 февраля 2020

Как это сделать - user1.domain.com переходит к user1 / index (не внутри области) - user2.domain.com переходит к user2 / index (не внутри области)

Я имею в виду

user1.domain.com / index

user2.domain.com / index

То же представление, но разные данные в зависимости от user {0}

с использованием MVC Core 2.2

Ответы [ 2 ]

3 голосов
/ 20 февраля 2020

Существует несколько подходов в зависимости от ваших потребностей.

Как это сделать - 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);
}
0 голосов
/ 25 февраля 2020

Проблема после входа в систему Identity Cook ie не распространяется на поддомен

enter image description here

Здесь мой код, где не так !!!

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 =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });

        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("ConnectionDb")));

        services.AddIdentity<ExtendIdentityUser, IdentityRole>(options =>
        {
            options.Password.RequiredLength = 8;
            options.Password.RequireUppercase = false;
            options.Password.RequireNonAlphanumeric = false;
            options.Password.RequiredUniqueChars = 0;
            options.Password.RequireLowercase = false;

        }).AddEntityFrameworkStores<ApplicationDbContext>(); // .AddDefaultTokenProviders();

        services.ConfigureApplicationCookie(options => options.CookieManager = new CookieManager());

        services.AddHttpContextAccessor();

        services.AddScoped<IUnitOfWork, UnitOfWork>();
        services.AddScoped<IExtendIdentityUser, ExtendIdentityUserRepository>();
        services.AddScoped<IItems, ItemsRepository>();

        services.AddMvc(otps =>
        {
            otps.ModelBinderProviders.Insert(0, new FromHostBinderProvider());
        });
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseDatabaseErrorPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }
        app.UseStaticFiles();

        app.UseAuthentication();
        //app.UseHttpsRedirection();
        app.UseCookiePolicy();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

И этот класс для подобласти подобен этому https://user1.localhost: 44390 / Home / Index

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 = "localhost:44390";  // 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;
    }
}
Using ICookieManager 
public class CookieManager : ICookieManager
{
    #region Private Members

    private readonly ICookieManager ConcreteManager;

    #endregion

    #region Prvate Methods

    private string RemoveSubdomain(string host)
    {
        var splitHostname = host.Split('.');
        //if not localhost
        if (splitHostname.Length > 1)
        {
            return string.Join(".", splitHostname.Skip(1));
        }
        else
        {
            return host;
        }
    }

    #endregion

    #region Public Methods

    public CookieManager()
    {
        ConcreteManager = new ChunkingCookieManager();
    }

    public void AppendResponseCookie(HttpContext context, string key, string value, CookieOptions options)
    {

        options.Domain = RemoveSubdomain(context.Request.Host.Host);  //Set the Cookie Domain using the request from host
        ConcreteManager.AppendResponseCookie(context, key, value, options);
    }

    public void DeleteCookie(HttpContext context, string key, CookieOptions options)
    {
        ConcreteManager.DeleteCookie(context, key, options);
    }

    public string GetRequestCookie(HttpContext context, string key)
    {
        return ConcreteManager.GetRequestCookie(context, key);
    }

    #endregion
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...