ASP.NET Core MVC的路由参数中:exists后缀有什么作用

我们在ASP.NET Core MVC中如果要启用Area功能,那么会看到在Startup类的Configure方法中是这么定义Area的路由的:

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "subAreaRoute",
        template: "{area:exists}/{subarea:exists}/{controller=Home}/{action=Index}/{id?}");
    routes.MapRoute(
        name: "areaRoute",
        template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});

我们可以看到其中{area:exists}和{subarea:exists}这两个路由参数后面都有个:exists后缀,那么这是用来干什么的呢?

来举个例子就明白了:

如果现在ASP.NET Core MVC项目中有个HomeController,并且HomeController下有名为Index的Action,如下所示:

HomeController:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

namespace WebCore.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
    }
}

并且Index这个cshtml文件的View也存在:

然后项目中没有定义任何Area文件夹

现在我们访问如下Url:

http://localhost:49908/Home/Index

该Url可以成功显示Index视图,这是因为该Url成功配匹到了ASP.NET Core MVC路由"default",也就是"{controller=Home}/{action=Index}/{id?}"

但是如果我们现在将ASP.NET Core MVC的路由配置改成下面这样,去掉"subAreaRoute"路由的{area}和{subarea}路由参数的:exists后缀:

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "subAreaRoute",
        template: "{area}/{subarea}/{controller=Home}/{action=Index}/{id?}");
    routes.MapRoute(
        name: "areaRoute",
        template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});

我们再访问Url:

http://localhost:49908/Home/Index

结果如下:

很明显路由解析失败了,ASP.NET Core MVC抛出了异常,可这是为什么呢?我们的项目中有HomeController也有Index这个Action,也有Index.cshtml视图,为什么Url地址http://localhost:49908/Home/Index没有成功匹配到呢?

这是因为当我们去掉了ASP.NET Core MVC路由"subAreaRoute"的路由参数:exists后缀后,Url地址http://localhost:49908/Home/Index匹配到的是"subAreaRoute"路由”{area}/{subarea}/{controller=Home}/{action=Index}/{id?}“,并不是"default"路由"{controller=Home}/{action=Index}/{id?}",然而如果根据”subAreaRoute“路由去解析Url地址http://localhost:49908/Home/Index,那么需要项目中存在一个叫Home的Area文件夹,其下面还要存在一个叫Index的SubArea文件夹,然后Index文件下还应该有一个HomeController文件,然后HomeController里面因该有一个叫Index的Action方法,然而我们的项目中并没有叫Home的Area,也没有叫Index的SubArea,所以虽然Url地址http://localhost:49908/Home/Index匹配"subAreaRoute"路由成功了,但是由于找不到Area文件夹所以最后ASP.NET Core MVC报错了,其错误描述页也显示ASP.NET Core MVC试图寻找Area文件夹的地址失败了:

那么问题是为什么现在Url地址http://localhost:49908/Home/Index会匹配"subAreaRoute"路由"{area}/{subarea}/{controller=Home}/{action=Index}/{id?}"成功了呢?

我们来分析下Url地址http://localhost:49908/Home/Index在匹配 "subAreaRoute"路由时,首先Home匹配路由参数{area},Index匹配路由参数{subarea},然后由于{controller=Home}和{action=Index}有默认参数值,所以参数{controller}就是Home,参数{action}就是Index,然后{id?}是可选参数,所以Url地址中没有对应项也无所谓,所以如此看来"subAreaRoute"路由中的所有路由参数都能从Url地址http://localhost:49908/Home/Index中找到对应项目,那么匹配当然成功!由于Url地址http://localhost:49908/Home/Index匹配"subAreaRoute"路由成功了,所以就不会在匹配"default"路由"{controller=Home}/{action=Index}/{id?}"了,开始进入寻找文件路径的环节,结果寻找文件路径失败,页面报错。。。

所以我们可以看到当去掉"subAreaRoute"路由”{area:exists}/{subarea:exists}/{controller=Home}/{action=Index}/{id?}“的:exists后缀后,相当于ASP.NET Core MVC不会关心匹配到的{area}和{subarea}路由参数是否在项目中真的有对应的Area文件夹,只要Url地址http://localhost:49908/Home/Index能提供"subAreaRoute"路由的所有路由参数值,那么路由匹配就算成功。而:exists后缀可以保证不仅路由参数能从Url地址匹配到值,还要确保路由参数值在项目中能找到真正的文件夹或文件,整个路由才算匹配成功。

我们可以再将"subAreaRoute"路由改为”{area:exists}/{subarea}/{controller=Home}/{action=Index}/{id?}“只给{area}参数加上:exists后缀:

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "subAreaRoute",
        template: "{area:exists}/{subarea}/{controller=Home}/{action=Index}/{id?}");
    routes.MapRoute(
        name: "areaRoute",
        template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});

然后在浏览器还是输入Url地址:

http://localhost:49908/Home/Index

结果如下:

页面成功显示,因为现在Url地址http://localhost:49908/Home/Index匹配到的又是"default"路由"{controller=Home}/{action=Index}/{id?}"了。"default"路由"{controller=Home}/{action=Index}/{id?}"是可以找到对应的HomeController及Index这个Action和Index.cshtml视图的,所以页面显示成功。因为现在"subAreaRoute"路由”{area:exists}/{subarea}/{controller=Home}/{action=Index}/{id?}“要求不光要从Url地址http://localhost:49908/Home/Index中匹配到{area}路由参数的值,由于{area}路由参数定义了:exists后缀,还要去检查项目中是否真的有名为Home的Area文件夹,结果找不到Area文件夹,所以匹配路由"subAreaRoute"失败,继续匹配路由表的"areaRoute"路由 "{area:exists}/{controller=Home}/{action=Index}/{id?}",同理还是匹配失败,再匹配路由表的最后一个"default"路由"{controller=Home}/{action=Index}/{id?}",这时匹配"default"路由成功,成功显示页面到浏览器。

所以:exists后缀在ASP.NET Core MVC路由中有验证文件夹和文件是否存在的功能,只有当路由参数值对应的文件夹和文件在项目中确实存在时,路由匹配才算成功,否者路由匹配失败,会继续用Url去匹配 ASP.NET Core MVC路由表中的后续路由。

猜你喜欢

转载自www.cnblogs.com/OpenCoder/p/9776003.html