属性路由显示为查询字符串
Attribute route shows up as query string
我正在使用属性路由,这就是我映射其中一个创建操作的方式:
[Route("Create/{companyId:guid?}/{branchId:guid?}/{departmentId:guid?}")]
[Route("Create/{companyId:guid?}/{departmentId:guid?}")]
public async Task<ActionResult> Create(Guid? companyId, Guid? branchId, Guid? departmentId)
我还尝试了两个映射到相同动作名称的不同动作,如下所示:
[Route("Create/{companyId:guid?}/{branchId:guid?}/{departmentId:guid?}")]
public async Task<ActionResult> Create(Guid? companyId, Guid? branchId, Guid? departmentId)
[Route("Create/{companyId:guid?}/{departmentId:guid?}")]
[ActionName("Create")]
public async Task<ActionResult> CreateWithDepartmentOnly(Guid? companyId, Guid? departmentId)
这样设置是因为可能没有branchId
而且你不能有一个空参数。
仅提供 companyId
或同时提供 companyId
和 departmentId
时工作正常。
当提供 branchId
时,该 ID 被添加为查询字符串,而不是路由参数。当我有两个不同的操作时会发生这种情况,而且是否提供 departmentId
也无关紧要。
我的代码适用于所有情况,我只想清理 URL 并且那里没有任何查询字符串。任何解决方案或解决方法?
编辑 1
我尝试了 Slicksim 的两个想法。
- 设置路由顺序无效
- 给路由命名就成功了,但只有在提供所有 3 个 ID 的情况下,省略最后一个参数
departmentId
应该没有效果,但是 branchId
却被忽略了
编辑 2
逻辑是这样的:
- 一个公司可以有分支机构and/or个部门
- 一个分支可以有部门
- 一个部门可以有多个部门
- 一个部门不一定要有一个分支机构
根据您创建部门的位置,这些 ID 在路线中。这是可行的解决方案吗?
[Route("Create/{companyId:guid}")]
[Route("Create/{companyId:guid}/{branchId:guid?}")]
[Route("Create/{companyId:guid}/{departmentId:guid?}")]
[Route("Create/{companyId:guid}/{branchId:guid?}/{departmentId:guid?}")]
public async Task<ActionResult> Create(Guid companyId, Guid? branchId, Guid? departmentId)
路由 2 和 3 是否可行,因为它们具有相同的参数数量和类型。如果 Id 被放置在操作中正确命名的参数中,那么我很幸运。
检查以下内容:
- 确保您已在 RouteConfig(或应用程序启动的某处)中调用
routes.MapMvcAttributeRoutes()
。
- 请确保在 默认路由之前已将呼叫
routes.MapMvcAttributeRoutes()
。
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Add this to get attribute routes to work
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
此外,您将参数设为可选是在搬起石头砸自己的脚。路由上只能使用 1 个可选参数,并且必须是最右边的参数。这里的第二个路由是一个无法到达的执行路径,因为前三个参数都是可选的。
[Route("Create/{companyId:guid?}/{branchId:guid?}/{departmentId:guid?}")]
[Route("Create/{companyId:guid?}/{departmentId:guid?}")]
从技术上讲,你可以用一条路线做你想做的事。
[Route("Create/{companyId:guid}/{departmentId:guid}/{branchId:guid?}")]
public async Task<ActionResult> Create(Guid companyId, Guid departmentId, Guid? branchId)
哪个会匹配
/Create/someCompanyId/someDepartmentId/someBranchId
/Create/someCompanyId/someDepartmentId
如果您确实希望所有参数都是可选的,最好的方法是使用查询字符串。路线不是为那种事情设计的,但如果你真的想使用路线去那里,请参阅 。
编辑 2 的答案
删除默认路由没有错。但是,您的业务逻辑对于默认路由来说有点太复杂了。您的评估是正确的,即第二条和第三条路线彼此无法区分(即使您设置了所需的参数),因为模式相同。
但是 .NET 路由比 MapRoute
或 Route
属性可以单独提供的更灵活。
此外,如果这些 URL 面向 Internet(不在登录后),这不是 SEO 友好的方法。 URLnames中的company, branch, department, subdepartment names会好很多26=]
无论您是否决定坚持使用 GUID 方案,您都拥有一组独特的 URL,这就是获得匹配所需的全部。您需要将每个可能的 URL 加载到缓存中,您可以根据传入请求检查缓存,然后您可以通过创建 手动提供路由值。您只需更改 PageInfo
对象以保存所有 3 个 GUID 值并相应地更新路由。但是请注意,如果您执行 table 连接并提出一个 GUID 来表示链接答案中的每个 URL,那会更好(实现的逻辑更少)。
public class PageInfo
{
public PageInfo()
{
CompanyId = Guid.Empty;
BranchId = Guid.Empty;
DepartmentId = Guid.Empty;
}
// VirtualPath should not have a leading slash
// example: events/conventions/mycon
public string VirtualPath { get; set; }
public Guid CompanyId { get; set; }
public Guid BranchId { get; set; }
public Guid DepartmentId { get; set; }
}
在GetRouteData
result.Values["controller"] = "CustomPage";
result.Values["action"] = "Details";
if (!page.CompanyId.Equals(Guid.Empty))
{
result.Values["companyId"] = page.CompanyId;
}
if (!page.BranchId.Equals(Guid.Empty))
{
result.Values["branchId"] = page.BranchId;
}
if (!page.DepartmentId.Equals(Guid.Empty))
{
result.Values["departmentId"] = page.DepartmentId;
}
在TryFindMatch
Guid companyId = Guid.Empty;
Guid branchId = Guid.Empty;
Guid departmentId = Guid.Empty;
Guid.TryParse(Convert.ToString(values["companyId"]), out companyId);
Guid.TryParse(Convert.ToString(values["branchId"]), out branchId);
Guid.TryParse(Convert.ToString(values["departmentId"]), out departmentId);
var controller = Convert.ToString(values["controller"]);
var action = Convert.ToString(values["action"]);
if (action == "Details" && controller == "CustomPage")
{
page = pages
.Where(x => x.CompanyId.Equals(companyId) &&
x.BranchId.Equals(branchId) &&
x.DepartmentId.Equals(departmentId))
.FirstOrDefault();
if (page != null)
{
return true;
}
}
return false;
我正在使用属性路由,这就是我映射其中一个创建操作的方式:
[Route("Create/{companyId:guid?}/{branchId:guid?}/{departmentId:guid?}")]
[Route("Create/{companyId:guid?}/{departmentId:guid?}")]
public async Task<ActionResult> Create(Guid? companyId, Guid? branchId, Guid? departmentId)
我还尝试了两个映射到相同动作名称的不同动作,如下所示:
[Route("Create/{companyId:guid?}/{branchId:guid?}/{departmentId:guid?}")]
public async Task<ActionResult> Create(Guid? companyId, Guid? branchId, Guid? departmentId)
[Route("Create/{companyId:guid?}/{departmentId:guid?}")]
[ActionName("Create")]
public async Task<ActionResult> CreateWithDepartmentOnly(Guid? companyId, Guid? departmentId)
这样设置是因为可能没有branchId
而且你不能有一个空参数。
仅提供 companyId
或同时提供 companyId
和 departmentId
时工作正常。
当提供 branchId
时,该 ID 被添加为查询字符串,而不是路由参数。当我有两个不同的操作时会发生这种情况,而且是否提供 departmentId
也无关紧要。
我的代码适用于所有情况,我只想清理 URL 并且那里没有任何查询字符串。任何解决方案或解决方法?
编辑 1
我尝试了 Slicksim 的两个想法。
- 设置路由顺序无效
- 给路由命名就成功了,但只有在提供所有 3 个 ID 的情况下,省略最后一个参数
departmentId
应该没有效果,但是branchId
却被忽略了
编辑 2
逻辑是这样的:
- 一个公司可以有分支机构and/or个部门
- 一个分支可以有部门
- 一个部门可以有多个部门
- 一个部门不一定要有一个分支机构
根据您创建部门的位置,这些 ID 在路线中。这是可行的解决方案吗?
[Route("Create/{companyId:guid}")]
[Route("Create/{companyId:guid}/{branchId:guid?}")]
[Route("Create/{companyId:guid}/{departmentId:guid?}")]
[Route("Create/{companyId:guid}/{branchId:guid?}/{departmentId:guid?}")]
public async Task<ActionResult> Create(Guid companyId, Guid? branchId, Guid? departmentId)
路由 2 和 3 是否可行,因为它们具有相同的参数数量和类型。如果 Id 被放置在操作中正确命名的参数中,那么我很幸运。
检查以下内容:
- 确保您已在 RouteConfig(或应用程序启动的某处)中调用
routes.MapMvcAttributeRoutes()
。 - 请确保在 默认路由之前已将呼叫
routes.MapMvcAttributeRoutes()
。
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Add this to get attribute routes to work
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
此外,您将参数设为可选是在搬起石头砸自己的脚。路由上只能使用 1 个可选参数,并且必须是最右边的参数。这里的第二个路由是一个无法到达的执行路径,因为前三个参数都是可选的。
[Route("Create/{companyId:guid?}/{branchId:guid?}/{departmentId:guid?}")]
[Route("Create/{companyId:guid?}/{departmentId:guid?}")]
从技术上讲,你可以用一条路线做你想做的事。
[Route("Create/{companyId:guid}/{departmentId:guid}/{branchId:guid?}")]
public async Task<ActionResult> Create(Guid companyId, Guid departmentId, Guid? branchId)
哪个会匹配
/Create/someCompanyId/someDepartmentId/someBranchId
/Create/someCompanyId/someDepartmentId
如果您确实希望所有参数都是可选的,最好的方法是使用查询字符串。路线不是为那种事情设计的,但如果你真的想使用路线去那里,请参阅
编辑 2 的答案
删除默认路由没有错。但是,您的业务逻辑对于默认路由来说有点太复杂了。您的评估是正确的,即第二条和第三条路线彼此无法区分(即使您设置了所需的参数),因为模式相同。
但是 .NET 路由比 MapRoute
或 Route
属性可以单独提供的更灵活。
此外,如果这些 URL 面向 Internet(不在登录后),这不是 SEO 友好的方法。 URLnames中的company, branch, department, subdepartment names会好很多26=]
无论您是否决定坚持使用 GUID 方案,您都拥有一组独特的 URL,这就是获得匹配所需的全部。您需要将每个可能的 URL 加载到缓存中,您可以根据传入请求检查缓存,然后您可以通过创建 PageInfo
对象以保存所有 3 个 GUID 值并相应地更新路由。但是请注意,如果您执行 table 连接并提出一个 GUID 来表示链接答案中的每个 URL,那会更好(实现的逻辑更少)。
public class PageInfo
{
public PageInfo()
{
CompanyId = Guid.Empty;
BranchId = Guid.Empty;
DepartmentId = Guid.Empty;
}
// VirtualPath should not have a leading slash
// example: events/conventions/mycon
public string VirtualPath { get; set; }
public Guid CompanyId { get; set; }
public Guid BranchId { get; set; }
public Guid DepartmentId { get; set; }
}
在GetRouteData
result.Values["controller"] = "CustomPage";
result.Values["action"] = "Details";
if (!page.CompanyId.Equals(Guid.Empty))
{
result.Values["companyId"] = page.CompanyId;
}
if (!page.BranchId.Equals(Guid.Empty))
{
result.Values["branchId"] = page.BranchId;
}
if (!page.DepartmentId.Equals(Guid.Empty))
{
result.Values["departmentId"] = page.DepartmentId;
}
在TryFindMatch
Guid companyId = Guid.Empty;
Guid branchId = Guid.Empty;
Guid departmentId = Guid.Empty;
Guid.TryParse(Convert.ToString(values["companyId"]), out companyId);
Guid.TryParse(Convert.ToString(values["branchId"]), out branchId);
Guid.TryParse(Convert.ToString(values["departmentId"]), out departmentId);
var controller = Convert.ToString(values["controller"]);
var action = Convert.ToString(values["action"]);
if (action == "Details" && controller == "CustomPage")
{
page = pages
.Where(x => x.CompanyId.Equals(companyId) &&
x.BranchId.Equals(branchId) &&
x.DepartmentId.Equals(departmentId))
.FirstOrDefault();
if (page != null)
{
return true;
}
}
return false;