更改 ASP.NET Core Identity UI 中的路由?
Change routing in ASP.NET Core Identity UI?
我正在使用自 ASP.NET Core 2.1 发布以来可用的新 Identity UI 软件包。使用新生成的 MVC 项目,这里有一些可用的页面 URL:
/Home/About
/Home/Contact
/Identity/Account/Login
/Identity/Account/Register
如何配置路由以从 URL 中删除 /Identity/
部分?
看来这还不可能。看源码,很明显Area name是hardcoded in IdentityDefaultUIConfigureOptions<TUser>
:
private const string IdentityUIDefaultAreaName = "Identity";
这在少数地方使用,包括 configuring Razor Pages。例如:
options.Conventions.AuthorizeAreaFolder(IdentityUIDefaultAreaName, "/Account/Manage");
还有 configuring the Cookies authentication。例如:
options.LoginPath = $"/{IdentityUIDefaultAreaName}/Account/Login";
值得注意的是 IdentityDefaultUIConfigureOptions<TUser>
本身是受保护的,因此似乎不存在覆盖选项的能力。
我已经打开了 Github issue 看看我们是否可以从参与项目本身的人那里得到反馈。
2018-06-12更新
ASP.NET Core Identity 团队的 Javier Calvarro Nelson 在我提出的 Github issue 中提供了一些有价值的反馈,总结如下:
The main reason for the Identity UI to be in an area is to minimize the impact on your app and to provide a clean separation between your app code and the Identity code.
Javier 在想要自定义 URL 时推荐以下选项之一:
- 使用默认 UI 的脚手架元素并自行进行所有必要的自定义。
- 使用将旧路由指向新路由的重定向规则。
- 根本不要使用默认 UI。
尽管 不受支持且不推荐 ,Javier 还指出 可以 使用自定义 IPageApplicationModelConvention
来覆盖URLs。但是,如果您错过了,不受支持且不推荐。
2018-06-27更新
official documentation 现已更新,以更好地解释上述 URL 更改。
就路由而言,它在 Web 框架中的标准是固定身份验证 URL,Django 做同样的事情。以下是如何根据自己的喜好自定义视图,因此我们不会从路由中删除 /Identity/,而是告诉 Identity 不要包含他们的视图并提供我们的路由。
转到Startup.cs
:
// USE METHOD WITH LESS DEFAULTS
//
// services.AddDefaultIdentity<IdentityUser>()
// .AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentity<IdentityUser, IdentityRole>(options => options.Stores.MaxLengthForKeys = 128)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
//
// ADD A ROUTE BELOW THE DEFAULT ROUTE
//
routes.MapRoute(
name: "identity",
template: "Identity/{controller=Account}/{action=Register}/{id?}");
现在我们已经设置了除视图之外的所有内容,所以我们需要按照我们通常在 mvc 中做的方式创建一个到它的路由。做一个帐户控制器。将 Index() 更改为 Register()。在名为 Account 的视图中创建一个文件夹。添加一个文件Register.cshtml
,这里是原来的html,根据您的需要定制:
<div class="container body-content">
<h2>Register</h2>
<div class="row">
<div class="col-md-4">
<form method="post" action="/Identity/Account/Register" novalidate="novalidate">
<h4>Create a new account.</h4>
<hr>
<div class="text-danger validation-summary-valid" data-valmsg-summary="true"><ul><li style="display:none"></li>
</ul></div>
<div class="form-group">
<label for="Input_Email">Email</label>
<input class="form-control" type="email" data-val="true" data-val-email="The Email field is not a valid e-mail address." data-val-required="The Email field is required." id="Input_Email" name="Input.Email" value="">
<span class="text-danger field-validation-valid" data-valmsg-for="Input.Email" data-valmsg-replace="true"></span>
</div>
<div class="form-group">
<label for="Input_Password">Password</label>
<input class="form-control" type="password" data-val="true" data-val-length="The Password must be at least 6 and at max 100 characters long." data-val-length-max="100" data-val-length-min="6" data-val-required="The Password field is required." id="Input_Password" name="Input.Password">
<span class="text-danger field-validation-valid" data-valmsg-for="Input.Password" data-valmsg-replace="true"></span>
</div>
<div class="form-group">
<label for="Input_ConfirmPassword">Confirm password</label>
<input class="form-control" type="password" data-val="true" data-val-equalto="The password and confirmation password do not match." data-val-equalto-other="*.Password" id="Input_ConfirmPassword" name="Input.ConfirmPassword">
<span class="text-danger field-validation-valid" data-valmsg-for="Input.ConfirmPassword" data-valmsg-replace="true"></span>
</div>
<button type="submit" class="btn btn-default">Register</button>
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8IWbPHM_NTJDv_7HGewWzbbRveP09yQOznYdTWL2aN5X_4_eVbNE1w8D_qz7zegloVtdAhuVOJbJLQo0ja73FB3PgYycyGpn-DfX3fJqv4Cx8ns6Ygh6M7nMxV0eozO7hoDxUfPwrIJb2RcFtyzhPpMevZ4P0M8aVyBP55SP-5C4l23dCtDXXUOAY_YLwt67dw"></form>
</div>
</div>
<hr>
<footer>
<p>© 2018 - SqlServerApp</p>
</footer>
</div>
在您的 startup.cs 中,您可以更改:
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
与 :
services.AddMvc().AddRazorPagesOptions(o => o.Conventions.AddAreaFolderRouteModelConvention("Identity", "/Account/", model =>
{
foreach (var selector in model.Selectors)
{
var attributeRouteModel = selector.AttributeRouteModel;
attributeRouteModel.Order = -1;
attributeRouteModel.Template = attributeRouteModel.Template.Remove(0, "Identity".Length);
}
})
).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
它将路由:
/Identity/Account/Login 至 /Account/Login
/Identity/Account/Register 至 /Account/Register
等...
要处理 ReturnUrl,您可以创建一个新操作:
[Route("Identity/Account/Login")]
public IActionResult LoginRedirect(string ReturnUrl)
{
return Redirect("/Account/Login?ReturnUrl=" + ReturnUrl);
}
最简单的方法是将 Pages 文件夹从 Areas/Identity 拖到主项目中
请记住,@page 指令(在 .cshtml 中)使 "Pages" 下的任何内容都可以直接访问视图(页面已变成动作)
如果您想更改默认设置,您也可以将帐户文件夹重命名为其他名称
/Account/Login 等页面
@page 指令也可用于指定自定义路径,例如:
@page "/登录"
通过导航到 /Login 直接访问登录页面
URL Rewriting Middleware可能是一个解决方案:
var options = new RewriteOptions()
.AddRewrite(@"^Account/(.*)", "Identity/Account/", skipRemainingRules: true);
app.UseRewriter(options);
从 .net core 3.1 和 2021 开始(我知道有 .net 5,有罪)虽然它通常不会删除路径的“身份”部分,但您可以将生成的剃刀页面映射到第一页一个到所需的路线:
在页眉中,默认只有 @page
,因此请将其更改为您想要的路线 @page "/login"
。
请注意,如果某些 MVC 路由匹配路由 /login,它将使用相应的控制器操作。如果您不希望您的 MVC 路由覆盖剃刀页面路由,我建议在 Startup.cs
的 Configure
方法中移动 endpoints.MapRazorPages();
高于所有(或至少一些)MVC 路由。
根据 David 的回答,我能够去掉登录页面路由中的“Identity/Account”。我正在开发 Asp.net 3.1 应用程序并使用脚手架身份页面。
我已经更改了 login.cshtml 页面中的 @page 指令,如下所示,并更改了 startup.cs 文件。
login.cshtml
@page "/Login"
startup.cs
services.ConfigureApplicationCookie(options =>
{
// httponly cookie
options.Cookie.HttpOnly = true;
options.Cookie.Name = "application.Identity";
***options.LoginPath = "/Login";*** // earlier it was options.LoginPath = "/Identity/Account/Login";
options.AccessDeniedPath = "/Error/Index/403";
options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
options.SlidingExpiration = true;
});
另请注意,如果您在应用程序的任何位置使用“/Identity/Account/Login”,您可能也需要更改它们。此外,如果您有相同的 MVC 路由,则必须更加小心并进行相应处理。
我正在使用自 ASP.NET Core 2.1 发布以来可用的新 Identity UI 软件包。使用新生成的 MVC 项目,这里有一些可用的页面 URL:
/Home/About
/Home/Contact
/Identity/Account/Login
/Identity/Account/Register
如何配置路由以从 URL 中删除 /Identity/
部分?
看来这还不可能。看源码,很明显Area name是hardcoded in IdentityDefaultUIConfigureOptions<TUser>
:
private const string IdentityUIDefaultAreaName = "Identity";
这在少数地方使用,包括 configuring Razor Pages。例如:
options.Conventions.AuthorizeAreaFolder(IdentityUIDefaultAreaName, "/Account/Manage");
还有 configuring the Cookies authentication。例如:
options.LoginPath = $"/{IdentityUIDefaultAreaName}/Account/Login";
值得注意的是 IdentityDefaultUIConfigureOptions<TUser>
本身是受保护的,因此似乎不存在覆盖选项的能力。
我已经打开了 Github issue 看看我们是否可以从参与项目本身的人那里得到反馈。
2018-06-12更新
ASP.NET Core Identity 团队的 Javier Calvarro Nelson 在我提出的 Github issue 中提供了一些有价值的反馈,总结如下:
The main reason for the Identity UI to be in an area is to minimize the impact on your app and to provide a clean separation between your app code and the Identity code.
Javier 在想要自定义 URL 时推荐以下选项之一:
- 使用默认 UI 的脚手架元素并自行进行所有必要的自定义。
- 使用将旧路由指向新路由的重定向规则。
- 根本不要使用默认 UI。
尽管 不受支持且不推荐 ,Javier 还指出 可以 使用自定义 IPageApplicationModelConvention
来覆盖URLs。但是,如果您错过了,不受支持且不推荐。
2018-06-27更新
official documentation 现已更新,以更好地解释上述 URL 更改。
就路由而言,它在 Web 框架中的标准是固定身份验证 URL,Django 做同样的事情。以下是如何根据自己的喜好自定义视图,因此我们不会从路由中删除 /Identity/,而是告诉 Identity 不要包含他们的视图并提供我们的路由。
转到Startup.cs
:
// USE METHOD WITH LESS DEFAULTS
//
// services.AddDefaultIdentity<IdentityUser>()
// .AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentity<IdentityUser, IdentityRole>(options => options.Stores.MaxLengthForKeys = 128)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
//
// ADD A ROUTE BELOW THE DEFAULT ROUTE
//
routes.MapRoute(
name: "identity",
template: "Identity/{controller=Account}/{action=Register}/{id?}");
现在我们已经设置了除视图之外的所有内容,所以我们需要按照我们通常在 mvc 中做的方式创建一个到它的路由。做一个帐户控制器。将 Index() 更改为 Register()。在名为 Account 的视图中创建一个文件夹。添加一个文件Register.cshtml
,这里是原来的html,根据您的需要定制:
<div class="container body-content">
<h2>Register</h2>
<div class="row">
<div class="col-md-4">
<form method="post" action="/Identity/Account/Register" novalidate="novalidate">
<h4>Create a new account.</h4>
<hr>
<div class="text-danger validation-summary-valid" data-valmsg-summary="true"><ul><li style="display:none"></li>
</ul></div>
<div class="form-group">
<label for="Input_Email">Email</label>
<input class="form-control" type="email" data-val="true" data-val-email="The Email field is not a valid e-mail address." data-val-required="The Email field is required." id="Input_Email" name="Input.Email" value="">
<span class="text-danger field-validation-valid" data-valmsg-for="Input.Email" data-valmsg-replace="true"></span>
</div>
<div class="form-group">
<label for="Input_Password">Password</label>
<input class="form-control" type="password" data-val="true" data-val-length="The Password must be at least 6 and at max 100 characters long." data-val-length-max="100" data-val-length-min="6" data-val-required="The Password field is required." id="Input_Password" name="Input.Password">
<span class="text-danger field-validation-valid" data-valmsg-for="Input.Password" data-valmsg-replace="true"></span>
</div>
<div class="form-group">
<label for="Input_ConfirmPassword">Confirm password</label>
<input class="form-control" type="password" data-val="true" data-val-equalto="The password and confirmation password do not match." data-val-equalto-other="*.Password" id="Input_ConfirmPassword" name="Input.ConfirmPassword">
<span class="text-danger field-validation-valid" data-valmsg-for="Input.ConfirmPassword" data-valmsg-replace="true"></span>
</div>
<button type="submit" class="btn btn-default">Register</button>
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8IWbPHM_NTJDv_7HGewWzbbRveP09yQOznYdTWL2aN5X_4_eVbNE1w8D_qz7zegloVtdAhuVOJbJLQo0ja73FB3PgYycyGpn-DfX3fJqv4Cx8ns6Ygh6M7nMxV0eozO7hoDxUfPwrIJb2RcFtyzhPpMevZ4P0M8aVyBP55SP-5C4l23dCtDXXUOAY_YLwt67dw"></form>
</div>
</div>
<hr>
<footer>
<p>© 2018 - SqlServerApp</p>
</footer>
</div>
在您的 startup.cs 中,您可以更改:
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
与 :
services.AddMvc().AddRazorPagesOptions(o => o.Conventions.AddAreaFolderRouteModelConvention("Identity", "/Account/", model =>
{
foreach (var selector in model.Selectors)
{
var attributeRouteModel = selector.AttributeRouteModel;
attributeRouteModel.Order = -1;
attributeRouteModel.Template = attributeRouteModel.Template.Remove(0, "Identity".Length);
}
})
).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
它将路由:
/Identity/Account/Login 至 /Account/Login
/Identity/Account/Register 至 /Account/Register
等...
要处理 ReturnUrl,您可以创建一个新操作:
[Route("Identity/Account/Login")]
public IActionResult LoginRedirect(string ReturnUrl)
{
return Redirect("/Account/Login?ReturnUrl=" + ReturnUrl);
}
最简单的方法是将 Pages 文件夹从 Areas/Identity 拖到主项目中 请记住,@page 指令(在 .cshtml 中)使 "Pages" 下的任何内容都可以直接访问视图(页面已变成动作) 如果您想更改默认设置,您也可以将帐户文件夹重命名为其他名称 /Account/Login 等页面
@page 指令也可用于指定自定义路径,例如: @page "/登录"
通过导航到 /Login 直接访问登录页面
URL Rewriting Middleware可能是一个解决方案:
var options = new RewriteOptions()
.AddRewrite(@"^Account/(.*)", "Identity/Account/", skipRemainingRules: true);
app.UseRewriter(options);
从 .net core 3.1 和 2021 开始(我知道有 .net 5,有罪)虽然它通常不会删除路径的“身份”部分,但您可以将生成的剃刀页面映射到第一页一个到所需的路线:
在页眉中,默认只有 @page
,因此请将其更改为您想要的路线 @page "/login"
。
请注意,如果某些 MVC 路由匹配路由 /login,它将使用相应的控制器操作。如果您不希望您的 MVC 路由覆盖剃刀页面路由,我建议在 Startup.cs
的 Configure
方法中移动 endpoints.MapRazorPages();
高于所有(或至少一些)MVC 路由。
根据 David 的回答,我能够去掉登录页面路由中的“Identity/Account”。我正在开发 Asp.net 3.1 应用程序并使用脚手架身份页面。
我已经更改了 login.cshtml 页面中的 @page 指令,如下所示,并更改了 startup.cs 文件。
login.cshtml
@page "/Login"
startup.cs
services.ConfigureApplicationCookie(options =>
{
// httponly cookie
options.Cookie.HttpOnly = true;
options.Cookie.Name = "application.Identity";
***options.LoginPath = "/Login";*** // earlier it was options.LoginPath = "/Identity/Account/Login";
options.AccessDeniedPath = "/Error/Index/403";
options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
options.SlidingExpiration = true;
});
另请注意,如果您在应用程序的任何位置使用“/Identity/Account/Login”,您可能也需要更改它们。此外,如果您有相同的 MVC 路由,则必须更加小心并进行相应处理。