【问题标题】:Using the subdomain as a parameter使用子域作为参数
【发布时间】:2014-11-17 12:39:57
【问题描述】:

我有一个使用多个子域运行的 ASP.net MVC (5.2) 站点,其中子域的名称是我数据库中客户端的名称。基本上我想要做的是在我的操作方法中使用子域作为变量,以允许我从我的数据库中获取正确的数据。

几年前我做过类似的事情,但它很混乱而且不直观,所以想知道是否有比我以前使用的更好的方法来做到这一点。这是我之前做的:

protected override void OnActionExecuting(ActionExecutingContext filterContext) {
    Session["subdomain"] = GetSubDomain(Request.Url);
}

private static string GetSubDomain(Uri url) {
    string host = url.Host;
    if (host.Split('.').Length > 1) {
        int index = host.IndexOf(".");
        string subdomain = host.Substring(0, index);
        if (subdomain != "www") {
            return subdomain;
        }
    }
    return null;
}

如果子域不是“www”,这基本上为会话变量分配了一个键,但我真的不喜欢这种方式,因为它依赖于我知道会话可能包含这个神奇的值!

理想情况下,我希望能够创建一个属性,我可以使用该属性来装饰我的类/方法,该属性将提取子域,然后允许我在我的操作方法中包含一个“子域”参数,该参数将包含提取的值由属性。这可能吗?

如果做不到,有没有更好的方法来做我现在正在做的事情,而不必依赖会话?

谢谢,

迪伦

【问题讨论】:

  • 客户端肯定需要登录?那时加载名称不是更容易吗?
  • 我在发布后立即意识到我可以在控制器中创建一个私有变量,并让 OnACtionExecuting 方法写入该变量而不是会话,但这仍然意味着必须在每个页面中包含逻辑,所以我仍然在追求一种不会让我重复代码的更干净的方法。
  • 我认为 Session 是存储值的正确位置,但是,我认为您从 per request 的 URL 中提取它的想法是错误的。就像我说的,客户端必须在某个时间点登录,因此必须与客户端和名称相关联 - 成功登录后,我只需在 Session 中设置名称。
  • 不,这是针对基于客户数据的面向公众的网站,因此无需登录。基本上每个客户在数据库中都会有不同的数据,并且在面向公众的网站上显示的页面将需要根据正在使用的子域使用不同的数据。我基本上创建了一个站点,该站点为来自同一代码库的多个不同站点提供服务,而子域是唯一用于区分它们的东西。
  • 好吧(类似于 stackexchange :))。

标签: c# asp.net asp.net-mvc


【解决方案1】:

你的权利这不需要存储在 Session 中,恕我直言不应该,我会将它重构到它自己的类中并使用HttpContext.Current

public interface ISubDomainProvider
{
    string SubDomain { get; set; }
}


public class SubDomainProvider : ISubDomainProvider
{
    public SubDomainProvider()
    {
        string host = HttpContext.Current.Request.Url.Host; // not checked (off the top of my head
        if (host.Split('.').Length > 1) 
        {
            int index = host.IndexOf(".");
            string subdomain = host.Substring(0, index);
            if (subdomain != "www") 
            {
                SubDomain = subdomain;
            }
        }
    }

    public string SubDomain { get; set; }
}

您选择如何使用它,如果您使用 IoC 容器,则只需通过构造函数将此类注入到您的控制器中,我喜欢这样,因为它更容易模拟和单元测试。当然你仍然可以这样做:

public class SomeController : Controller
{
    private readonly ISubDomainProvider _subDomainProvider;
    public SomeController()
    {
        _subDomainProvider = new SubDomainProvider();
    }
} 

您甚至可以创建自己的抽象控制器类:

public abstract class MyAbstractController : Controller
{
    public MyAbstractController()
    {
        SubDomain = new SubDomainProvider();
    }

    protected string SubDomain {get; set; }
}

public class SomeController : MyAbstractController
{
    public ActionResult SomeAction()
    {
        // access the subdomain by calling the base base.SubDomain 
    }
}

【讨论】:

  • 这看起来是个不错的方法,而且似乎比使用属性更灵活。谢谢!
  • @FooBar 为什么你不觉得它不应该存储在会话中?它是用户特定的,它需要在会话期间可用,并且只需要一次(会话开始后)。仅供参考,将 HttpContext 之类的外部依赖项埋在您的 SubDomainProvider 中并不是最好的主意。更好的方法是使用GetSubDomain(string url) 之类的方法并传入请求 URL。
  • @James 好问题!我认为这是特定于请求而不是特定于用户的。它只需要在请求期间可用,由于请求 URL 可用,因此不需要在请求之间持续存在。会话存储会带来开销。那么为什么在不需要的时候使用呢?在单元测试方面,会话也比接口更难模拟。
  • @FooBar 我同意可测试性,但是,除非 OP 真的打算对其进行测试,否则它是无益的。就我个人而言,我只是觉得奇怪的是,当您知道 per session 子域不会更改时,您会 per request 给自己提取工作,执行一次会话开始,仅此而已 - 我的意思是您甚至不需要使用 Session,如果需要,您可以将其存储在 Cache 中。无论如何,这仍然是一个解决方案,我只是对您为什么觉得它不属于会话状态感兴趣。
【解决方案2】:

您可以在global.asax 中的Session_Start 事件上设置Session 中的名称,这意味着它只会发生一次,并且会在用户会话期间持续存在

public void Session_Start(object sender, EventArgs e)
{
    Session["subdomain"] = GetSubDomain(Request.Url);
}

【讨论】:

  • 这是可能的。如果用户访问 client1.example.com,然后访问 client2.example.com,那将启动一个新会话,不是吗?由于网站的性质,用户实际上不太可能这样做,但需要确定边缘情况!
  • @DylanParry 我相信是这样,至少如果您使用的是基于 cookie 的会话(值得测试以确保)。
【解决方案3】:

看起来有一个很好的方法来做我所追求的:

ASP.NET MVC Pass object from Custom Action Filter to Action

它本质上是使用路由数据将自定义参数传递给动作,也可以传递简单字符串等以外的对象。

从好的方面来说,它避免了使用会话并依赖于魔法值,但从不好的方面来说,它意味着为每个请求处理 URL,如果涉及到数据库,这可能不是一个好主意。

【讨论】:

  • 我不确定您为什么认为Session 是错误的解决方案? Session 对象专为正是此目的而设计 - 在用户访问期间存储与用户相关的数据。如果内存分配是一个问题,我会理解,但我们正在谈论一个字符串......
  • 这更多是因为它依赖于我知道会话中有一个神奇的值被设置在代码的更高位置,我一直觉得这就像一个黑客.如果其他人接管了代码,那么这个值的来源可能不会立即显而易见。
  • 我自己认为这不是问题,但是,如果您不想使用 Session 但希望避免每次都提取子域,那么我建议您caching它。这样,您将首先检查缓存,如果它不存在,请提取它,如果您链​​接到的答案是您要采用的方法,我可以将其作为重复项关闭。
猜你喜欢
  • 2012-03-22
  • 1970-01-01
  • 2014-07-03
  • 1970-01-01
  • 1970-01-01
  • 2019-02-07
  • 1970-01-01
  • 2011-02-03
  • 2020-10-17
相关资源
最近更新 更多