一、介绍
在ASP.NET Core中,路由是将传入的URL请求映射到正确的控制器和操作的方法。Attribute路由是一种基于属性,用于定义路由规则的方式,通过在控制器类和操作方法上应用特定的属性,来定义URL模板。
- 基本概念:
- **路由:**在ASP.NET Core中,路由是将URL请求映射到正确的控制器和操作的过程。路由中间件会按照定义的路由规则,将传入的HTTP请求匹配到正确的路由路径,进而执行对应的控制器和操作方法。
- **控制器:**控制器是处理HTTP请求的类,其中包含处理请求的操作方法。在ASP.NET Core中,控制器类必须继承自Controller或ControllerBase类。
- **操作方法:**操作方法是控制器中用于处理HTTP请求的具体实现。通过在控制器类或操作方法上应用特定的属性,可以定义URL模板,从而实现路由匹配。
- 重要性:
- 可读性更强:使用属性路由,可以定义更加清晰和易读的路由路径,使得URL更加友好和易于理解。
- **灵活性更高:**属性路由可以更加灵活地定义路由规则,支持多种路由匹配方式,如默认路由、自定义路由、参数路由等。
- **可维护性更高:**属性路由的路由规则定义更加集中和清晰,易于维护和管理。同时,由于路由规则定义在控制器类和操作方法上,可以更好地与代码分离,提高代码的可读性和可维护性。
- **性能更优:**属性路由在路由匹配时,可以利用编译时静态分析,提前解析路由模板,从而提高路由匹配的性能。
二、传统路由和属性路由的比较和选择
传统路由和属性路由(Attribute Routing)是ASP.NET Core中的两种主要路由(Routing)方式。下面是它们的比较和选择:
- 传统路由(Convention-based Routing):
传统路由是一种基于约定的路由方式。在传统路由中,我们定义路由规则时,需要指定路由的URL模板以及相应的控制器和操作方法。传统路由是一种静态路由方式,它的路由规则是在应用程序启动时静态定义的。
- 优点:
- 简单易用:传统路由的路由规则定义简单明了,易于理解和使用。
- 性能较高:传统路由的路由规则定义是在应用程序启动时静态定义的,因此在路由匹配时具有较高的性能。
- 缺点:
- 不够灵活:传统路由的路由规则定义是基于约定的,不够灵活,无法满足一些复杂的路由需求。
- 可读性较差:传统路由的路由规则定义在代码中分散开来,可读性较差。
- 属性路由(Attribute Routing):
属性路由是一种基于属性的路由方式。在属性路由中,我们可以在控制器类和操作方法上应用特定的属性来定义路由规则。属性路由是一种动态路由方式,它的路由规则是在运行时动态定义的。
- 优点:
- 更加灵活:属性路由的路由规则定义更加灵活,可以满足一些复杂的路由需求。
- 可读性更好:属性路由的路由规则定义在代码中集中起来,可读性更好。
- 可维护性更高:属性路由的路由规则定义更加集中和清晰,易于维护和管理。
- 缺点:
- 相对复杂:属性路由的路由规则定义相对复杂,需要一定的学习成本。
- 性能较低:属性路由的路由规则定义是在运行时动态定义的,因此在路由匹配时性能相对较低。
- 选择:
在选择传统路由和属性路由时,需要根据具体的应用场景和需求来选择。如果应用程序的路由规则比较简单,且性能要求较高,可以选择传统路由;如果应用程序的路由规则比较复杂,且需要更高的可读性和可维护性,可以选择属性路由。同时,在实际开发中,也可以结合使用传统路由和属性路由,以满足不同的路由需求。
三、Attribute路由的基本使用
3.1 在Controller上使用Attribute路由
在ASP.NET Core中,我们可以在控制器类上使用[Route]
属性来定义控制器级别的路由规则。以下是一个示例:
[Route("api/[controller]")]
public class UsersController : Controller
{
[HttpGet]
public IActionResult Get()
{
return Ok();
}
[HttpGet("{id}")]
public IActionResult Get(int id)
{
return Ok();
}
// ...
}
在上面的示例中,我们在UsersController
类上使用了[Route("api/[controller]")]
属性,这表示所有该控制器的操作方法都可以通过“api/users”路径访问。
Tip:这里的
[controller]
是一个占位符,它会被实际的控制器名称替换。例如,如果你访问api/users
路径,[controller]
将被替换为Users
。
此外,我们还分别在Get
和Get(int id)
方法上使用了[HttpGet]
和[HttpGet("{id}")]
属性来定义它们的路由。其中,[HttpGet]
表示该方法可以通过HTTP GET请求访问,而[HttpGet("{id}")]
表示该方法可以通过具有id
参数的HTTP GET请求访问。
通过这种方式,我们可以方便地定义控制器级别的路由规则,从而更好地组织我们的代码和URL。
3.2 在Action上使用Attribute路由
在ASP.NET Core中,我们可以在操作方法上使用[Route]
属性来定义操作方法级别的路由规则。以下是一个示例:
public class UsersController : Controller
{
[HttpGet("{id:int}")]
public IActionResult GetUser(int id)
{
// ...
}
[HttpPost]
[Route("users/create")]
public IActionResult CreateUser(UserViewModel model)
{
// ...
}
// ...
}
在上面的示例中,我们在GetUser
方法上使用了[HttpGet("{id:int}")]
属性,这表示该方法可以通过具有id
参数的HTTP GET请求访问,并且id
必须是整数类型。而在CreateUser
方法上,我们使用了[HttpPost]
和[Route("users/create")]
属性。这表示该方法可以通过HTTP POST请求访问,并且可以通过“users/create”路径访问。
通过这种方式,我们可以更加精细地定义操作方法级别的路由规则,从而更好地满足我们的需求。注意,操作方法级别的路由规则会覆盖控制器级别的路由规则。如果一个操作方法上定义了路由规则,它将优先于控制器级别的路由规则。
3.3 使用自定义路由
在ASP.NET Core中,我们可以使用自定义路由来实现更加灵活的路由规则。以下是一个示例:
public class CustomRouteAttribute : Attribute, IConfigureRoute
{
public string RouteName { get; set; }
public string[] RouteNames { get; set; }
public string Template { get; set; }
public object[] Constraints { get; set; }
public string[] Order { get; set; }
public void Configure(RouteCollection routes, string routeName)
{
var route = new Route(Template, new CustomRouteHandler());
if (Constraints != null)
{
foreach (var constraint in Constraints)
{
route.Constraints.Add(constraint.GetType().Name, constraint);
}
}
if (Order != null)
{
var routeList = new List<Route>();
foreach (var name in RouteNames)
{
var r = routes.GetRouteData(new MvcContext(HttpContext.Current));
while (r != null)
{
if (r.Route.RouteName.Equals(name, StringComparison.OrdinalIgnoreCase))
{
routeList.Add(r.Route);
break;
}
r = r.Route.Parent;
}
}
if (routeList.Count > 0)
{
var index = routeList.IndexOf(route);
if (index >= 0)
{
routeList.RemoveAt(index);
routeList.Insert(index, route);
}
}
else
{
routes.Add(route);
}
}
else
{
routes.Add(route);
}
}
}
[CustomRoute(RouteName = "custom", Template = "custom/{id}")]
public class CustomController : Controller
{
public IActionResult Index()
{
return View();
}
}
在上面的示例中,我们自定义了一个CustomRouteAttribute
属性,并在CustomController
上使用它来定义路由规则。该属性的Template
属性定义了路由模板,RouteName
属性定义了路由名称,还可以定义其他的路由约束和路由顺序等。在Configure
方法中,我们通过RouteCollection
和RouteName
来添加路由规则,并且可以根据需要对路由规则进行排序。
通过这种方式,我们可以更加灵活地定义路由规则,从而更好地满足我们的需求。注意,在使用自定义路由时,需要将UseMvc
替换为UseMvcWithDefaultRoute
,并且需要在Startup.cs
文件的ConfigureServices
方法中注册自定义路由。
四、Attribute路由的高级使用
4.1 路由参数
在 ASP.NET Core 中,我们可以使用 Attribute 路由来定义路由参数。这可以让我们更精确地控制路由的生成。
下面是一个基本的例子:
[Route("api/[controller]")]
public class UsersController : Controller
{
[HttpGet("{id:int}")]
public IActionResult GetUser(int id)
{
// ...
}
}
在这个例子中,[Route("api/[controller]")]
是控制器级别的路由,表示所有 UsersController 的操作都会被路由到 “api/users” 路径。[HttpGet("{id:int}")]
是操作级别的路由,表示 GetUser 方法可以被通过 GET 请求访问,并且需要一个整数参数 “id”。
你可以使用不同的 HTTP 方法来定义路由,例如:
[HttpGet("{id:int}")]
public IActionResult GetUser(int id)
{
// ...
}
[HttpPost]
public IActionResult CreateUser(UserModel model)
{
// ...
}
在上面的例子中,[HttpPost]
表示 CreateUser 方法只能通过 POST 请求访问。
你还可以使用自定义的路由约束来限制路由参数的取值范围,例如:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public class PositiveIntConstraint : Attribute, IHttpRouteConstraint
{
public bool Match(HttpContext context, RouteData data, string parameterName, out RouteValueDictionary values)
{
var value = data.Values[parameterName];
if (value != null && value.ToString().IsInt() && Convert.ToInt32(value) > 0)
{
values = new RouteValueDictionary(new { });
return true;
}
return false;
}
}
public class UsersController : Controller
{
[HttpGet("{id}", Name = "GetUserById")]
[PositiveIntConstraint]
public IActionResult GetUser(int id)
{
// ...
}
}
在上面的例子中,PositiveIntConstraint
是一个自定义的路由约束,它限制了路由参数 “id” 必须是一个大于 0 的整数。如果不符合这个条件,路由请求将会失败。
4.2 其他高级功能
除了上述的路由参数,Attribute 路由还有其他一些高级功能,包括:
- 路由模板:你可以使用和传统路由一样的模板语法来定义 Attribute 路由。比如,
[Route("{controller}/{action}/{id}")]
。 - 可选参数:你可以定义可选的路由参数。比如,
[Route("products/{id:int?}")]
,这里 id 是可选的。 - 默认值:你可以给路由参数设置默认值。比如,
[Route("{controller=Home}/{action=Index}/{id=0}")]
,这里 controller 的默认值是 Home,action 的默认值是 Index,id 的默认值是 0。 - 约束类型:除了整数,你还可以对其他类型的参数进行约束。比如,
[HttpGet("{id:guid}")]
可以约束 id 必须是 GUID 类型。 - 自定义约束:你可以定义自己的约束。比如,你可以定义一个约束来检查一个字符串是否是一个有效的 email 地址。
- 嵌套路由:你可以在一个路由中嵌套另一个路由。比如,
[Route("{category}/{product}")]
,这里 category 和 product 都是路由参数。 - 静态和动态路由:你可以结合使用静态和动态路由。比如,
[Route("/about")]
和[Route("{id}")]
可以同时存在。
五、示例
好的,以下是一个综合了前面所有内容的示例:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public class PositiveIntConstraint : Attribute, IHttpRouteConstraint
{
public bool Match(HttpContext context, RouteData data, string parameterName, out RouteValueDictionary values)
{
var value = data.Values[parameterName];
if (value != null && value.ToString().IsInt() && Convert.ToInt32(value) > 0)
{
values = new RouteValueDictionary(new { });
return true;
}
return false;
}
}
public class UsersController : Controller
{
[HttpGet("{id}", Name = "GetUserById")]
[PositiveIntConstraint]
public IActionResult GetUser(int id)
{
// ...
}
[HttpGet("users/edit/{id:guid}")]
public IActionResult EditUser(Guid id)
{
// ...
}
[HttpPost]
[Route("users/create")]
public IActionResult CreateUser(UserModel model)
{
// ...
}
[Route("users/deleted/{id}")]
public IActionResult DeleteUser(int id)
{
// ...
}
[Route("users/restore/{id}")]
public IActionResult RestoreUser(int id)
{
// ...
}
[Route("{*url}", Name = "PageNotFound")]
public IActionResult PageNotFound()
{
// ...
}
}
在上面的例子中,我们定义了一个 PositiveIntConstraint
约束来限制路由参数必须是一个大于 0 的整数。我们定义了 5 个不同的路由:
-
GetUser
方法可以通过/users/123
这样的 URL 访问,其中 123 是一个大于 0 的整数。 -
EditUser
方法可以通过/users/edit/456
这样的 URL 访问,其中 456 是一个 GUID 类型的参数。 -
CreateUser
方法可以通过/users/create
这样的 URL 访问,不需要任何参数。 -
DeleteUser
方法可以通过/users/deleted/789
这样的 URL 访问,其中 789 是一个大于 0 的整数。 -
RestoreUser
方法可以通过/users/restore/1000
这样的 URL 访问,其中 1000 是一个大于 0 的整数。 - 如果请求的 URL 不符合上面的任何一个路由,那么就会返回
PageNotFound
方法的结果,这个方法会返回一个 404 页面。
六、总结
Attribute路由是一种强大的路由机制,允许我们在 ASP.NET Core 中灵活地定义路由。通过使用各种属性和约束,我们可以精确控制 URL 的生成和解析。Attribute 路由提供了路由参数、可选参数、默认值、约束类型、自定义约束、嵌套路由、静态和动态路由等高级功能。这使得我们能够构建复杂而强大的 Web 应用程序,同时也提高了代码的可读性和可维护性。通过合理地使用 Attribute 路由,我们可以提升 Web 应用程序的性能和用户体验。