ASP.NET Core MVC 中的属性路由

Author Image
admin Sunday, May 10, 2020 阅读数: 161

本文作者:梁桐铭- 微软最有价值专家(Microsoft MVP)
本文出自《从零开始学 ASP.NET Core 与 EntityFramework Core》目录
视频课程效果更佳:跨平台开发实战掌握 ASP.NET Core 与 EntityFramework Core

ASP.NET Core MVC 中的属性路由

在本章节中,我们将讨论 ASP.NET CoreMVC 中的属性路由。

请参考Startup.cs文件的Configure()方法中的以下代码。 请注意,我们使用UseMvc()方法,所以不包含默认路由模板,无法进行参数传递。

publicvoidConfigure(IApplicationBuilderapp,IHostingEnvironmentenv)
{
if(env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseStaticFiles();

app.UseMvc();

//app.UseMvc(routes=>
//{
//routes.MapRoute("default","{controller=Home}/{action=Index}/{id?}");
//});
}

这意味着,目前我们的应用程序没有配置任何的路由,当我们导航到以下任何 URL 时,我们会看到 404 错误。

http://localhost:1234
http://localhost:1234/home
http://localhost:1234/home/index

属性路由示例

使用属性路由,我们使用Route()属性来定义路由。我们可以在Controller或 Controller 的操作方法上应用Route属性。

publicclassHomeController:Controller
{

[Route("")]
[Route("Home")]
[Route("Home/Index")]
publicViewResultIndex()
{
returnView();
}
}

Index()操作方法上指定了 3 个不同的Route()属性。对于Route()属性的每个实例,我们指定了不同的路由模板。使用这 3 个路由模板,3 个 URL 路径中,都会访问 HomeController 的 Index()操作方法。

使用这 3 个路由模板,当遇到以下 3 种 url 地址进行访问HomeController的Index()操作方法的时候,都会匹配成功,进入方法内。

/
/Home
/Home/Index

属性路由参数

使用传统路由,我们可以将路由参数指定为路由模板的一部分。 当然属性路由也可以做同样的事情。 请考虑下面的例子。

  public class HomeController : Controller
    {
        private readonly IStudentRepository _studentRepository;

       //使用构造函数注入的方式注入IStudentRepository
        public HomeController(IStudentRepository studentRepository)
        {
            _studentRepository = studentRepository;
        }

        [Route("Home/Details/{id}")]
        public IActionResult Details(int id)
        {
           //实例化HomeDetailsViewModel并存储Student详细信息和PageTitle
            HomeDetailsViewModel homeDetailsViewModel = new HomeDetailsViewModel()
            {
                Student = _studentRepository.GetStudent(id),
                PageTitle = "学生详细信息"
            };

            return View(homeDetailsViewModel);
        }
    }

Details ()操作方法具有 id 参数。此参数根据指定的 id 来查询学生的详细信息 。请注意, 在路由模板中, 我们指定了 id 参数。因此, URL (/Hom/ditails1)将执行Details (int id)操作方法, 并将值"1"映射到Details(int id)的"id"参数。这是通过模型绑定来完成的。我们会在后面的课程来讨论模型绑定。

属性路由可选参数

当前,当我们在URL(/Home/Details/1)中具有"id"路由参数的值时,才执行HomeControllerDetails(int id)操作方法。 如果 id 值不在 URL 中,我们得到 404. 例如,URl/Home/Details不会执行Details(int id)操作方法。 而是显示 404 错误。 要使路由参数"id"可选,只需在末尾添加"?" 即可。

  public class HomeController : Controller
    {
        private readonly IStudentRepository _studentRepository;

       //使用构造函数注入的方式注入IStudentRepository
        public HomeController(IStudentRepository studentRepository)
        {
            _studentRepository = studentRepository;
        }

        //?使路由模板中的id参数为可选,如果要使它为必选,删除?即可
        [Route("Home/Details/{id?}")]
       //? 使id方法参数可以为空
        public IActionResult Details(int? id)
        {
           //实例化HomeDetailsViewModel并存储Student详细信息和PageTitle
            HomeDetailsViewModel homeDetailsViewModel = new HomeDetailsViewModel()
            {//如果"id"为null,则使用1,否则使用路由中传递过来的值
                Student = _studentRepository.GetStudent(id??1),
                PageTitle = "学生详细信息"
            };

            return View(homeDetailsViewModel);
        }
    }

控制器和操作方法名称

在属性路由中,控制器名称和操作方法名称,不会影响路由属性名称,他们没有强关联关系。 请看下面的示例。

public class WelcomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    public ViewResult Welcome()
    {
        return View();
    }
}

由于我们直接在操作方法上指定了路由模板,因此WelcomeController中的Welcome()操作方法对以下 3 个 URL 路径执行,都会执行。

/
/Home
/Home/Index

属性路由支持层次结构

Route()属性也可以应用于 Controller 类以及各个操作。 为了使属性路由代码重复性降低,我们可以将控制器上的路由属性与各个操作方法的路由属性相结合。 考虑下面的例子:

  public class HomeController : Controller
    {
        private readonly IStudentRepository _studentRepository;

       //使用构造函数注入的方式注入IStudentRepository
        public HomeController(IStudentRepository studentRepository)
        {
            _studentRepository = studentRepository;
        }


          [Route("")]
        [Route("Home")]
        [Route("Home/Index")]
        public IActionResult Index()        {
            IEnumerable<Student> students = _studentRepository.GetAllStudents();
            return View(students);
        }

        //?使路由模板中的id参数为可选,如果要使它为必选,删除?即可
        [Route("Home/Details/{id?}")]
       //? 使id方法参数可以为空
        public IActionResult Details(int? id)
        {
           //实例化HomeDetailsViewModel并存储Student详细信息和PageTitle
            HomeDetailsViewModel homeDetailsViewModel = new HomeDetailsViewModel()
            {//如果"id"为null,则使用1,否则使用路由中传递过来的值
                Student = _studentRepository.GetStudent(id??1),
                PageTitle = "学生详细信息"
            };

            return View(homeDetailsViewModel);
        }
    }

HomeController的Index()的操作方法是匹配以下 3 种 URL 路径

/Home
/Home/Index

HomeController的Details(int?id)的操作方法,匹配以下 2 种 URL 路径

/Home/Details
/Home/Details/2

正如你所看到的,有很多重复的路由名称。 我们对代码进行修改,精简代码,在 HomeController 类上应用Route()属性,如下所示。

[Route("Home")]
  public class HomeController : Controller
    {
        private readonly IStudentRepository _studentRepository;

       //使用构造函数注入的方式注入IStudentRepository
        public HomeController(IStudentRepository studentRepository)
        {
            _studentRepository = studentRepository;
        }


        [Route("")]
        [Route("Index")]
        public IActionResult Index()        {
            IEnumerable<Student> students = _studentRepository.GetAllStudents();
            return View(students);
        }

        //?使路由模板中的id参数为可选,如果要使它为必选,删除?即可
        [Route("Details/{id?}")]
       //? 使id方法参数可以为空
        public IActionResult Details(int? id)
        {
           //实例化HomeDetailsViewModel并存储Student详细信息和PageTitle
            HomeDetailsViewModel homeDetailsViewModel = new HomeDetailsViewModel()
            {//如果"id"为null,则使用1,否则使用路由中传递过来的值
                Student = _studentRepository.GetStudent(id??1),
                PageTitle = "学生详细信息"
            };
            return View(homeDetailsViewModel);
        }
    }

我们将应用于控制器操作方法的路由模板移动到了控制器上。但是,当我们导航到根URL(http://localhost:1234),HomeController的Index()操作方法将不会被执行,反而看到的是 404 页面错误。要解决这个问题,请在Index()操作方法中包含以"/"开头的路径模板,如下所示。

[Route("/")]
[Route("")]
[Route("Index")]
public ViewResult Index()
{
   var model = _studentRepository.GetAllStudents();
   return View(model);
}

需要记住的一个非常重要的一点是, 如果操作方法上的路由模板以/或 ~/开头, 则控制器路由模板不会与操作方法路由模板组合在一起。

属性路由中自定义路由

属性路由通过将标记括在方括号([])中来支持标记替换。 标记[controller]和[action]将替换为定义路径的控制器名称和操作名称的值。

如下代码所示:

[Route("[controller]")]
public class DepartmentsController : Controller
{
    [Route("[action]")]
    public string List()
    {
        return " DepartmentsController控制器中的List()方法";
    }

    [Route("[action]")]
    public string Details()
    {
        return " DepartmentsController控制器中的Details()方法";
    }
}

使用 controller 和 action 标记,在 URL 路径中的/Departments/List将进入DepartmentsController中执行List()方法。 类似地,在 URL 路径中的/Departments/Details将进入DepartmentsController中执行Details()方法。

这是一种非常强大的技术,因为如果我们要进行重命名控制器或动作名称,我们就不必更改路径模板。

要使 List()方法成为 DepartmentsController 的默认路由入口,您仍可以使用空字符串包含的 Route("")属性,如下所示。


[Route("[controller]")]
public class DepartmentsController : Controller
{
    [Route("[action]")]
    [Route("")]//使 List()成为默认路由入口
    public string List()
    {
        return " DepartmentsController控制器中的List()方法";
    }

    [Route("[action]")]
    public string Details()
    {
         return " DepartmentsController控制器中的Details()方法";
    }
}

我们最好在控制器上只设置一次,而不是在控制器中的每个操作方法中包含[action]标记,如下所示。

[Route("[controller]/[action]")]
public class DepartmentsController : Controller
{
    public string List()
    {
          return " DepartmentsController控制器中的List()方法";
    }

    public string Details()
    {
        return " DepartmentsController控制器中的Details()方法";
    }
}

常规路由与属性路由对比

使用属性路由时, 路由属性需要在实际使用它们的操作方法上方设置。 属性路由比传统路由提供了更大的灵活性。 通常情况下, 常规路由用于服务 HTML 页的控制器。 而属性路由则用于服务 REST API 的控制器。 当然,这个只是规范和建议,如果你的应用程序需要有更多的路由灵活性,我们也可以将常规路由与属性路由混合使用。

文章说明

如果您觉得我的文章质量还不错,欢迎打赏,也可以订阅我的视频哦
未得到授权不得擅自转载本文内容,52abp.com 保留版权

感谢您对我的支持

关注微信公众号:角落的白板报

公众号:角落的白板报