ASP.NET Core中使用自定义路由
2021-02-19 16:19
标签:color 比较 理解 required completed tar byte ignore 组件
上一篇文章《ASP.NET Core中使用默认MVC路由》提到了如何使用默认的MVC路由配置,通过这个配置,我们就可以把请求路由到Controller和Action,通常情况下我们使用默认的路由器就可以了。 但是有些情况下,我们需要创建自己的路由规则,不是简单的修改MVC路由模板这么简单,比如我们需要针对一些特定的URL做特殊处理,这种情况通常是我们需要兼容一些旧的URL,但是升级之后总不能不管吧,要们做跳转或者给用户一个友好提示等等。如果旧的URL是MVC的那种格式Controller/Action格式还好办,如果是webform格式的URL,比如xxx.aspx或者静态文件URL,比如bbb.html。这时候我们不能使用默认的MVC路由器来处理我们的请求,我们需要提供一个特定的Router,也可以为理解为路由处理器,如果请求的URL匹配得上,那么就交给一个特定的handler处理。 这里我们提到了Handler,实际上可以理解为Httphandler,没错就是它。在Webform里面,之前版本的MVC都一直存在,web请求最终都给交给一个特定的Handler处理。在Webform里面的handler是aspx的code behind文件,在MVC里面是Routehandler,之后各自实现自己的process方法。 好了不深入去看,我们接着上一篇的例子,创建自己的一个Router,这Router可以实现以下功能 1.这个Router的作用是兼容不存在的Url,对于这些不存在的Url,我们给出友好提示 2.我们还可以将某些特定URL的请求转交给MVC框架去处理,这里说的是转交,而不是直接让MVC路由去处理。 实现过程 1.在项目根目录下创建一个类,名字为LegacyRoute,实现IRouter接口,实现代码如下 上述代码实现了IRouter的两个接口,这两个接口方法的简单介绍一下 RouteAsync:这个处理请求的关键方法,有请求过来的时候,并且URL能匹配的上,系统会调用这个Router进行匹配,看能否处理这个请求,如果能处理则给出响应。 比如这个例子,Router保存了一些需要兼容的旧的Url,如果请求过来的Url包含在里面,那么直接Repsonse输出结果。 处理的方式是创建一个Handler的实例给RouteContext,这个Handler的类型是RequestDelegate,是一个委托,这个实例可以理解为一个中间件实例,参考《ASP.NET Core中Middleware的使用》里面的说明。 GetVirtualPath:这个方法是返回用户能看到的URL路径,比如在cshtml里面调用Url.Action得到的Url,会调用这个方法获取对应的URL,这个方法一会再讲讲如何实现。 2.定义好了Router之后,然后就是应用到特定的Url,路由配置当然也是在Startup里面完成。 由于我们要把自定义的Router加入到当前的Routes集合,所以之前使用的简化版UseMvcWithDefaultRoute需要改成如下方式 这里自定义的LegacyRoute要放在MVC的路由之前,否则会被MVC的路由提前拦截。 LegacyRoute的构造方法里提供了两个需要兼容的旧的Url,这时候启动项目,并且输入这两个URL会给出一段文字提示。 如果输入其他不存在的路径,那么还是返回404错误的 3 这时这个Router只是简单的修改Response内容,那如果需要返回更多的信息咋办,不能在这方寸方法里写大堆html代码吧,自己定义一套模板模式, 那几乎又是重写了一套逻辑。MVC的模板已经很强大了,那么我们完全可以把这个路由接收到请求再次转交给MVC去处理,这样就能用到高达上的Razor模板了。 我们先新建一个Controller,名字为LegacyController,增加一个简单的Action 然后在Views文件夹创建Legacy目录,在Legacy目录创建GetLegacyUrl.cshtml文件,然后给一些内容文字什么的,具体不给截图了,都很简。 创建这两个文件是为了后面操作做铺垫。 紧接着修改LegacyRoute为如下代码 主要改动是注入了IServiceProvider,这是Core里面用于查找服务的Provider,用它实现service locate功能,查找我们需要的服务组件,这是IOC的知识点,先不详述。 引入IServiceProvider主要是为了得到MvcRouteHandler这个服务组件。 设置contexnt的RouteData的数据,然后将整个context实例交给MvcRouteHandler的实例去处理,这样就顺利将某些特定旧Url的请求转交给了Mvc去处理。 LegacyRoute的构造方法改了,那么在Startup里面也要调整,要提供IServiceProvider的实例,还好通过IApplicationBuilder的实例就可以轻松获得 4 到这一步的时候,看起来已经可以处理某些特定的URL的请求。但是LegacyRoute里面还有一个方法没有实现,那就是GetVirtualPath,这是干嘛的呢,实际上就是用于生成对外显示的URL路径。 使用如下代码完善GetVirtualPath方法 这个代码是判断是否在路由参数里提供了legacyUrl参数,如果有则进一步处理,并返回一个VirtualPathData实例。 说这么多可能不太好理解,实际上这个用在cshtml里面就明白了 在页面里创建一个a标记,使用如下代码 /old/mvc3 (由于这里用到了TagHelper,所以还必须做一些处理,具体参考源代码的配置) 那么生成的的Html代码如下 /old/mvc3 看起来比较的多余,跟直接写死/old/mvc3路径一样效果,但是走的路径不同,如果修改了路由规则,那么通过asp-route-*方式设置的路径也会跟着修改。 到这里基本实现了自定义的路由,基本的思路就是这样,当然能做跟多复杂的事情,甚至定义一个完整的路由规则。 完整代码示例可以从以下路径下载 https://github.com/shenba2014/AspDotNetCoreMvcExamples/tree/master/CustomRouter ASP.NET Core中使用自定义路由 标签:color 比较 理解 required completed tar byte ignore 组件 原文地址:https://www.cnblogs.com/lonelyxmas/p/12928362.htmlpublic class LegacyRoute : IRouter
{
private readonly string[] _urls;
public LegacyRoute(params string[] urls)
{
_urls = urls;
}
public Task RouteAsync(RouteContext context)
{
var requestedUrl = context.HttpContext.Request.Path.Value.TrimEnd(‘/‘);
if (_urls.Contains(requestedUrl, StringComparer.OrdinalIgnoreCase))
{
context.Handler = async ctx => {
var response = ctx.Response;
byte[] bytes = Encoding.ASCII.GetBytes($"This URL: {requestedUrl} is not available now");
await response.Body.WriteAsync(bytes, 0, bytes.Length);
};
}
return Task.CompletedTask;
}
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
return null;
}
}
app.UseMvc(routes =>
{
routes.Routes.Add(new LegacyRoute(
"/articles/aspwinform.html",
"/old/mvc3"));
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
public class LegacyController : Controller
{
public ViewResult GetLegacyUrl(string legacyUrl)
=> View("GetLegacyUrl", legacyUrl);
}
@model string
@{ Layout = null; }
DOCTYPE html>
html>
head>
meta name="viewport" content="width=device-width" />
title>Routingtitle>
head>
body class="panel-body">
h2>GetLegacyURLh2>
This URL: @Model is not available now
body>
html>
public class LegacyRoute : IRouter
{
private readonly string[] _urls;
private readonly IRouter _mvcRoute;
public LegacyRoute(IServiceProvider services, params string[] urls)
{
_urls = urls;
_mvcRoute = services.GetRequiredService
routes.Routes.Add(new LegacyRoute(
app.ApplicationServices,
"/articles/aspwinform.html",
"/old/mvc3"));
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
if (context.Values.ContainsKey("legacyUrl"))
{
var url = context.Values["legacyUrl"] as string;
if (_urls.Contains(url))
{
return new VirtualPathData(this, url);
}
}
return null;
}