使用 IPageRouteModelConvention 和单独的租户页面文件夹配置 Razor Pages 进行多租户
我正在尝试配置 Razor 页面 路由(不是 Razor 视图)并使其通过目录结构支持多租户...
所以,我有一堆针对不同租户的页面,例如:
/Pages/1/About.cshtml
/Pages/2/About.cshtml
/Pages/2/Other.cshtml
... plus many many more ...
以及租户ID查找的来源,即:
"example.com" : 1,
"site.tld" : 2,
...
然后,当有人请求“example.com/About”时,它会正确映射到租户#1子文件夹中的页面(因为“example.com”映射到上面示例中的#1),而不是一个不同的租户“关于”页面。
废弃的解决方案...
- 有很多 Razor View 解决方案,但这不是我正在寻找的(我正在使用 Razor PAGES)。
- 我还看到有人使用 url 重写,但这有点暴力和不优雅,我想要一个合适的路由解决方案。
- 硬编码路由显然可以工作(无论是在映射中还是在页面指令中),但这不可扩展并且容易出错。
可能的解决方案?
使用 IPageRouteModelConvention 似乎是配置 Razor Pages 路由的“正确”方法?
似乎我可以修改路由选择器以删除租户 Id 子目录,从而使页面在根路径处可用。但是,我还需要确保请求适当的租户页面,而不是不同租户的页面...
(我认为)可以做到这一点的一种方法是使用 ActionConstraint(也可以在 IPageRouteModelConvention 中配置)。如果 origin:tenantId 字典是硬编码的,那么我认为这很容易......但是我的租户查找数据需要从数据库中提取(实际上我在 .NET Core 服务集合中添加了一个 TenantCollection 服务作为单例)已经)。
问题是我无法在 builder.Services.Configure(...) 调用中访问 ServiceProvider(以获取我的 TenantCollection)。 因此,我无法创建 ActionConstraint 来限制对某些来源的某些页面的访问,因为我没有租户映射数据。
这是一些示例代码,以防它有助于说明......
builder.Services.AddSingleton<TenantCollection>();
builder.Services.AddRazorPages();
builder.Services.Configure<RazorPagesOptions>(options =>
{
var tenantCollection = GET_MY_TENANT_COLLECTION; // Cant do?
options.Conventions.Add(new MultiTenantPageRouteModelConvention(tenantCollection));
});
我觉得我错过了一些明显的东西,或者从错误的方向解决问题?
I'm trying to configure Razor Pages routing (not Razor Views) and make it support multi-tenancy via directory-structure...
So, I'd have a bunch of pages for different tenants, eg:
/Pages/1/About.cshtml
/Pages/2/About.cshtml
/Pages/2/Other.cshtml
... plus many many more ...
and an origin to tenantID lookup, ie:
"example.com" : 1,
"site.tld" : 2,
...
Then when someone requests "example.com/About" it maps correctly to page in the tenant # 1 subfolder (since "example.com" maps to # 1 in example above), rather than a different tenant "About" page.
Discarded solutions...
- There are a bunch of Razor View solutions but that's not what I'm looking for (I'm using Razor PAGES).
- Also I've seen one person use url-rewriting, but this is a bit brute-force and inelegant and I'd like a proper routing solution.
- Hardcoding routes would obviously work (either in a mapping or in page directives) but this is not scalable and is error-prone.
Possible solution?
Using IPageRouteModelConvention seems like the "correct" way of configuring Razor Pages routes?
Seems like I can modify the route selectors to strip off the tenant Id sub-dir and therefore make the pages available at the root path. However then I also need to make sure the appropriate tenant's page is requested rather than a different tenant's...
One way (I think) this could be done is using an ActionConstraint (which can also be configured in IPageRouteModelConvention). If the origin:tenantId dictionary was hard-coded then I think that would be easy... but my tenant lookup data needs to be pulled from the DB (I actually have a TenantCollection service added as a singleton in the .NET Core service collection already).
The problem is that I don't have access to the ServiceProvider (to get my TenantCollection) at builder.Services.Configure(...) call.
So I can't create the ActionConstraint to restrict access to certain pages for certain origins since I don't have the tenant mapping data.
Here is some example code in-case it helps to illustrate...
builder.Services.AddSingleton<TenantCollection>();
builder.Services.AddRazorPages();
builder.Services.Configure<RazorPagesOptions>(options =>
{
var tenantCollection = GET_MY_TENANT_COLLECTION; // Cant do?
options.Conventions.Add(new MultiTenantPageRouteModelConvention(tenantCollection));
});
I feel like I'm missing something obvious, or attacking the problem from the wrong direction?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
所以,最后我“错过了一些明显的东西”。
在 ActionConstraint 中,可以通过 ActionConstraintContext 的 RouteContext.HttpContext.RequestServices 引用来访问 ServiceProvider。
这使我能够获得做我需要做的事情所需的服务。简单的。
我认为我最好让这篇文章更有价值,而不是就此搁置。所以我将给出我正在做的事情的精简实现,以防万一未来的人发现它有用。
Program.cs
MyPageRouteModelConvention.cs
MyTenantActionConstraint.cs
So, in the end I was "missing something obvious".
In the ActionConstraint the ServiceProvider can be accessed via the ActionConstraintContext's RouteContext.HttpContext.RequestServices reference.
This allows me to get the service I needed to do what I needed. Simple.
Instead of leaving it at that, I figure I might as well make this post more worth while.. so I'll give a stripped down implementation of what I'm doing, just in case some future person finds it useful.
Program.cs
MyPageRouteModelConvention.cs
MyTenantActionConstraint.cs
我有一个测试服务,它将返回一个字符串。那么这段代码对我有用,我可以通过上面的代码调用该服务。
I have a test service which will return a string. then this code worked for me, I can call this service by the code above.