【ASP.NET Core 基础知识】--路由和请求处理--Attribute路由

一、介绍

在ASP.NET Core中,路由是将传入的URL请求映射到正确的控制器和操作的方法。Attribute路由是一种基于属性,用于定义路由规则的方式,通过在控制器类和操作方法上应用特定的属性,来定义URL模板。

  1. 基本概念:

    • **路由:**在ASP.NET Core中,路由是将URL请求映射到正确的控制器和操作的过程。路由中间件会按照定义的路由规则,将传入的HTTP请求匹配到正确的路由路径,进而执行对应的控制器和操作方法。
    • **控制器:**控制器是处理HTTP请求的类,其中包含处理请求的操作方法。在ASP.NET Core中,控制器类必须继承自Controller或ControllerBase类。
    • **操作方法:**操作方法是控制器中用于处理HTTP请求的具体实现。通过在控制器类或操作方法上应用特定的属性,可以定义URL模板,从而实现路由匹配。
  2. 重要性:

    • 可读性更强:使用属性路由,可以定义更加清晰和易读的路由路径,使得URL更加友好和易于理解。
    • **灵活性更高:**属性路由可以更加灵活地定义路由规则,支持多种路由匹配方式,如默认路由、自定义路由、参数路由等。
    • **可维护性更高:**属性路由的路由规则定义更加集中和清晰,易于维护和管理。同时,由于路由规则定义在控制器类和操作方法上,可以更好地与代码分离,提高代码的可读性和可维护性。
    • **性能更优:**属性路由在路由匹配时,可以利用编译时静态分析,提前解析路由模板,从而提高路由匹配的性能。

二、传统路由和属性路由的比较和选择

传统路由和属性路由(Attribute Routing)是ASP.NET Core中的两种主要路由(Routing)方式。下面是它们的比较和选择:

  1. 传统路由(Convention-based Routing):

    传统路由是一种基于约定的路由方式。在传统路由中,我们定义路由规则时,需要指定路由的URL模板以及相应的控制器和操作方法。传统路由是一种静态路由方式,它的路由规则是在应用程序启动时静态定义的。

    • 优点:

      • 简单易用:传统路由的路由规则定义简单明了,易于理解和使用。
      • 性能较高:传统路由的路由规则定义是在应用程序启动时静态定义的,因此在路由匹配时具有较高的性能。
    • 缺点:

      • 不够灵活:传统路由的路由规则定义是基于约定的,不够灵活,无法满足一些复杂的路由需求。
      • 可读性较差:传统路由的路由规则定义在代码中分散开来,可读性较差。
  2. 属性路由(Attribute Routing):

    属性路由是一种基于属性的路由方式。在属性路由中,我们可以在控制器类和操作方法上应用特定的属性来定义路由规则。属性路由是一种动态路由方式,它的路由规则是在运行时动态定义的。

    • 优点:

      • 更加灵活:属性路由的路由规则定义更加灵活,可以满足一些复杂的路由需求。
      • 可读性更好:属性路由的路由规则定义在代码中集中起来,可读性更好。
      • 可维护性更高:属性路由的路由规则定义更加集中和清晰,易于维护和管理。
    • 缺点:

      • 相对复杂:属性路由的路由规则定义相对复杂,需要一定的学习成本。
      • 性能较低:属性路由的路由规则定义是在运行时动态定义的,因此在路由匹配时性能相对较低。
  3. 选择:

    在选择传统路由和属性路由时,需要根据具体的应用场景和需求来选择。如果应用程序的路由规则比较简单,且性能要求较高,可以选择传统路由;如果应用程序的路由规则比较复杂,且需要更高的可读性和可维护性,可以选择属性路由。同时,在实际开发中,也可以结合使用传统路由和属性路由,以满足不同的路由需求。

三、Attribute路由的基本使用

3.1 在Controller上使用Attribute路由

在ASP.NET Core中,我们可以在控制器类上使用[Route]属性来定义控制器级别的路由规则。以下是一个示例:

csharp 复制代码
[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

此外,我们还分别在GetGet(int id)方法上使用了[HttpGet][HttpGet("{id}")]属性来定义它们的路由。其中,[HttpGet]表示该方法可以通过HTTP GET请求访问,而[HttpGet("{id}")]表示该方法可以通过具有id参数的HTTP GET请求访问。

通过这种方式,我们可以方便地定义控制器级别的路由规则,从而更好地组织我们的代码和URL。

3.2 在Action上使用Attribute路由

在ASP.NET Core中,我们可以在操作方法上使用[Route]属性来定义操作方法级别的路由规则。以下是一个示例:

csharp 复制代码
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中,我们可以使用自定义路由来实现更加灵活的路由规则。以下是一个示例:

csharp 复制代码
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方法中,我们通过RouteCollectionRouteName来添加路由规则,并且可以根据需要对路由规则进行排序。

通过这种方式,我们可以更加灵活地定义路由规则,从而更好地满足我们的需求。注意,在使用自定义路由时,需要将UseMvc替换为UseMvcWithDefaultRoute,并且需要在Startup.cs文件的ConfigureServices方法中注册自定义路由。

四、Attribute路由的高级使用

4.1 路由参数

ASP.NET Core 中,我们可以使用 Attribute 路由来定义路由参数。这可以让我们更精确地控制路由的生成。

下面是一个基本的例子:

csharp 复制代码
[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 方法来定义路由,例如:

csharp 复制代码
[HttpGet("{id:int}")]
public IActionResult GetUser(int id)
{
    // ...
}

[HttpPost]
public IActionResult CreateUser(UserModel model)
{
    // ...
}

在上面的例子中,[HttpPost]表示 CreateUser 方法只能通过 POST 请求访问。

你还可以使用自定义的路由约束来限制路由参数的取值范围,例如:

csharp 复制代码
[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 路由还有其他一些高级功能,包括:

  1. 路由模板 :你可以使用和传统路由一样的模板语法来定义 Attribute 路由。比如,[Route("{controller}/{action}/{id}")]
  2. 可选参数 :你可以定义可选的路由参数。比如,[Route("products/{id:int?}")],这里 id 是可选的。
  3. 默认值 :你可以给路由参数设置默认值。比如,[Route("{controller=Home}/{action=Index}/{id=0}")],这里 controller 的默认值是 Home,action 的默认值是 Index,id 的默认值是 0。
  4. 约束类型 :除了整数,你还可以对其他类型的参数进行约束。比如,[HttpGet("{id:guid}")] 可以约束 id 必须是 GUID 类型。
  5. 自定义约束:你可以定义自己的约束。比如,你可以定义一个约束来检查一个字符串是否是一个有效的 email 地址。
  6. 嵌套路由 :你可以在一个路由中嵌套另一个路由。比如,[Route("{category}/{product}")],这里 category 和 product 都是路由参数。
  7. 静态和动态路由 :你可以结合使用静态和动态路由。比如,[Route("/about")][Route("{id}")] 可以同时存在。

五、示例

好的,以下是一个综合了前面所有内容的示例:

csharp 复制代码
[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 应用程序的性能和用户体验。

相关推荐
安的列斯凯奇5 小时前
SpringBoot篇 单元测试 理论篇
spring boot·后端·单元测试
架构文摘JGWZ5 小时前
FastJson很快,有什么用?
后端·学习
BinaryBardC5 小时前
Swift语言的网络编程
开发语言·后端·golang
邓熙榆5 小时前
Haskell语言的正则表达式
开发语言·后端·golang
专职8 小时前
spring boot中实现手动分页
java·spring boot·后端
Ciderw8 小时前
Go中的三种锁
开发语言·c++·后端·golang·互斥锁·
m0_748246359 小时前
SpringBoot返回文件让前端下载的几种方式
前端·spring boot·后端
m0_748230449 小时前
创建一个Spring Boot项目
java·spring boot·后端
卿着飞翔9 小时前
Java面试题2025-Mysql
java·spring boot·后端
C++小厨神9 小时前
C#语言的学习路线
开发语言·后端·golang