添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

ASP.NET 中 Url 大小写
不严格来讲,ASP.NET MVC 对 Url 是不敏感的,以下 Url 都是相同的,都可以访问到 PersonController 的 Query 方法:
~/Persons/Query ~/PERSONS/QUERY ~/persons/query
但对 MVC 的数据绑定来说,大小写似乎还是有区别的:
~/Persons/Query?Name=Bob ~/Persons/Query?Name=bob
对以上两个 Url,Query 中 name 参数会接收到两个不同值:Bob、bob。Action 中的参数只是原样接收,并没有作任何处理。至于name 字符串的大小写是否敏感要看具体的应用了。
再回头看前面的三个 Url:
~/Persons/Query: 是 MVC 中默认生成的,因为在 .Net 中方法命名通常采用 PascalCase; ~/PERSONS/QUERY: 全部大写,这种写法很不友好,很难读,应该杜绝采用这种方式; ~/persons/query:这种方式比较好,易读,也是大多数人选择的方式。
本文探讨如何在 MVC 中使用第三种方式,也就是小写(但不完全小写),目标如下:
在不影响程序正常运行的前提下,将所有能小写的都小写,如:
~/persons/query?name=Bob&age=18
~/persons/query/name/Bob/age/18
MVC 中 Url 的生成
在 View 中生成超级链接有多种方式:
<%: Html.ActionLink("人员查询", "Query", "Persons", new { name = "Bob" }, null) %>
<%: Html.RouteLink("人员查询", new { controller = "Persons", action = "Query", name = "Bob" })%>
<a href="<%:Url.Action("Query", "Persons", new { name="Bob" }) %>">人员查询</a>
在 Action 中,可以使用 RedirectTo 来调转至新的页面:
return RedirectToAction("Query", "Persons", new { name = "Bob" });
return RedirectToRoute(new { controller = "Persons", action = "Query", name = "Bob" });
ActionLink、RouteLink、RedirectToAction 和 RedirectToRouter 都会生成 Url,并最终显示在浏览器的地址栏中。
这四个方法都有很多重载,想从这里下手控制 Url 小写实在是太麻烦了。当然也不可行,因为可能还有其它方式来生成 Url。
MVC 是一个非常优秀的框架,但凡优秀的框架都会遵循 DRY(Don't repeat yourself) 原则,MVC 也不例外。MVC 中 RouteBase 负责 Url 的解析和生成:
public abstract class RouteBase
public abstract RouteData GetRouteData(HttpContextBase httpContext);
public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
GetRouteData 用来解析 Url,GetVirtualPath 用来生成 Url。ActionLink、RouteLink、RedirectToAction 和 RedirectToRouter 内部都会调用 GetVirtualPath 方法来生成 Url。
因此我们的入手点就是 GetVirtualPath 方法。
自定义 Route 以生成小写的 Url
MVC 中 RouteBase 的具体实现类是 Route,我们经常在 Global.asax 中经常使用:
public class MvcApplication : System.Web.HttpApplication
public static void RegisterRoutes(RouteCollection routes)
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
//...
MapRoute 返回 Route,MapRoute 有很多重载,用来简化我们构建 Route 的过程。
Route 类没有给我们提供可直接扩展的地方,因此我们只能自定义一个新的 Route 来实现我们的小写 Url。但处理路由的细节也是相当麻烦的,因此我们最简单的方式就是写一个继承自 Route 的类,然后重写它的 GetVirtualPath 方法:
public class LowerCaseUrlRoute : Route
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
//在此处进行小写处理
return base.GetVirtualPath(requestContext, values);
再来看下我们的目标:
~/persons/query?name=Bob&age=18
~/persons/query/name/Bob/age/18
其实我们只需要进行两步操作:
将路由中的 area、controller、action 的值都变成小写; 将路由中其它键值对的键变成小写,如:Name=Bob 中的 Name。
那我们先来完成这个功能吧:
private static readonly string[] requiredKeys = new [] { "area", "controller", "action" };
private void LowerRouteValues(RouteValueDictionary values)
foreach (var key in requiredKeys)
if (values.ContainsKey(key) == false) continue;
var value = values[key];
if (value == null) continue;
var valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
if (valueString == null) continue;
values[key] = valueString.ToLower();
var otherKyes = values.Keys
.Except(requiredKeys, StringComparer.InvariantCultureIgnoreCase)
.ToArray();
foreach (var key in otherKyes)
var value = values[key];
values.Remove(key);
values.Add(key.ToLower(), value);
GetVirtualPath 生成 Url 时,会将 requestContext.RouteData.Values、values(第二个参数) 以及 Defaults(当前 Router 的默认值)三个 RouteValueDictionary 进行合并,如在 View 写了如下的一个 ActionLinks:
<%: Html.ActionLink("查看") %>
生成的 Html 代码可能是:
<a href="/Home/Details">查看</a>
因为没有指定 Controller,MVC 会自动使用当前的,即从 requestContext.RouteData.Values 中获取 Controller,得到 ”Home“;”Details“来自 values;如果连 ActionLink 中 Action 也不指定,那将会从 Defaults 中取值。
因此我们必须将这三个 RouteValueDictionary 都进行处理才能达到我们的目标:
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
LowerRouteValues(requestContext.RouteData.Values);
LowerRouteValues(values);
LowerRouteValues(Defaults);
return base.GetVirtualPath(requestContext, values);
再加上几个构造函数,完整的 LowerCaseUrlRoute 如下:
public class LowerCaseUrlRoute : Route
private static readonly string[] requiredKeys = new [] { "area", "controller", "action" };
public LowerCaseUrlRoute(string url, IRouteHandler routeHandler)
: base(url, routeHandler) { }
public LowerCaseUrlRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
: base(url, defaults, routeHandler){ }
public LowerCaseUrlRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
: base(url, defaults, constraints, routeHandler) { }
public LowerCaseUrlRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
: base(url, defaults, constraints, dataTokens, routeHandler) { }
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
LowerRouteValues(requestContext.RouteData.Values);
LowerRouteValues(values);
LowerRouteValues(Defaults);
return base.GetVirtualPath(requestContext, values);
private void LowerRouteValues(RouteValueDictionary values)
foreach (var key in requiredKeys)
if (values.ContainsKey(key) == false) continue;
var value = values[key];
if (value == null) continue;
var valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
if (valueString == null) continue;
values[key] = valueString.ToLower();
var otherKyes = values.Keys
.Except(requiredKeys, StringComparer.InvariantCultureIgnoreCase)
.ToArray();
foreach (var key in otherKyes)
var value = values[key];
values.Remove(key);
values.Add(key.ToLower(), value);
有了 LowerCaseUrlRoute,我们就可以修改 Global.asax 文件中的路由了。
创建 LowerCaseUrlRouteMapHelper
这一步不是必须的,但有了这个 MapHelper 我们在修改 Global.asax 文件中的路由时可以非常方便:
public static void RegisterRoutes(RouteCollection routes)
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapLowerCaseUrlRoute( //routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
尤其是已经配置了很多路由的情况下,其代码如下:
public static class LowerCaseUrlRouteMapHelper
public static LowerCaseUrlRoute MapLowerCaseUrlRoute(this RouteCollection routes, string name, string url){
return routes.MapLowerCaseUrlRoute(name, url, null, null);
public static LowerCaseUrlRoute MapLowerCaseUrlRoute(this RouteCollection routes, string name, string url, object defaults){
return routes.MapLowerCaseUrlRoute(name, url, defaults, null);
public static LowerCaseUrlRoute MapLowerCaseUrlRoute(this RouteCollection routes, string name, string url, string[] namespaces){
return routes.MapLowerCaseUrlRoute(name, url, null, null, namespaces);
public static LowerCaseUrlRoute MapLowerCaseUrlRoute(this RouteCollection routes, string name, string url, object defaults, object constraints){
return routes.MapLowerCaseUrlRoute(name, url, defaults, constraints, null);
public static LowerCaseUrlRoute MapLowerCaseUrlRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces){
return routes.MapLowerCaseUrlRoute(name, url, defaults, null, namespaces);
public static LowerCaseUrlRoute MapLowerCaseUrlRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces){
if (routes == null) throw new ArgumentNullException("routes");
if (url == null) throw new ArgumentNullException("url");
LowerCaseUrlRoute route2 = new LowerCaseUrlRoute(url, new MvcRouteHandler());
route2.Defaults = new RouteValueDictionary(defaults);
route2.Constraints = new RouteValueDictionary(constraints);
route2.DataTokens = new RouteValueDictionary();
LowerCaseUrlRoute item = route2;
if ((namespaces != null) && (namespaces.Length > 0))
item.DataTokens["Namespaces"] = namespaces;
routes.Add(name, item);
return item;
public static LowerCaseUrlRoute MapLowerCaseUrlRoute(this AreaRegistrationContext context, string name, string url){
return context.MapLowerCaseUrlRoute(name, url, null);
public static LowerCaseUrlRoute MapLowerCaseUrlRoute(this AreaRegistrationContext context, string name, string url, object defaults){
return context.MapLowerCaseUrlRoute(name, url, defaults, null);
public static LowerCaseUrlRoute MapLowerCaseUrlRoute(this AreaRegistrationContext context, string name, string url, string[] namespaces){
return context.MapLowerCaseUrlRoute(name, url, null, namespaces);
public static LowerCaseUrlRoute MapLowerCaseUrlRoute(this AreaRegistrationContext context, string name, string url, object defaults, object constraints)        {
return context.MapLowerCaseUrlRoute(name, url, defaults, constraints, null);
public static LowerCaseUrlRoute MapLowerCaseUrlRoute(this AreaRegistrationContext context, string name, string url, object defaults, string[] namespaces){
return context.MapLowerCaseUrlRoute(name, url, defaults, null, namespaces);
public static LowerCaseUrlRoute MapLowerCaseUrlRoute(this AreaRegistrationContext context, string name, string url, object defaults, object constraints, string[] namespaces)
if ((namespaces == null) && (context.Namespaces != null))
namespaces = context.Namespaces.ToArray<string>();
LowerCaseUrlRoute route = context.Routes.MapLowerCaseUrlRoute(name, url, defaults, constraints, namespaces);
route.DataTokens["area"] = context.AreaName;
bool flag = (namespaces == null) || (namespaces.Length == 0);
route.DataTokens["UseNamespaceFallback"] = flag;
return route; 无标题文档先给出本文中测试用的 controller:public class PersonsController : Controller{    public ActionResult Query(string name)    {        return View();    }}  ASP.NET 中 Url 大小写  不严格来讲,ASP
一、 MVC 路由(Route)是什么? MVC 路由(Route)可以理解规定用户访问网站方式的配置文件,就例如:我们在访问普通页面时http://xxxx/web/xx.aspx,但在 MVC 中我们的访问方式有可能是:http://xxx/控制器名/Action方法名(这个是默认的路由规则) 那问题就来了,为什么Route可以改变我们的访问方式的呢? 二、Route的原理 1.页面生命周期
1、Global.asax文件注册路由(创建 MVC 项目时就有) public class Mvc Application : System.Web.HttpApplication protected void Application_Start() RouteConfig.RegisterRoutes(RouteTable.Ro...
public ActionResult Index( string controller, string action, int id) ViewBag.Name1 = controller; ViewBag.Name2 = action; ViewBag.Name3 = id
当使用Route在页面内做跳转时,有时想要传一些 自定义 属性到子路由组件中,而常用的&lt;Route path={xxx} component={Component} test={123}/&gt;这种方式设置test属性是无效的,因此需要用到Route的第二种渲染方式 Route组件的render方法 &lt;Route path="/home" re...
为了给用户更好的体验,我们一般会保持网站的 url 一致,一般 url 均为小写,更加美观。 这里以 MVC 为例: 1. 在 MVC 5.0 中,有一个属性,非常简单好用,我们直接在 RouteConfig 的静态方法RegisterRoutes 中设置routes.Lowercase Url s = true;即可。 该属性用于 “获取或设置指示标准化虚拟路径时是否将 URL 转换...
先说一下基本的路由规则原则。基本的路由规则是从特殊到一般排列,也就是最特殊(非主流)的规则在最前面,最一般(万金油)的规则排在最后。这是因为匹配路由规则也是照着这个顺序的。如果写反了,那么即便你路由规则写对了那照样坐等404. XD 首先说 URL 的构造。 其实这个也谈不上构造,只是语法特性吧。 URL 构造 命名参数规范+匿名对象 routes.MapR...
https://blog.csdn.net/u011966339/article/details/79387818 ASP.NET 引入了路由,以消除将每个 URL 映射到物理文件的需求。路由使我们能够定义映射到请求处理程序的 URL 模式。这个请求处理程序可以是一个文件或类。 路由定义了 URL 模式和处理程序信息。存储在RouteTable中的应用程序的所有已配置路由将由路由引擎用于为传...
https://docs.microsoft.com/en-us/aspnet/core/ mvc /controllers/routing?view=aspnetcore-2.1 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-2.1 https://docs.microso...
先给出本文中测试用的 controller: public class PersonsController : Controller public ActionResult Query( string name) return View(); ASP.NET Url 大小写 不严格来讲, ASP.NET MVC 对 ...
public class CustomerRoute : RouteBase {     //从路径中解析出controller、action以及其他参数,创建RouteData(其中包括HttpHandler),然后调用HttpHandler处理请求 public override RouteData GetRouteData(System.Web.Ht...
ASP.NET MVC Bootstrap是一个基于Bootstrap框架的开源项目,用于快速构建 ASP.NET MVC 应用程序。这个项目提供了一套预定义的样式和组件,可以轻松地创建出具有响应式设计的网页。 ASP.NET MVC Bootstrap的主要特点是可定制性强。用户可以根据自己的需求选择只需的组件,也可以根据自己的品牌色 自定义 主题。另外,这个项目还提供了大量的样例代码,便于用户学习和使用。 ASP.NET MVC Bootstrap的代码库是开源的,用户可以在GitHub上克隆代码或者直接下载源码包。其中包括Bootstrap框架、 ASP.NET MVC 样板代码以及相关的JavaScript、CSS和图片文件。用户可以根据自己的需求对源代码进行二次开发,或者将其作为自己的基础框架来开发 ASP.NET MVC 应用程序。 总之, ASP.NET MVC Bootstrap是一个非常实用、免费且具有高度定制性的开源框架,对开发 ASP.NET MVC 应用程序的人来说,是一个非常不错的选择。