zoukankan      html  css  js  c++  java
  • Asp.Net MVC4 系列-- 进阶篇之路由(1)

    创建一个路由  

    打开 RouteConfig.cs  ,发现已经创建了一个默认路由 :

    routes.MapRoute(
                    name:"Default",
                    url:"{controller}/{action}/{id}"
                  //  defaults: new { controller ="Home", action = "Index", id = UrlParameter.Optional }
                );

    为了说明路由的url匹配过程,暂时comment掉default参数。 

    打开Global.cs ,可以看到路由配置文件已经注册:

    protected void Application_Start()
            {
               AreaRegistration.RegisterAllAreas();
     
               WebApiConfig.Register(GlobalConfiguration.Configuration);
               FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
               RouteConfig.RegisterRoutes(RouteTable.Routes);
               BundleConfig.RegisterBundles(BundleTable.Bundles);
            }

    关于路由工作方式

    Asp.net MVC Framework 的路由部分,是插入在http pipeline中的,当接受到http请求,会寻找注册的路由表(在ApplicationStart时候注册,就是应用启动时候),找到路由规则,获取每个路由规则的pattern,试图匹配当前请求合适的那个route,匹配成功,则解析出controller和action,从controllerfactory找到相应的controller,把请求传递给action,如果请求中传参,路由还会解析出参数,给action。

    下面是几种url匹配的例子:

    http://mysite/Admin/Index

    Controller =Admin,Action=Index

    http://mysite/Index/Admin

    Controller=Index,Action=Admin

    http://mysite/Apples/Oranges

    Controller=Apples,Action=Oranges

    http://mysite/Admin

    匹配失败,Segment太少

    http://mysite/Admin/Index/Soccer

    匹配失败,Segment太多

     

    路由会调用route handler来完成路由过程,默认的,mvc应用会使用MVCRouteHandler.手动添加一个Route,就可以体现出来:

    routes.Add("MyRoute",newRoute("{controller}/{action}", new MvcRouteHandler()));

    指定默认(default)

    刚才说明url匹配时候,拿掉了default参数,这时我们一起看看default参数的作用。

    routes.MapRoute(
                    name:"Default",
                    url:"{controller}/{action}/{id}",
                   defaults: new { controller = "Home", action ="Index", id = UrlParameter.Optional }
                );

    可以看到最后一个参数,指定了一个默认的controller和action。

     

    Mydomain.com

    Controller = Home ,action=Index

    Mydomain.com/Customer

    Controller=Customer ,action=Index

    Mydomain.com/Customer/List

    Controller=Customer, action=List

    Mydomain.com/Customer/List/All

    匹配失败,segment太多

     

    定值Segment

    场景1,所有请求中第一个Segment为”public”的,需要统一处理,因此定义一个路由:

         routes.MapRoute(name: "PublicReqRoute", url:"Public/{controller}/{action}",
                               defaults: new {controller = "PublicHome", action ="Index"});

    示例url:http://mysite/Public

    匹配结果:controller = PublicHome,action=Index

    场景2,请求中以public开始的,需要统一处理,定义路由:

         routes.MapRoute(name: "PublicReqRoute", url:"Public{controller}/{action}",
                               defaults: new {controller = "PublicHome", action ="Index"});

    示例url: Http:/mysite/PublicX/

    匹配结果:controller=X,action=Index


    场景3:有个controller或action不工作,需要改个bug,把所有原来指向这个controller的请求暂时路由到首页:

    routes.MapRoute("myshop","Shop/OldAction",
    new { controller = "Home", action ="Index" });

    注意:路由是按着添加顺序依次解析的,因此把最特殊的那个路由放在前面,避免先fall 到相对generall的那个。

     

    获取参数

    对于Route:

    routes.MapRoute(
                    name:"Default",
                    url:"{controller}/{action}/{id}",
                   defaults: new { controller = "Home", action ="Index", id = UrlParameter.Optional }
                );

    请求:http://mysite/Home/Index/15

     

    Action 中使用RouteData.Values获取传入的id:

    public ActionResult CustomVariable() {
    ViewBag.Controller = "Home";
    ViewBag.Action = "CustomVariable";
    ViewBag.CustomVariable = RouteData.Values["id"];
    return View();
    }

    使用mvcframework Route System自动传参机制

     

    除了使用RouteData.Values取出传入的参数,可以更简单的定义个参数在action,但是参数名要和route定义的相同(id)

    Public  ActionResult  CustomVariable(string id) {
    ViewBag.Controller = "Home";
    ViewBag.Action = "CustomVariable";
    ViewBag.CustomVariable = id;
    return View();
    }

    对于url:http://mysite/Home/Index/15,id就会自动被赋值为15传入action

    定义可选参数

    依然对于url:

    routes.MapRoute(
                    name:"Default",
                    url:"{controller}/{action}/{id}",
                   defaults: new { controller = "Home", action ="Index", id = UrlParameter.Optional }
                );

    Id=UrlParameter.Optional,此时id就是可选参数

    Mydomain.com

    Controller=home ,action=Index

    Mydomain.com/customer

    Controller=customer, action=Index

    Mydomain.com/customer/List

    Controller=customer, action=List

    Mydomain.com/customer/List/All

    Controller=customer , action=List, Id=All

    Mydomain.com/customer/List/All/Delete

    url 匹配失败

     

    如果没有传参,action提供了id参数,那么id此时就为null;

    public  ActionResultCustomVariable(string id) {
    ViewBag.Controller = "Home";
    ViewBag.Action = "CustomVariable";
    ViewBag.CustomVariable = id == null ? "<novalue>" : id;
    return View();
    }
    作为另一个选择,可以指定一个默认参数,如果没url没传值,默认参数值就会被使用。
    Public  ActionResultCustomVariable(string id = "DefaultId") {
    ViewBag.Controller = "Home";
    ViewBag.Action = "CustomVariable";
    ViewBag.CustomVariable = id;
    return View();
    }

    使用{*catchall}捕捉超出数量的segment

    例如,对于这条route:

    routes.MapRoute("MyRoute","{controller}/{action} /{*catchall}",
    new { controller = "Home", action ="Index",
    id = UrlParameter.Optional });

    由于使用了{*catchall},对于url:

    http://mysite/Home/Index/All/More/More/More

    此时,controller=Home,Action=Index, catchall=”All/More/More/More”

    这样,就把解析剩下segment的工作交给了自己处理

     

    解决在不同namespace的同名controller

    例如现在有两个controller,在不同的命名空间:

    namespace UrlsAndRoutes.Controllers {
    public class HomeController : Controller {
    public ActionResult Index() {
    ViewBag.Controller = "Additional Controllers - Home";
    ViewBag.Action = "Index";
    return View("ActionName");
    }
    }
    }
    namespace UrlsAndRoutes.AdditionalControllers {
    public  classHomeController : Controller {
    public  ActionResultIndex() {
    ViewBag.Controller = "Additional Controllers -Home";
    ViewBag.Action = "Index";
    return View("ActionName");
    }
    }
    }

    对于这种情况,可能希望路由先在一个指定的命名空间里找,找到了返回:

    routes.MapRoute("MyRoute","{controller}/{action}/{id}/{*catchall}",
    new { controller = "Home", action ="Index",
    id = UrlParameter.Optional
    },
    new[] { "URLsAndRoutes.AdditionalControllers" });

    关键在于最后的那个命名空间参数,mvc framework会优先找 “URLsAndRoutes.AdditionalControllers”里面的controller,如果没找到,会搜索其余的命名空间。

    注意,这个new []{}数字里的参数是平行的,也就是说,如果mvc framework在这些命名空间里找到多个同名controller,不会找到第一个就返回,依然会抛出异常,例如:

    new[] { "URLsAndRoutes.AdditionalControllers","UrlsAndRoutes.Controllers"});

    对于url:

    http://mysite/home/Index/15

    mvc framework会在指定的命名空间数组里找,由于找到了多个homecontroller,因此抛出异常。

    如果希望这样:指定多个命名空间,找到了第一个就返回,怎么做?

    可以配置多条route:

    routes.MapRoute("AddContollerRoute","Home/{action}/{id}/{*catchall}",
    new { controller = "Home", action ="Index",
    id = UrlParameter.Optional },
    new[] { "URLsAndRoutes.AdditionalControllers" });
     
    routes.MapRoute("MyRoute","{controller}/{action}/{id}/{*catchall}",
    new { controller = "Home", action ="Index",
    id = UrlParameter.Optional },
    new[] { "URLsAndRoutes.Controllers" });

    mvc framework就会按着添加的顺序依次查找匹配的controller和action,找到了把解析好的参数(如果有)传递给action就返回了。

    只允许mvc framework在指定的 namespace里找,如何做?

    Route myRoute=routes.MapRoute("AddContollerRoute",
    "Home/{action}/{id}/{*catchall}",
    new { controller = "Home", action ="Index",
    id = UrlParameter.Optional },
    new[] { "URLsAndRoutes.AdditionalControllers" }); 
    
    myRoute.DataTokens["UseNamespaceFallback"] = false;

    由于把DataTokens[“UseNamespaceFallback”] 设为false,因此mvcframework在指定的命名空间:URLsAndRoutes.AdditionalControllers"里面找完了,就不去其他地方找了。

     

    给路由加限制

     

    正则限制

    可以加正则限制在controller和action:

    routes.MapRoute("MyRoute","{controller}/{action}/{id}/{*catchall}",
    new { controller = "Home", action ="Index", id = UrlParameter.Optional },
    new { controller = "^H.*"},
    new[] { "URLsAndRoutes.Controllers"});

    由于给controller加上了正则表达式:”^H.*”的限制,因此对于url匹配了urlpattern,解析出controller,如果不是以H开头的,也会被过滤掉。

    类似的,也可以使用给action加正则限制:

    new { controller = "^H.*", action ="^Index$|^About$"}

    这样,就限制了action必须是Index和About。

     

    使用Http Method限制

    可以限制请求是Get ,Post亦或是Put ,Delete等类型的。

    new { controller = "^H.*", action ="Index|About",
    httpMethod = new HttpMethodConstraint("GET") } ,

    这样,就限制了请求必须是Get方式的。

     

    注意,给route加httpmethod限制,和给controller还有action加httpmethod区别在于,route在httppipeline很早的位置就被处理了,而到controller和action的时候,已经是httppipeline很晚的时候了,controller和action已经被解析了,参数也已经被解析了(如果有)。

     

    自定义限制

    Mvc 提供了IRouteConstranit 接口,可以自己定义限制,通过实现这个接口:

    public class UserAgentConstraint : IRouteConstraint {
    private string requiredUserAgent;
    public UserAgentConstraint(string  agentParam) {
    requiredUserAgent = agentParam;
    }
    Public  boolMatch(HttpContextBase  httpContext, Routeroute, string  parameterName,
    RouteValueDictionary  values,RouteDirection routeDirection) {
    return httpContext.Request.UserAgent != null &&
    httpContext.Request.UserAgent.Contains(requiredUserAgent);
    }
    }

    以上代码,实现了一个限制,必须Request对象中的代理对象不为空,也就是必须使用代理才能访问,并且代理名称包含指定的名称。

    使用自定义限制:

    routes.MapRoute("ChromeRoute","{*catchall}",
    new { controller = "Home", action ="Index" },
    new {
    customConstraint = newUserAgentConstraint("Chrome")
    },
    new[] { "UrlsAndRoutes.AdditionalControllers" });

    这样,就实现了,必须使用chrome才能访问的限制。

     

    让访问物理文件目录的请求也参与路由

    默认情况下,route system会先检查url是否指向了一个物理文件目录,如果是,返回找到这个文件,返回;否则,执行注册到路由表里面的每一条route。也就是说,指向物理文件目录的请求实际在mvc路由机制之前已经被处理掉了,默认没有参与路由。

     

    如果要改变这个行为,例如访问

    http://mysite/resource/pic/  ,不希望route去找resource/pic物理目录,而希望route找resourcecontroller ,pic action,那么:

    在注册路由时,加上

    routes.RouteExistingFiles = true;

    这样,本地文件的检测 就会在路由机制执行之后了。

    在路由前后,httppipeline的示意图


    其中,httpmodule 1和 httpmodule2 在pipeline上面routesystem前后的两个module。

    加上route.RouteExistingFiles=true

    Route system中,就会先执行MVC Route,后执行CheckDisk Files .最后,为物理文件指定一个route:

    routes.MapRoute("DiskFile","Content/StaticContent.html",
    new {
    controller = "Resource",
    action = "List",
    });

    By Passing Route System

    有些访问资源文件的请求,是需要ignore的,这种需求直接用ignoreRoute就可以了:

    routes.IgnoreRoute("Content/{filename}.html");

    这样,对于url:http://mysite/Content.test.html 的请求就被route忽略了,访问页面,就会出现资源无法找到和404错误:

    HTTP Error 404.0 - Not Found

    The resource you are looking for has beenremoved, had its name changed, or is temporarily unavailable.

























  • 相关阅读:
    Pedometer_forAndroid
    linux改动登陆主机提示信息
    Cocos2d-x-Lua (2.x)脚本开发之 Lua语言基础
    共享库加载时重定位
    寻找志同道合的伙伴
    Leetcode[20]-Valid Parentheses
    storm的集群安装与配置
    CSS样式
    splash启动速度优化
    Android驱动之 Linux Input子系统之TP——A/B(Slot)协议
  • 原文地址:https://www.cnblogs.com/smartsmile/p/6234081.html
Copyright © 2011-2022 走看看