目录一、概述二、路由原理1、注册路由2、路由匹配2.1、匹配方式一2.2、匹配方式二2.3、匹配方式三3、URL参数默认值3.1、参数默认值一3.2、参数默认值二3.4、参数默认值三
在asp.net mvc架构中,控制器在3大核心构件中处于中心地位,通过控制器支配模型和视图,然而从浏览器发出的请求到控制器还需要路由的协助,路由将特定的请求和控制器的动作对应起来。
在ASP.net MVC程序中,路由主要有两方面的职责:
先看Global.asax中的代码:
网站启动的时候执行Application_Start方法,通过RouteConfig.ReGISterRoutes(RouteTable.Routes)这段代码进行路由的注册,在RegisterRoutes上面F12转到定义,可以查看该方法,其实就是App_Start文件夹下面的RouteConfig类,该类定义如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.WEB;
using System.Web.Mvc;
using System.Web.Routing;
namespace ASPNETMVCDemo
{
/// <summary>
/// RouteCollection 所有路由的集合
/// </summary>
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
// 表示忽略路由 只要URL满足这个正则规则,就不使用路由
// .axd 是因为iis6给没有后缀的请求加个asd,然后支持MVC的请求
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// 默认路由规则
// 里面使用的是命名参数,顺序可以改变
routes.MapRoute(
// 路由名称 RouteCollection是个字典 每个路由都要有自己的名称,相同名称的路由会被覆盖
name: "Default",
// url的正则规则,去掉域名和端口后的地址进行匹配
url: "{controller}/{action}/{id}",
// 默认值
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
}
注册路由其实就是把路由规则添加到RouteCollection路由集合中。
解释说明:
启动程序,这时URL地址是:Http://localhost:49730
其实该地址相当于在浏览器里面输入:http://localhost:49730/Home/Index,这时因为在路由规则里面设置了默认值,如果没有控制器和action方法,则默认是Home控制器下面的Index方法。
修改URL里面的controller和action,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace ASPNETMVCDemo
{
/// <summary>
/// RouteCollection 所有路由的集合
/// </summary>
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
// 表示忽略路由 只要URL满足这个正则规则,就不使用路由
// .axd 是因为iis6给没有后缀的请求加个asd,然后支持MVC的请求
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// 默认路由规则
// 里面使用的是命名参数,顺序可以改变
routes.MapRoute(
// 路由名称 RouteCollection是个字典 每个路由都要有自己的名称,相同名称的路由会被覆盖
name: "Default",
// url的正则规则,去掉域名和端口后的地址进行匹配
url: "{controller1}/{action1}/{id}",
// 默认值
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
}
然后运行程序查看结果:
这时程序运行出错,所以说controller和action千万不能写错。把controller和action改回正确的,然后颠倒controller和action的位置,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace ASPNETMVCDemo
{
/// <summary>
/// RouteCollection 所有路由的集合
/// </summary>
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
// 表示忽略路由 只要URL满足这个正则规则,就不使用路由
// .axd 是因为iis6给没有后缀的请求加个asd,然后支持MVC的请求
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// 默认路由规则
// 里面使用的是命名参数,顺序可以改变
routes.MapRoute(
// 路由名称 RouteCollection是个字典 每个路由都要有自己的名称,相同名称的路由会被覆盖
name: "Default",
// url的正则规则,去掉域名和端口后的地址进行匹配
url: "{action}/{controller}/{id}",
// 默认值
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
}
在运行程序查看结果:
这说明:controller和action的位置是可以颠倒的。这时如果想输入完整的URL地址就行访问就应该输入:http://localhost:49730/Index/Home。
看下面的截图:
解释说明:
看下面的示例:
首先修改Home控制器里面的Index方法,修改后的代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace ASPNETMVCRoute.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
string paras = "";
// 遍历获取参数值
foreach(KeyValuePair<string,object> keyValue in RouteData.Values)
{
paras += string.FORMat($"{keyValue.Key}={keyValue.Value} ");
}
// 通过ViewData向页面传值
ViewData["msg"] = paras;
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
修改对应的视图,在界面展示ViewData["msg"]的值:
@{
ViewBag.Title = "Home Page";
}
<div class="jumbotron">
<h1>ASP.NET</h1>
<p class="lead">ASP.NET is a free web framework for building great Web sites and Web applications using html, CSS and javascript.</p>
<p><a href="https://asp.net" rel="external nofollow" rel="external nofollow" class="btn btn-primary btn-lg">Learn more »</a></p>
<!--展示ViewData["msg"]的值-->
<p style="color:red;">@ViewData["msg"]</p>
</div>
<div class="row">
<div class="col-md-4">
<h2>Getting started</h2>
<p>
ASP.NET MVC gives you a powerful, patterns-based way to build dynamic websites that
enables a clean separation of concerns and gives you full control over markup
for enjoyable, agile development.
</p>
<p><a class="btn btn-default" href="https://Go.microsoft.com/fwlink/?LinkId=301865" rel="external nofollow" rel="external nofollow" >Learn more »</a></p>
</div>
<div class="col-md-4">
<h2>Get more libraries</h2>
<p>NuGet is a free Visual Studio extension that makes it easy to add, remove, and update libraries and tools in Visual Studio projects.</p>
<p><a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkId=301866" rel="external nofollow" rel="external nofollow" >Learn more »</a></p>
</div>
<div class="col-md-4">
<h2>Web Hosting</h2>
<p>You can easily find a web hosting company that offers the right mix of features and price for your applications.</p>
<p><a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkId=301867" rel="external nofollow" rel="external nofollow" >Learn more »</a></p>
</div>
</div>
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
运行程序查看结果:
路由规则如下代码所示:
routes.MapRoute(
name: "const",
url: "admin/{controller}/{action}"
);
运行程序,在浏览器里面输入:http://localhost:64957/admin/home/index,运行结果如下:
路由规则如下:
routes.MapRoute(
name: "blx",
// 错误的URL
// url: "{language}{country}/{controller}/{action}"
url: "{language}-{country}/{controller}/{action}"
);
运行程序,URL里面输入:http://localhost:64957/chinese-china/home/index,运行结果如下:
看下面截图:
解释说明:
看下面的路由规则:
routes.MapRoute(
name: "RouteRule",
url: "{controller}/{action}/{query-name}/{*plus}"
);
运行程序,在URL里面输入:http://localhost:64957/home/index/123/wqer_1234,运行结果如下:
看下面截图:
解释说明:
routes.MapRoute(
name: "tlppone",
url: "{controller}/{action}/{filename}.{ext}"
);
运行程序,在URL里面输入:http://localhost:64957/home/index/2342.234.234.aspx,运行结果如下:
routes.MapRoute(
name: "tlpptwo",
url: "{controller}/{action}/{foo}xyz{bar}"
);
运行程序,在URL里面输入:http://localhost:64957/home/index/xyzxyzxyzwer23,运行结果如下:
完整路由规则代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace ASPNETMVCRoute
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// 使用字面量做精确匹配
// http://localhost:64957/admin/home/index
routes.MapRoute(
name: "const",
url: "admin/{controller}/{action}"
);
// 不允许连续的URL参数
// http://localhost:64957/chinese-china/home/index
routes.MapRoute(
name: "blx",
// 错误的URL
// url: "{language}{country}/{controller}/{action}"
url: "{language}-{country}/{controller}/{action}"
);
// 使用*号匹配URL剩余部分
// http://localhost:64957/home/index/2342.234.234.aspx 与第一个和第二个路由规则都匹配,显示第一个,说明路由匹配的顺序是从上往下
// http://localhost:64957/home/index/123/wqer_1234
routes.MapRoute(
name: "RouteRule",
url: "{controller}/{action}/{query-name}/{*plus}"
);
// 贪婪匹配
// http://localhost:64957/home/index/2342.234.234.aspx
routes.MapRoute(
name: "tlppone",
url: "{controller}/{action}/{filename}.{ext}"
);
// 贪婪匹配2
// http://localhost:64957/home/index/xyzxyzxyzwer23
routes.MapRoute(
name: "tlpptwo",
url: "{controller}/{action}/{foo}xyz{bar}"
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
}
看下面的截图:
解释说明:
路由规则代码如下:
// 参数默认值一
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
看下面截图:
解释说明:
路由规则如下:
// 参数默认值二:默认值不提供的必须通过URL提供
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { action = "Index", id = UrlParameter.Optional });
这时直接运行程序,因为没有提供controller,所以程序会报错:
然后在URL里面输入:http://localhost:55650/Home,就可以正常访问了:
解释说明:
路由规则如下:
// 参数默认值三:只提供中间参数的默认值是不起作用的
routes.MapRoute(
name: "Default2",
url: "{controller}/{action}/{id}",
defaults: new { action = "Index" }
);
这时候需要在浏览器里面输入完整的URL才能访问。
看下面截图:
解释说明:
路由规则如下:
// 参数默认值四:只提供中间参数的默认值是不起作用的
routes.MapRoute(
name: "Default2",
url: "{controller}-{action}",
defaults: new { action = "Index" });
运行程序,如果浏览器里面只输入http://localhost:55650/home-是报错的:
这时需要输入完整的URL地址:http://localhost:55650/home-index
假如我们想对URL后面的数字进行限制,比如四位数字或者两位数字,如下图所示,该如何限制呢?
如下图所示:
解释说明:
路由规则如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace MVCURLParaConstraint
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// 1、使用正则表达式设置参数值的约束
routes.MapRoute(
name: "Default1",
url: "{controller}/{action}/{id}/{year}/{month}/{day}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
// year是4位整数 month和day是2位整数
constraints:new { year = @"\d{4}",month=@"\d{2}",day=@"\d{2}"}
);
// 默认路由
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
}
运行程序,在浏览器里面输入:http://localhost:51244/Home/Index/1/2019/05/24,效果如下:
因为设置了约束month是2位整数,这时将month改为1位整数,然后输入:http://localhost:51244/Home/Index/1/2019/5/24,效果如下:
这时程序就报错了,说明设置的正则约束起作用了。
如下图所示:
解释说明:
路由规则如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace MVCURLParaConstraint
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// 2、使用约束类来约束参数值
routes.MapRoute(
name: "Default1",
url: "{controller}/{action}/{id}/{year}/{month}/{day}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
// 使用约束类的实例
constraints: new { year = @"\d{4}", month =new MonthConstraint(), day = @"\d{2}" }
);
// 1、使用正则表达式设置参数值的约束
//routes.MapRoute(
// name: "Default1",
// url: "{controller}/{action}/{id}/{year}/{month}/{day}",
// defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
// // year是4位整数 month和day是2位整数
// constraints:new { year = @"\d{4}",month=@"\d{2}",day=@"\d{2}"}
// );
// 默认路由
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
/// <summary>
/// 对月进行约束,实现IRouteConstraint接口
/// 对年或者日进行约束同理
/// </summary>
public class MonthConstraint : IRouteConstraint
{
// 实现Match方法
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
// values["month"]表示根据键名或者值
// 长度为2,并且在1到12之间表示匹配通过返回true,否则不通过返回false
if (values["month"].ToString().Length==2 &&
Convert.ToInt32(values["month"])>=1 &&
Convert.ToInt32(values["month"]) <=12)
{
return true;
}
else
{
return false;
}
}
}
}
运行程序,在浏览器里面输入:http://localhost:51244/Home/Index/2/2019/05/24,效果如下:
如下图所示:
解释说明:
解释说明:
我们注意到RigisterRoutes方法中默认的第一句代码是routes.IgnoreRoute方法,他的作用就是排除路由,比如上图下面的URL不会在路由表中匹配的,而是直接被忽略掉!
先看下面的截图:
解释说明:
上面说的UrlRoutingModule在哪里呢?看下面截图:
从控制器中获取URL中的值共有三种方式:
解释说明:
RouteDate.Values[“id”]就是当前字典上的路由数据,通过访问键名的方式得到键值,比如URL模式匹配的字符串是id,这里必须是id。
Action方法的参数和路由字典的参数是对应的,MVC框架在执行action之前会首先为这些参数赋值。
控制器代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MVCURlValue.Controllers
{
public class HomeController : Controller
{
/// <summary>
/// Request.QueryString
/// </summary>
/// <returns></returns>
public ActionResult Index()
{
if(Request.QueryString["bookid"]!=null)
{
string value = Request.QueryString["bookid"];
ViewData["msg"] = value;
}
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
视图也代码如下:
@{
ViewBag.Title = "Home Page";
}
<div class="jumbotron">
<h1>ASP.NET</h1>
<p class="lead">ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS and JavaScript.</p>
<p><a href="https://asp.net" rel="external nofollow" rel="external nofollow" class="btn btn-primary btn-lg">Learn more »</a></p>
<p style="color:red;font-weight:bold;">@ViewData["msg"]</p>
</div>
<div class="row">
<div class="col-md-4">
<h2>Getting started</h2>
<p>
ASP.NET MVC gives you a powerful, patterns-based way to build dynamic websites that
enables a clean separation of concerns and gives you full control over markup
for enjoyable, agile development.
</p>
<p><a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkId=301865" rel="external nofollow" rel="external nofollow" >Learn more »</a></p>
</div>
<div class="col-md-4">
<h2>Get more libraries</h2>
<p>NuGet is a free Visual Studio extension that makes it easy to add, remove, and update libraries and tools in Visual Studio projects.</p>
<p><a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkId=301866" rel="external nofollow" rel="external nofollow" >Learn more »</a></p>
</div>
<div class="col-md-4">
<h2>Web Hosting</h2>
<p>You can easily find a web hosting company that offers the right mix of features and price for your applications.</p>
<p><a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkId=301867" rel="external nofollow" rel="external nofollow" >Learn more »</a></p>
</div>
</div>
程序运行效果:
控制器代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MVCURlValue.Controllers
{
public class HomeController : Controller
{
/// <summary>
/// Request.QueryString
/// </summary>
/// <returns></returns>
//public ActionResult Index()
//{
// if(Request.QueryString["bookid"]!=null)
// {
// string value = Request.QueryString["bookid"];
// ViewData["msg"] = value;
// }
// return View();
//}
/// <summary>
/// RouteData.Values
/// </summary>
/// <returns></returns>
public ActionResult Index()
{
string value = $"controller={RouteData.Values["controller"]},action={RouteData.Values["action"]}";
ViewData["msg"] = value;
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
运行效果:
这时会遇到一个问题,如果在URL里面还有其他参数呢?比如下面的情况
这时候如果要获取id的值就要修改代码,那么有没有其他简便的方式呢?看下面代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MVCURlValue.Controllers
{
public class HomeController : Controller
{
/// <summary>
/// Request.QueryString
/// </summary>
/// <returns></returns>
//public ActionResult Index()
//{
// if(Request.QueryString["bookid"]!=null)
// {
// string value = Request.QueryString["bookid"];
// ViewData["msg"] = value;
// }
// return View();
//}
/// <summary>
/// RouteData.Values
/// </summary>
/// <returns></returns>
public ActionResult Index()
{
// 方式一
//string value = $"controller={RouteData.Values["controller"]},action={RouteData.Values["action"]}";
// 方式二
string value = "";
foreach(var item in RouteData.Values)
{
value += $"{item.Key}={item.Value} ";
}
ViewData["msg"] = value;
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
运行效果:
因为RouteData.Values是一个字典集合,所以可以遍历RouteData.Values,这样无论URL里面有多少参数,都可以获取到对应的value值。
控制器代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MVCURlValue.Controllers
{
public class HomeController : Controller
{
/// <summary>
/// Request.QueryString
/// </summary>
/// <returns></returns>
//public ActionResult Index()
//{
// if(Request.QueryString["bookid"]!=null)
// {
// string value = Request.QueryString["bookid"];
// ViewData["msg"] = value;
// }
// return View();
//}
/// <summary>
/// RouteData.Values
/// </summary>
/// <returns></returns>
//public ActionResult Index()
//{
// // 方式一
// //string value = $"controller={RouteData.Values["controller"]},action={RouteData.Values["action"]}";
// // 方式二
// string value = "";
// foreach(var item in RouteData.Values)
// {
// value += $"{item.Key}={item.Value} ";
// }
// ViewData["msg"] = value;
// return View();
//}
/// <summary>
/// action参数
/// 方法参数名称必须和URL里面参数名称保持一致
/// </summary>
/// <returns></returns>
public ActionResult Index(string controller,string action)
{
string value = $"controller={controller},action={action}";
ViewData["msg"] = value;
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
运行效果:
解释说明:
由路由生成URL其实就是简单的两句代码,第一步就是使用RouteCollection对象的GetVirtualPath方法,通过该方法可以返回一个VirtualPahData对象,该类型表示有关路由和虚拟路径的信息,包含了两个重载的方法,区别是第二个方法可以指定路由项的名称,就是添加路由时,设置的路由键名,参数Values表达生成URL时设置的参数值,是一个字典类型。
得到VirtualPahData实例后,就可以通过他的VirtualPath属性值得到URL地址,它是一个字符串类型,从路由的这个功能可以看出,他是有双向功能的,不同于URL重写!
看下面的案例:
修改RouteConfig代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace MVCRouteToUrl
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default2",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", id = UrlParameter.Optional }
);
// 默认路由匹配规则
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
}
控制器代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace MVCRouteToUrl.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
// 1.不使用路由名称的方式生成URL
VirtualPathData vp= RouteTable.Routes.GetVirtualPath(null, new RouteValueDictionary(new { controller = "Home", action = "Index2", id = 4 }));
string url = vp.VirtualPath;
ViewData["msg"] = url;
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
界面运行效果:
在看下面一种方式:
控制器代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace MVCRouteToUrl.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
// 1.不使用路由名称的方式生成URL
//VirtualPathData vp= RouteTable.Routes.GetVirtualPath(null, new RouteValueDictionary(new { controller = "Home", action = "Index2", id = 4 }));
//string url = vp.VirtualPath;
//ViewData["msg"] = url;
// 2.根据路由名称生成URL
// 因为controller和id有默认值,所以这里只指定action的值
VirtualPathData vp = RouteTable.Routes.GetVirtualPath(null, "Default2", new RouteValueDictionary(new { action = "Index5" }));
string url = vp.VirtualPath;
ViewData["msg"] = url;
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
运行效果:
--结束END--
本文标题: ASP.NET MVC中的路由原理与用法
本文链接: https://www.lsjlt.com/news/143094.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2023-05-21
2023-05-21
2023-05-21
2023-05-21
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0