ASP.NET Core – Razor Pages Routing
前言
之前有提过, MVC 和 Razor Pages 最大的区别就在 Routing 上.
Razor Pages 的结构是 route, page, model
route match to page, page declare model (route > page > model)
route 是怎样 match to page 的呢? ASP.NET Core 有一套机制. 这篇主要就是介绍这个和如果修改它.
参考:
默认 route 匹配
创建一个 Razor Pages 项目
dotnet new webapp -o TestRazorPagesRouting
dotnet watch run
打开游览器访问
https://localhost:7194 (首页)
https://localhost:7194/Privacy (Privacy 页面)
它们对应的 Page 是
看的出来, 匹配方式是 file name, 然后 Index 可以忽略
Privary.cshtml = /Privary
Index.cshtml = /
那么 https://localhost:7194/Index 也可以访问首页面?
答案是可以的.
multiple layer
按照它的思路, 应该就是把 path 加长. 没错!
Mac/Compare.cshtml = /Mac/Compare
结论
Razor Page Routing 的机制是
先去 root folder "Pages"
找到所有带有 @page 的 .cshtml
依据 file 的所在位置 (folder) 和它的 file name 作为 URL path.
而 Index 可以被忽略 (要写也是可以)
Index.cshtml = /
Index.cshtml = /Index
Privary.cshtml = /Privary
Mac/Compare.cshtml = /Mac/Compare
默认 route 匹配的问题
这个默认匹配方式不是很直观, 它有一下问题:
1. Case Style, URL 通常是 kebab-case. 但 folder 和 file name 却是 PascalCase. 而它竟然没有做任何转换.
2. Index 不应该被访问. 有见过 /index.html 这样访问的, 但没有见过 /Index 这样访问的...
3. 整体 Structure 没有顾虑到 CSS 和 JS
下面这个才是正常网站的 folder structure
html, js, css 一定是在同一个 folder 里的, 表示一个页面. 嵌套也是一样.
虽然它默认的匹配不太理想, 但是一个好的框架不是评价它的默认, 而是它是否允许自定义.
幸好, 在自定义方面, ASP.NET Core 还是提供了接口.
Area
在讲解修改默认匹配之前, 先看看 Area (因为我没有用, 所以大概记入一下就好)
当有很多 pages 以后, 想给它们分类就可以用 Area
结构长这样
匹配的 URL 是
/Adminstration/Reports
/Production
Extend Route Match
默认匹配只能 cover 简单的场景, 真实项目中, 通常会需要 extend.
Extend Path
extend product name
花括弧是 dynamic value, 问号是可有可无的意思.
RouteData 是用来获取 dynamic value 的
它匹配
/ProductDetail
/ProductDetail/whatever
这样就可以做 dynamic page 了.
Add extra route match
builder.Services.AddRazorPages(options =>
{
options.Conventions.AddPageRoute("/ProductDetail", "/product-detail");
options.Conventions.AddPageRoute("/ProductDetail", "/product-information");
});
注: 它是 extend 不是 override 哦
它可以 match URL:
/ProductDetail
/product-detail
/product-information
有时候 URL 换了, 可以用这招来做 301 redirect.
Override Route Match
这个结构默认匹配是
/Product/iPhone
/Product/iPhone/Index
通过 @page "/YourRootPath" 就可以完全覆盖默认匹配了
修改后的匹配是 /iPhone
注: 开头是 "/" 才表示 override 哦, 不然就是 extend 了
Modify Default Route Match Pattern
虽然可以 extend 和 override, 但是底层的问题还是得通过修改底层去解决.
modify root folder
默认是 /Pages
builder.Services.AddRazorPages(options =>
{
options.RootDirectory = "/Web";
});
to kebab-case
参考: stackoverflow – Automatically generate lowercase dashed routes in ASP.NET Core
builder.Services.AddRazorPages(options =>
{
options.RootDirectory = "/Web";
options.Conventions.Add(new PageRouteTransformerConvention(new KebabCaseTransformer()));
});
KebabCaseTrasformer
public class KebabCaseTransformer : IOutboundParameterTransformer
{
public string? TransformOutbound(object? value)
{
if (value == null) return null;
if (value.ToString()! == "") return ""; return ToKebabCase(value.ToString()!); string ToKebabCase(string value)
{
return value == "AboutUs" ? "about-us" : value;
}
}
}
ASP.NET Core 在创建 route mapping 时会遍历所有的 @page .cshtml
然后依据 folder file name 做 route, 这时拦截它就可以替换掉它的机制.
Conventions 就是拦截点.
Transformer 是一个比较具体的替换方式, 如果想换的更复杂一点就要直接使用 IPageRouteModelConvention
PageRouteTransformerConvention 就是继承了 IPageRouteModelConvention.
Fully Customize
添加 Convention, Convention 是可以多个的, 类似 middleware 那样.
builder.Services.AddRazorPages(options =>
{
options.RootDirectory = "/Web";
options.Conventions.Add(new MyPageRouteModelConvention());
});
MyPageRouteModelConvention
public class MyPageRouteModelConvention : IPageRouteModelConvention
{
public void Apply(PageRouteModel model)
{
var path = model.ViewEnginePath; // /FolderName/FileName e.g. /Mac/Compare/Index
if (path.Contains("AboutUs"))
{
model.RouteParameterTransformer = new KebabCaseTransformer(); // 就是这里换 transform 的
model.Selectors.Clear(); // selectors 就是关键. 把原本的 clear 到完
model.Selectors.Add(new SelectorModel
{
AttributeRouteModel = new AttributeRouteModel
{
Template = "/about-us"
}
});
}
}
}
从 model 里面获取信息, 然后修改 Selectors (它就是最终的匹配)
我们来看看原本的 Selectors, 这样就大概知道要怎么改了
默认它生成了 2 个 Selectors
第一个是 AboutUs/Index, 第 2 个是 AboutUs
Index suppress link generation = true, 所有 link generation 不会 generate 出这样的 path.
IsAbsoluteTemplate 指的是 template 是否 start with slash "/"
我们加上 extend 和 override path 看看
EndpointMetadata 可以获取到 RouteTemplate, 如果没有就是 null, 但 EndpointMetadata 是一定会有的
总结
要想完全控制最终的 route mapping 就需要添加 Convention
通过 model.ViewEnginePath 获取到 folder 和 file name
通过 model.Selectors 获取原本的 Selectors info, 最重要的是 EndpointMetadata 可以获取到 RouteTemplate
最后通过 add/edit/delete Selectors 来达成最终的 mapping override.
我自己的做法是:
1. kebab-case
2. remove 掉 Index 访问
3. 支持多语言 /zh-hans/about-us, /jp/about-us
关于 Link Generation
tag helper 可以通过 PageName (folder file name) 自动生成 URL
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
也可以在代码中调用
当 URL 换了以后, 不需要全场找 anchor 替换. 这个出发点是好的. 但是目前的 Link Generation 不是很给力.
几年前我就有提过 issue, 但最后不了了之. 而现阶段也还有很多待加强的地方 Link generation improvements issue
所以呢, 如果你有修改很多 default route 机制的话, 不建议使用 link generation 来管理. hardcode URL 就可以了. 毕竟 URL 也不是随随便便可以换的丫, SEO 不用管吗 ?不需要 301 redirect ?
ASP.NET Core – Razor Pages Routing的更多相关文章
- ASP.NET Core Razor Pages
Razor 页面是Asp.Net Core2.0新增的一个功能.Razor 页面是 ASP.NET Core MVC 的一个新特性,它可以使基于页面的编码方式更简单高效. 环境:vs2017 .net ...
- ASP.NET Core Razor Pages 初探
最近新建 Asp.net Core MVC 项目的时候不小心选错了个模板,发现了一种新的项目模板.它使用cshtml视图模板,但是没有Controller文件夹.后来才发现这是ASP.NET Core ...
- ASP.NET Core - Razor页面之Handlers处理方法
简介 在前一篇文章中,我们讨论了Razor页面.今天我们来谈谈处理方法(Handlers). 我们知道可以将代码和模型放在 .cshtml 文件里面或与 .cshtml 匹配的 .cshtml.cs ...
- ASP.NET Core:Pages
ylbtech-ASP.NET Core:Pages 1.返回顶部 1._Layout.cshtm <!DOCTYPE html> <html> <head> &l ...
- .NET Core Razor Pages中ajax get和post的使用
ASP.NET Core Razor Pages Web项目大部分情况下使用继承与PageModel中的方法直接调用就可以(asp-page),但是有些时候需要使用ajax调用,更方便些.那么如何使用 ...
- C# -- HttpWebRequest 和 HttpWebResponse 的使用 C#编写扫雷游戏 使用IIS调试ASP.NET网站程序 WCF入门教程 ASP.Net Core开发(踩坑)指南 ASP.Net Core Razor+AdminLTE 小试牛刀 webservice创建、部署和调用 .net接收post请求并把数据转为字典格式
C# -- HttpWebRequest 和 HttpWebResponse 的使用 C# -- HttpWebRequest 和 HttpWebResponse 的使用 结合使用HttpWebReq ...
- ASP.NET Core - Razor 页面简介
简介 随着ASP.NET Core 2 即将来临,最热门的新事物是Razor页面.在之前的一篇文章中,我们简要介绍了ASP.NET Core Razor 页面. Razor页面是ASP.NET Cor ...
- ASP.NET Core Razor中处理Ajax请求
如何ASP.NET Core Razor中处理Ajax请求 在ASP.NET Core Razor(以下简称Razor)刚出来的时候,看了一下官方的文档,一直没怎么用过.今天闲来无事,准备用Rozor ...
- 学习ASP.NET Core Razor 编程系列一
一. 概述 .NET Core 1.0发布的时候就想进行学习的,不过根据微软的以往的发布规律1.0版可以认为是大众测试版,2.0才算稳定.现在2.1都已经发布了预览版,之前对其"不稳定&qu ...
- 学习ASP.NET Core Razor 编程系列四——Asp.Net Core Razor列表模板页面
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
随机推荐
- Unity无法安装Entities 1.2.0 Package的解决方法
会出现如下的错误提示: 本质原因是国内版的Unity使用了自己的Package加速CDN:packages.unity.cn,而不是官方的packages.unity.com.而这个CDN更新了Ent ...
- Vue查询传参
通过修改 getWK005 函数来实现这一点.这里的 query 参数就是发送 GET 请求时的查询参数.你可以将需要的条件作为 query 对象的属性传递进去.比如,如果你想要按照特定的条件查询信息 ...
- 重写Save()的两种方法
# 重新Save()def save(self, force_insert=False, force_update=False, using=None, update_fields=None): su ...
- Tomcat 线程池学习总结
前提 Tomcat 10.1.x Tomcat线程池介绍 Tomcat线程池,源于JAVA JDK自带线程池.由于JAVA JDK线程池策略,比较适合处理 CPU 密集型任务,但是对于 I/O 密集型 ...
- php 选择驱动写法
在 ThinkPHP 5.1 中,若要根据配置文件 sms.conf 中的设置在不同的短信渠道之间进行切换,可以采用以下步骤: 第一步:定义接口 首先,创建一个接口,这个接口将由所有短信渠道类实现.这 ...
- 写写Redis十大类型GEO(地理空间)的常用命令
又是一个用于处理sql性能的类型,这个类型的核心思想就是把三维的地球变成二维的坐标,再将二维的坐标换成一维的点块,最后将一维的点块转化成base32编码 类型就是zset, 在shell的启动命令后加 ...
- c语言模拟Python的命名参数
最近在书里看到的,让c语言去模拟其他语言里有的命名函数参数.觉得比较有意思所以记录一下. 目标 众所周知c语言里是没有命名函数参数这种东西的,形式参数虽然有自己的名字,但传递的时候并不能通过这个名字来 ...
- 通过程序名称kill掉所有的进程
通过程序名称kill掉所有的进程 今天差点把服务器搞崩了. 脚本的循环条件有问题,结果起了无数的nslookup,用pkill杀不掉,只能用kill一个个解决,服务器还被搞得慢得要命. 还好又黄队长, ...
- 【JS】06 语法补充
严格模式(use strict) 其实就是就是对JS随意的语法做一个强制规范要求 开启严格模式: "use strict"; 注意,只有在第一行声明才会有效,. 否则在严格模式之前 ...
- 【VMware】虚拟机 VMware WorkStation Pro 下载安装(Windows)
官网地址: 下载地址:[VMware WorkStation Pro 15.5 For Windows] https://www.vmware.com/cn/products/workstation- ...