GVKun编程网logo

asp.net与MVC4的路由原理和过程【学习笔记】(.net mvc路由原理)

7

在本文中,我们将给您介绍关于asp.net与MVC4的路由原理和过程【学习笔记】的详细内容,并且为您解答.netmvc路由原理的相关问题,此外,我们还将为您提供关于ASP.NETCoreMVC学习笔记

在本文中,我们将给您介绍关于asp.net与MVC4的路由原理和过程【学习笔记】的详细内容,并且为您解答.net mvc路由原理的相关问题,此外,我们还将为您提供关于ASP.NET Core MVC学习笔记、ASP.NET MVC , ASP.NET Web API 的路由系统与 ASP.NET 的路由系统是怎么衔接的?、asp.net mvc4 Controller与Action执行过程的研究(学习笔记)、Asp.Net MVC4入门指南(10):第三方控件Studio for ASP.NET Wijmo MVC4 工具应用的知识。

本文目录一览:

asp.net与MVC4的路由原理和过程【学习笔记】(.net mvc路由原理)

asp.net与MVC4的路由原理和过程【学习笔记】(.net mvc路由原理)

当IIS收到一个http请求,把请求信息发给对应的HttpModel(实际是实现类UrlRoutingModule),在HttpModel中会注册HttpApplication 类中的PostResolveRequestCache事件,通过此事件来动态选择映射HttpHandler处理程序。通过匹配到的RouteData类的RouteHandler属性得到IRouteHandler对象(MVC4MvcRouteHandlerasp.net原生的是PageRouteHandler),通过这个对象的GetHttpHandler方法就可以得到HttpHandler处理程序。具体代码如下:

public virtual void PostResolveRequestCache(object sender,EventArgs e) {

   HttpApplication app = (HttpApplication)sender;

       HttpContextBase context = new HttpContextwrapper(app.Context);
       //详细见附1
       RouteData routeData = RouteCollection.GetRouteData(context);
       if (routeData == null) {
            return;
       }
       //详细见附1
       IRouteHandler routeHandler = routeData.RouteHandler;
       if (routeHandler is StopRoutingHandler) {
           return;
       }
       RequestContext requestContext = new RequestContext(context,routeData);
       context.Request.RequestContext = requestContext;
       IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); 
       if (httpHandler is UrlAuthFailureHandler) {
           if (FormsAuthenticationModule.FormsAuthrequired) {
                 UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current,this);
            return;
          }
          else {
           throw new HttpException(401,SR.GetString(SR.Assess_Denied_Description3));
          }
      }
      context.RemapHandler(httpHandler);//动态指定HttpHandler
 }

 

1

RouteCollection.GetHttpHandler方法获取匹配当前路由信息的RouteData,此方法是循环遍历RouteCollection集合并调用集合中RouteBase实现类为Route)的GetRouteData方法,并返回第一个不为NullRoudataRoudata实在RoudataBaseGetRouteData方法中被创建的,详见如下代码:

 public override RouteData GetRouteData(HttpContextBase httpContext) {
     string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
     RouteValueDictionary values = _parsedRoute.Match(requestPath,Defaults);
     if (values == null) {
          return null;
     }
    //为Roudata指定IRouteHandler对象
    RouteData routeData = new RouteData(this,RouteHandler);     
if (!ProcessConstraints(httpContext,values,RouteDirection.IncomingRequest)) {
          return null;
     }
     foreach (var value in values) {
          routeData.Values.Add(value.Key,value.Value);
     }
     if (DataTokens != null) {
          foreach (var prop in DataTokens) {
             routeData.DataTokens[prop.Key] = prop.Value;
          }
     }
    return routeData;
 }
 
public class RouteData {
        private IRouteHandler _routeHandler; 
        public RouteData() {
        }
        public RouteData(RouteBase route,IRouteHandler routeHandler) {
            Route = route;
            RouteHandler = routeHandler;
        }
//更多代码
}

 

MVC4:动态指定到HttpHandler后,就是处理Controller的创建和Action的执行了

HttpHandler根据Routedata获取到Controller的名称,然后通过ControllerBuilder的静态方法GetControllerFactory获取IControllerFactory的实现类(默认是DefaultControllerFactory )来创建请求的IController的实现类的实例。最后执行IControllerExecute方法并传入请求上下文,方法Execute实现在System.Web.Mvc.ControllerBase这个抽象类中,Execute最终调用的是System.Web.Mvc.Controller中的ExecuteCore方法,ExecuteCore方法主要是保存TempData然后根据路由数据调用执行Action。代码如下:

 

protected override void ExecuteCore()
{
   PossiblyLoadTempData();
   try
   {
      string actionName = GetActionName(RouteData);
//ActionInvoker是System.Web.Mvc.Controller的属性,该属性是一个返回类型为IActionInvoker的action执行器 if (!ActionInvoker.InvokeAction(ControllerContext,actionName)) { HandleUnkNownAction(actionName); } } finally { PossiblySaveTempData(); } }

 

ASP.NET Core MVC学习笔记

ASP.NET Core MVC学习笔记

最近由于疫情紧张,遂在家办公,在领导的带领下,学习了一下.Net Core MVC。

 

一,构建web应用

1.选择c#-所有平台-web  找到ASP.NET Core web应用程序

 

 2.项目命名之后转至如下界面:选择Web应用程序(模型视图控制器)。

 

Ok点击创建,这个项目的基本框架就生成了。

 

二,EF-Code First 数据迁移

1,在Models里面新建实体类和数据库上下文类,并添加相对应的引用和框架

 

具体代码如下:

 实体类:

public class InfoUser
    {
        [Key]
        [StringLength(10)]
        [DisplayName("用户名")]
        [Required(ErrorMessage = "用户名不能为空")]
        public string UserName { get; set; }
        [StringLength(16)]
        [DisplayName("密码")]
        [Required(ErrorMessage = "密码不能为空")]
        public string Password { get; set; }
        [DisplayName("创建日期")]
        public DateTime DTCreate { get; set; }
        [DisplayName("登陆日期")]
        public DateTime DTLogin { get; set; }

        public byte[] HeaderImg { get; set; }
    }
    /// <summary>
    /// 博客表
    /// </summary>
    public class InfoBlog
    {
        public int ID { get; set; }
        [StringLength(10)]
        public string UserName { get; set; }
        [StringLength(64)]
        public string Title { get; set; }

        public string Content { get; set; }
        public int VisitCount { get; set; }
        public DateTime DTCreate { get; set; }
    }
    /// <summary>
    /// 回复表
    /// </summary>
    public class InfoReply
    {
        public int ID { get; set; }
        public int BlogID { get; set; }
        [StringLength(10)]
        public string UserName { get; set; }
        public string Content { get; set; }
        public DateTime DTCreate { get; set; }
    }
    /// <summary>
    /// 日志表
    /// </summary>
    public class InfoLog
    {
        public int ID { get; set; }
        [StringLength(64)]
        public string Title { get; set; }
        public string Content { get; set; }
        public DateTime DTCerate { get; set; }

    }

上下文类(需要继承DbContext):

public class DBXlp : DbContext
    {
        public DBXlp(DbContextOptions<DBXlp> options)
         : base(options)
        {

        }
        //add-migration init update-database 
        public DbSet<InfoUser> InfoUsers { get; set; }
        public DbSet<InfoBlog> InfoBlogs { get; set; }
        public DbSet<InfoReply> InfoReplies { get; set; }
        public DbSet<InfoLog> InfoLogs { get; set; }

    }

 

2,通过NuGet添加下面的两个包

 

 

3,在appsettings.json里面添加连接数据库的字符串

 

{
    "ConnectionStrings": {
      "DbXlp": "Server=.;Database=dbXlp;User id=sa;Password=123456"
    },
    "Logging": {
      "LogLevel": {
        "Default": "Information",
        "Microsoft": "Warning",
        "Microsoft.Hosting.Lifetime": "Information"
      }
    },
    "AllowedHosts": "*"
  }

  

4,然后在 Startup.cs 的 ConfigureServices(IServiceCollection services) 中,我们作为一个服务注册了上下文对象。

services.AddDbContext<DBXlp>(options=>options.UseSqlServer(Configuration.GetConnectionString("DbXlp")));

5,打开 工具->NuGet包管理器->程序包管理器控制台

  1. 输入命令:add-migration init(可随意)(为挂起的Model变化添加迁移脚本 )
  2. 输入命令:update-database( 将挂起的迁移更新到数据库 )

 

 Ok 可以看到下面数据库里已经有了对应表信息!

三,使用Identity进行登录验证

在Startup中:

 1.定义一个string类型的CookieScheme作为认证方案

public const string CookieScheme = "Cookies";

2.ConfigureServices中使用AddCookie设置选项

 

services.AddAuthentication(CookieScheme)
            .AddCookie(CookieScheme, option =>
            {
                // 登录路径:这是当用户试图访问资源但未经过身份验证时,程序将会将请求重定向到这个相对路径。
                option.LoginPath = new PathString("/account/login");
                // 禁止访问路径:当用户试图访问资源时,但未通过该资源的任何授权策略,请求将被重定向到这个相对路径
                option.AccessDeniedPath = new PathString("/account/denied");
            });

  

3.在Configure方法中使用 UseAuthentication来调用认证中间件(如下图所示位置)

 

控制器里面:

创建一个包含用户信息的 cookie可以构造一个ClaimsPrincipal。用户信息会被序列化然后保存在cookie 里面。

public async Task<ActionResult> Logining()
        {
            var userName = Request.Form["UserName"];
            var password = Request.Form["Password"];
            var item = db.InfoUsers.Find(userName);
            if (item != null && password == item.Password)
            {
                item.DTLogin = DateTime.Now;
                db.SaveChanges();
                //用Claim来构造一个ClaimsIdentity,然后调用 SignInAsync 方法。
                var claims = new List<Claim>();
                claims.Add(new Claim(ClaimTypes.Name, userName));
                var claimsIdentity = new ClaimsIdentity(claims, "Cookies");
                //登录
                await HttpContext.SignInAsync("Cookies", new ClaimsPrincipal(claimsIdentity));
                return RedirectToAction("Index", "Blog");
            }
            else
                ViewBag.Msg = "登陆失败";
            return View();
        }
public async Task<IActionResult> Logout() { //退出 await HttpContext.SignOutAsync("Cookies"); return RedirectToAction("Index", "Home"); }

然后我们只需要对那些需要授权登陆才能执行的界面或者操作标记[Authorize]即可:

最后我们也可以在Razor中判断是否授权成功:

@if (User.Identity.IsAuthenticated)
{
<textarea id="TextArea1" rows="10"></textarea>
    <button onclick="Reply()">回复</button>
}
else
{
    <a asp-action="Login" asp-controller="Account">登录</a>
}

 就写到这里。下次继续!

  

 

ASP.NET MVC , ASP.NET Web API 的路由系统与 ASP.NET 的路由系统是怎么衔接的?

ASP.NET MVC , ASP.NET Web API 的路由系统与 ASP.NET 的路由系统是怎么衔接的?

 

ASP.NET MVC 的路由实际上是建立在 ASP.NET 的路由系统之上的.

MVC 路由注册通常是这样的:

RouteTable 是一个全局路由表, 它的 Routes 静态属性是一个 RouteCollection 类型的实例,而 RouteCollection 是一个继承自 Collection<RouteBase> 的子类, RouteBase 是 ASP.NET 路由系统定义的基类

.

RouteBase 有一个唯一的实现类:

当我们通过如下方法注册一个路由时:

 

实际是向全局路由表中添加了一个 Route 类型的实例,部分源码如下: 

public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
    {
      ......
      Route route = new Route(url, (IRouteHandler) new MvcRouteHandler())
      {
    ......
      };
    ......
      routes.Add(name, (RouteBase) route);
      return route; }

从源码中我们可以看到,添加 Route 对象的时候,直接传入了一个 MvcRouteHandler 类型的实例.

我们知道, ASP.NET 的路由系统对路由的解析是通过一个注册的 HttpModule 对象实现对请求的拦截,然后为当前 Http 上下文动态映射一个 HttpHandler 对象, 而这个  HttpHandler 对象会接管对当前请求的处理并最终对请求予以响应.

这个注册的 HttpModule 对象的类型叫做 UrlRoutingModule .

我们可以在ASP.NET 的全局系统配置文件中找到它:

     <httpModules>
        ......
            <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule"/>
           ......
        </httpModules>

该类型在 PostResolveRequestCache 事件实现对请求的拦截:

 在拦截时,它做了这么几件事(部分源码省略):

public virtual void PostResolveRequestCache(HttpContextBase context)
    {
      RouteData routeData = this.RouteCollection.GetRouteData(context);     
      IRouteHandler routeHandler = routeData.RouteHandler; IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);  context.RemapHandler(httpHandler); }

1.遍历所有注册的路由,也就是所有添加到全局路由表中的 Route 类型的实例,通过调用它们的 GetRouteData 方法,拿到第一个匹配的 RouteData (路由数据);

2.拿到路由数据中的 RouteHandler 对象, 其实就是 MvcRouteHandler 类型的实例;

3.调用 MvcRouteHandler 的 GetHttpHandler 方法,拿到 HttpHandler.

MvcRouteHandler 的 GetHttpHandler 方法源码如下:

protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
      requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext));
      return (IHttpHandler) new MvcHandler(requestContext);
    }

可以看到,直接 new 了一个 MvcHandler 类型的实例,

最终,请求转交给这个 MvcHandler 类型的实例处理.

 

ASP.NET Web API 是怎么与 ASP.NET 路由系统接轨的呢?

我们知道, ASP.NET 的路由系统对路由的解析是通过一个注册的 HttpModule 对象实现对请求的拦截,然后为当前 Http 上下文动态映射一个 HttpHandler 对象, 而这个  HttpHandler 对象会接管对当前请求的处理并最终对请求予以响应.

这一条不仅对 MVC 适用, 对 Web API 同样适用,因为他俩都是借助于 ASP.NET 的路由系统.

区别在于 HttpHandler 的类型不一样而已.

ASP.NET Web API 注册路由的代码通常是这样的:

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new
                {
                    id = RouteParameter.Optional } );

config.Routes 是一个 HttpRouteCollection 类型的实例,并且是只读的.

只读的,就意味着只能在该实例所属的类的构造函数中初始化.

我们知道,这个 config 是 HttpConfiguration 类型,它在 GlobalConfiguration 类中初始化.

在它的初始化代码中,我们可以看到:

private static Lazy<HttpConfiguration> CreateConfiguration()
        {
            return new Lazy<HttpConfiguration>(() =>
            {
                HttpConfiguration config = new HttpConfiguration(new HostedHttpRouteCollection(RouteTable.Routes));
          ......return config;
            });
        }

 

HttpConfiguration 实际是对 HostedHttpRouteCollection 的封装,而后者是对 RouteTable.Route 的封装. 即 ASP.NET 全局路由表的封装.

所以说, HttpConfiguration 类型封装了 ASP.NET 的全局路由表. 它的 Routes 属性的实际类型是  HostedHttpRouteCollection

我们再回头看 config.Routes.MapHttpRoute 方法 , 也就是 HostedHttpRouteCollection 类型的 MapHttpRoute 方法:

public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, object constraints, HttpMessageHandler handler)
        {
            ......
            IHttpRoute route = routes.CreateRoute(routeTemplate, defaultsDictionary, constraintsDictionary, dataTokens: null, handler: handler);
            routes.Add(name, route);
            return route;
        }

很简单,创建了一个路由,然后添加它.

我们继续查看 HostedHttpRouteCollection 类型的 CreateRoute 方法:

public override IHttpRoute CreateRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler)
    {
    ......
    return (IHttpRoute) new HostedHttpRoute(uriTemplate, defaults, constraints, dataTokens, handler); }

返回了一个 HostedHttpRoute 类型的实例.

我们可以把这个方法 和 上面 MVC 的 MapRoute 方法做比较:

MVC:

public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
    {
      ......
      Route route = new Route(url, (IRouteHandler) new MvcRouteHandler())
      {
    ......
      };
    ......
      routes.Add(name, (RouteBase) route);
      return route;
    }

 

是不是非常像!不同的只是 MVC  new 的路由对象是 Route 类型,而 Web API new 的路由对象是 HostedHttpRoute 类型.

讲到这里,其实 ASP.NET Web API 的路由系统还没有和 ASP.NET 的路由系统衔接起来,它们二者的关系仅仅体现在下面这句话:

HttpConfiguration 实际是对 HostedHttpRouteCollection 的封装,而后者是对 RouteTable.Route 的封装. 即 ASP.NET 全局路由表的封装.

但是,当 HostedHttpRoute 创建后,调用 HostedHttpRouteCollection 的 Add 方法添加时,衔接就真正开始了:

public override void Add(string name, IHttpRoute route)
        {
            _routeCollection.Add(name, route.ToRoute());
        }
 

 _routeCollection 是 RouteCollection 类型,没看错,就是 ASP.NET 路由系统的 RouteCollection .

所以,这句代码实际是向 ASP.NET 路由系统的路由集合中添加路由,目的就是为了让 UrlRoutingModule 能够拦截到匹配了 Web API 注册的路由的请求.

但是,问题来了,从上面 MVC 的讲解中我们知道, ASP.NET 路由系统的 RouteCollection 是一个继承自 Collection<RouteBase> 的子类, RouteBase 是 ASP.NET 路由的基类,

而 HostedHttpRoute 是实现了 IHttpRoute 接口的实例, 

IHttpRoute  和 RouteBase 风马牛不相接啊!

所以,添加时,Web API 通过 HostedHttpRoute  的 ToRoute 方法,将自己转成了 RouteBase 类型!!

这个转化非常简单:

public static Route ToRoute(this IHttpRoute httpRoute)
        {
       ...... HostedHttpRoute hostedHttpRoute = httpRoute as HostedHttpRoute; if (hostedHttpRoute != null) { return hostedHttpRoute.OriginalRoute; }
       ...... }

问题又来了, HostedHttpRoute 类型的 OriginalRoute 是个什么鬼?当然,肯定是个 Route  类型,也就是说,它是一个 ASP.NET 路由系统定义的 Route 类型.那它是怎么来的呢?

我们知道,在 Web API 注册路由时, MapHttpRoute 内部创建了一个 HostedHttpRoute 类型的实例,并且是直接 new 的.

那么我们去看看 HostedHttpRoute 的构造函数:

public HostedHttpRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler)
        {
            ......
            OriginalRoute = new HttpWebRoute(uriTemplate, routeDefaults, routeConstraints, routeDataTokens, HttpControllerRouteHandler.Instance, this);
            ......
        }

OriginalRoute 原来是一个 HttpWebRoute 类型,而 HttpWebRoute 则是 ASP.NET 路由系统定义的 Route 类型的子类.

并且,创建 HttpWebRoute 类型的实例时,传入了一个 ASP.NET 路由系统定义的 IRouteHandler 类型的实例 : HttpControllerRouteHandler.Instance

而 HttpControllerRouteHandler 的 GetHttpHandler 方法如下:

/// <summary>
        /// Provides the object that processes the request.
        /// </summary>
        /// <param name="requestContext">An object that encapsulates information about the request.</param>
        /// <returns>
        /// An object that processes the request.
        /// </returns>
        protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new HttpControllerHandler(requestContext.RouteData);
        }

返回了一个 HttpControllerHandler 类型的实例.

HttpControllerHandler 类型的XML注释则非常清晰的解释了它的作用:

  /// 用于将 ASP.NET 请求传递给管道并写回结果。</summary>

这里说的管道,自然就是 Web API 的消息处理管道了.

 

总结:

ASP.NET MVC 和 ASP.NET Web API 都是通过 UrlRoutingModule ,在 PostResolveRequestCache 事件实现对请求的拦截.

拦截后,通过对HTTP上下文,路由等一系列处理后,

MVC 创建了 MvcHandler 进行具体的请求处理及响应;

Web API 创建了 HttpControllerHandler 进行具体的请求处理及响应.

原文出处:https://www.cnblogs.com/refuge/p/10505565.html

asp.net mvc4 Controller与Action执行过程的研究(学习笔记)

asp.net mvc4 Controller与Action执行过程的研究(学习笔记)

当IIS收到一个http请求,把请求信息发给对应的HttpModel(实际是实现类UrlRoutingModule),在HttpModel中会注册HttpApplication 类中的PostResolveRequestCache事件,通过此事件来动态选择映射HttpHandler处理程序。通过匹配到的RouteData类的RouteHandler属性得到IRouteHandler对象(MVC4MvcRouteHandlerasp.net原生的是PageRouteHandler),通过这个对象的GetHttpHandler方法就可以得到HttpHandler处理程序。具体代码如下:

public virtual void PostResolveRequestCache(object sender,EventArgs e) {
   HttpApplication app = (HttpApplication)sender;
       HttpContextBase context = new HttpContextwrapper(app.Context);
       //详细见附1
       RouteData routeData = RouteCollection.GetRouteData(context);
       if (routeData == null) {
            return;
       }
       //详细见附1
       IRouteHandler routeHandler = routeData.RouteHandler;
       if (routeHandler is StopRoutingHandler) {
           return;
       }
       RequestContext requestContext = new RequestContext(context,routeData);
       context.Request.RequestContext = requestContext;
       IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); 
       if (httpHandler is UrlAuthFailureHandler) {
           if (FormsAuthenticationModule.FormsAuthrequired) {
                 UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current,this);
            return;
          }
          else {
           throw new HttpException(401,SR.GetString(SR.Assess_Denied_Description3));
          }
      }
      context.RemapHandler(httpHandler);//动态指定HttpHandler
 }

 

1

RouteCollection.GetHttpHandler方法获取匹配当前路由信息的RouteData,此方法是循环遍历RouteCollection集合并调用集合中RouteBase实现类为Route)的GetRouteData方法,并返回第一个不为NullRoudataRoudata实在RoudataBaseGetRouteData方法中被创建的,详见如下代码:

 

public override RouteData GetRouteData(HttpContextBase httpContext) {
     string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
     RouteValueDictionary values = _parsedRoute.Match(requestPath,Defaults);
     if (values == null) {
          return null;
     }
//为Roudata指定IRouteHandler对象
    RouteData routeData = new RouteData(this,RouteHandler);     
if (!ProcessConstraints(httpContext,values,RouteDirection.IncomingRequest)) {
          return null;
     }
     foreach (var value in values) {
          routeData.Values.Add(value.Key,value.Value);
     }
     if (DataTokens != null) {
          foreach (var prop in DataTokens) {
             routeData.DataTokens[prop.Key] = prop.Value;
          }
     }
    return routeData;
 }

public class RouteData {
        private IRouteHandler _routeHandler; 
        public RouteData() {
        }
 
        public RouteData(RouteBase route,IRouteHandler routeHandler) {
            Route = route;
            RouteHandler = routeHandler;
        }
//更多代码
}

 

 MVC4:动态指定到HttpHandler后,就是处理Controller的创建和Action的执行了

 

HttpHandler根据Routedata获取到Controller的名称,然后通过ControllerBuilder的静态方法GetControllerFactory获取IControllerFactory的实现类(默认是DefaultControllerFactory )来创建请求的IController的实现类的实例。最后执行IControllerExecute方法并传入请求上下文,方法Execute实现在System.Web.Mvc.ControllerBase这个抽象类中,Execute最终调用的是System.Web.Mvc.Controller中的ExecuteCore方法,ExecuteCore方法主要是保存TempData然后根据路由数据调用执行Action。代码如下:

 

protected override void ExecuteCore()
{
PossiblyLoadTempData();
try
{
string actionName = GetActionName(RouteData);
//ActionInvoker是System.Web.Mvc.Controller的属性,该属性是一个返回类型为IActionInvoker的action执行器
if (!ActionInvoker.InvokeAction(ControllerContext,actionName))
{
HandleUnkNownAction(actionName);
}
}
finally
{
PossiblySaveTempData();
}
}

 

 

MVC4:执行Action的过程

HttpHandler调用Controller.Execute方法后,最终将会通过IActionInvoke.ActionInvoker(ControllerContext controllerContext, string actionName)方法来执行ActionSystem.Web.Mvc.Controller类中有一个ActionInvoker属性,此属性提供IActionInvoke的实现类的实例,用来执行此Controller类中的Action,属性具体的赋值方式如下:

 

protected virtual IActionInvoker CreateActionInvoker()
{
// Controller supports asynchronous operations by default.
// Those factories can be customized in order to create an action invoker for each request.
IAsyncActionInvokerFactory asyncActionInvokerFactory = Resolver.GetService<IAsyncActionInvokerFactory>();
if (asyncActionInvokerFactory != null)
{
return asyncActionInvokerFactory.CreateInstance();
}
IActionInvokerFactory actionInvokerFactory = Resolver.GetService<IActionInvokerFactory>();
if (actionInvokerFactory != null)
{
return actionInvokerFactory.CreateInstance();
}
// Note that getting a service from the current cache will return the same instance for every request.
return Resolver.GetService<IAsyncActionInvoker>() ??
Resolver.GetService<IActionInvoker>() ??
new AsyncControllerActionInvoker();
}

 

 ActionInvokerControllerDescriptor/ActionDescriptor的关系图如下:

由此可以看出Controller选择IActionInvoker的机制是这样的:

1、通过当前的DependencyResolverIAsyncActionInvokerFactory接口去获取注册的ActionInvoker工厂类,如果返回对象不为Null,则将其CreateInstance方法的返回值作为默认的ActionInvoker

2、通过当前的DependencyResolverIActionInvokerFactory接口去获取注册的ActionInvoker工厂类,如果返回对象不为Null,则将其CreateInstance方法的返回值作为默认的ActionInvoker

3、通过当前的DependencyResolverIAsyncActionInvoker接口去获取注册的ActionInvoker,如果返回对象不为Null,则将其作为默认的ActionInvoker

4、通过当前的DependencyResolverIActionInvoker接口去获取注册的ActionInvoker,如果返回对象不为Null,则将其作为默认的ActionInvoker

5、创建AsyncControllerActionInvoker对象作为默认的ActionInvoker

 

 

 

获取到ActionInvoker后,就会执行InvokeAction方法(异步的是BeginInvokeAction/EndInvokeAction,方法中会根据controllerContext先获取ControllerDescriptor、然后根据controllerContextActionName通过ControllerDescriptor.FindAction方法获取ActionDescriptor(获取过程中需要通过Action选择器来过滤匹配Action,选择器包含ActionNameSelectorAttributeActionMethodSelectorAttribute,具体下面会有描述),获取到ActionDescriptor之后,开始执行Action的筛选器(IActionFilterIAuthorizationFilterIExceptionFilterIResultFilter),在通过ControllerDescriptor获取到ActionDescriptor之后,ActionInvoker会获取到所有的筛选器特性(Filterattribute),按照四种类别存储在FilterInfo类的四个属性中(ActionFiltersAuthorizationFiltersExceptionFiltersResultFilters),然后开始循环验证AuthorizationFilters,(如果其中一个验证没有通过,就立即退出Action后续的执行,根据Result响应回复客户端),如果验证通过后,开始验证IActionFilter(这里看的比较饶人,因为要依次构建一个环环相扣的执行树,而这个树是从执行方向的尾部先构建的,所以为了保证执行顺序不反转就要先反转Filter,然后通过Aggregate方法循环Filte列表,再为每一个Filter建立lamda表达式层层组建为一个嵌套的Fun委托,最后再执行这个组建完成的委托,当其中某一个Filter验证失败的时候,就不再继续执行下一层的Fliter,直接返回Filter的验证失败Result),IActionFilter有两个方法,一个是开始执行Action前执行的方法OnActionExecuting,一个是执行完Action后执行的方法OnActionExecuted,验证不通过将不会执行OnActionExecuted方法。验证过所有的IActionFilter之后(Action也执行了并返回了Result)就开始验证IResultFilterIResultFilter也要分OnResultExecutingOnResultExecuted方法大致和IActionFilter差不多。

执行三种筛选器和Action的时候有任何异常,都会被try/catch捕捉到,并开始执行IExceptionFilter筛选器,此筛选器和IAuthorizationFilter差不多。

(需要研究的是ActionResult的执行过程)

ControllerDescriptorActionDescriptor之间的关系

 

 

ControllerDescriptorControllerDescriptor详解

 

 首先是抽象类型的ControllerDescriptor

 

 public abstract class ControllerDescriptor : ICustomAttributeProvider
   {  
        public virtual object[] GetCustomAttributes(bool inherit);
       public virtual object[] GetCustomAttributes(Type attributeType,bool inherit);
       public virtual bool IsDefined(Type attributeType,bool inherit);
        public virtual IEnumerable<Filterattribute> GetFilterattributes(bool useCache);
   	//获取指定Action,如果Action上应用了ActionNameSelectorAttribute特性(实现类
	//ActionNameAttribute),则会根据特性的IsValidName方法的返回值来匹配
        public abstract ActionDescriptor FindAction(ControllerContext controllerContext,string actionName);
	//返回此Controller中所有的Action描述类
	//(仅限于公有实例方法,但是从.Controller中继承下来的方法除外)
        public abstract ActionDescriptor[] GetCanonicalActions();
   
      public virtual string ControllerName { get; }
      public abstract Type ControllerType { get; }
      public virtual string UniqueId { get; }
  }
  //此类用来获取Controller上的所有自定义特性或指定类型的特性
  public interface ICustomAttributeProvider
  {
      object[] GetCustomAttributes(bool inherit);
      object[] GetCustomAttributes(Type attributeType,bool inherit);
 //判断指定类型是否应用在Controller上
      bool IsDefined(Type attributeType,bool inherit);
  }

 

ControllerDescriptor的默认实现类有两个,这两个都是通过反射的方式解析用于描述Controller的元数据:ReflectedControllerDescriptorReflectedAsyncControllerDescriptor,这两个类的区别就在FindActionGetCanonicalActions两个方法,主要区别在于ReflectedControllerDescriptor是通过反射匹配ActionNameReflectedAsyncControllerDescriptor要处理ActionName后面的{ActionName}Async{ActionName}Complete的后缀。

 

抽象类型ActionDescriptorExecute方法要深入研究一下:

 

public abstract class ActionDescriptor : ICustomAttributeProvider
   {
	//这三个方法同ControllerDescriptor
       public virtual object[] GetCustomAttributes(bool inherit);
       public virtual object[] GetCustomAttributes(Type attributeType,bool inherit);

       public virtual IEnumerable<Filterattribute> GetFilterattributes( bool useCache);
      //Action需要接受的所有参数的描述
       public abstract ParameterDescriptor[] GetParameters();
	//Action方法的执行
       public abstract object Execute(ControllerContext controllerContext,IDictionary<string,object> parameters);

 

 

//ActionSelector用于选择Action,可以自定义选择器(实现ActionMethodSelectorAttribute类)然后

//以特性的方式应用在Action上,如Post Get请求分开的Action,就是通过选择器来实现的。值得注意的是,

//Action的别名选择器特性ActionNameSelectorAttribute的匹配优先于

//ActionMethodSelectorAttribute选择器,虽然优先,但是结果还是会被

//ActionMethodSelectorAttribute选择器过滤

 

     public virtual ICollection<ActionSelector> GetSelectors();
//Action筛选器,总共有四种筛选器IActionFilter、IAuthorizationFilter、IExceptionFilter
//IResultFilter(需要继续深入研究)
      public virtual FilterInfo GetFilters();   
  
      public abstract string ActionName { get; }
      public abstract ControllerDescriptor ControllerDescriptor { get; }
      public virtual string UniqueId { get; }
  }

 

 抽象类ActionDescriptor的实现类:ReflectedActionDescriptor普通ActionReflectedAsyncActionDescriptorXxxAsync/XxxCompleted异步ActionTaskAsyncActionDescriptor是返回类型为TaskAction

 

 

 

 

 

 

Asp.Net MVC4入门指南(10):第三方控件Studio for ASP.NET Wijmo MVC4 工具应用

Asp.Net MVC4入门指南(10):第三方控件Studio for ASP.NET Wijmo MVC4 工具应用

ComponentOne Studio for ASP.NET Wijmo最新版本2013V1支持MVC4,其中包括:

  • 新增 MVC 4 工程模板 (C# & VB) 开箱即用的MVC 4 工程模板基于Microsoft内置模板创建,我们仅优化了标记和CSS样式为 Wijmo风格,熟悉的模板布局和界面风格,无疑将缩短您的学习过程、节省开发时间及提高开发效率。

  • 新增国际化主题(Metro)

    • MVC4 模板自动增强Wijmo MVC Scaffolding模板,将会为您应用程序中的增删改查(CRUD)操作生成默认的模板文件,这些生成的文件为您的工程构建了起始的工程文件目录结构,当然你也可以修改它,Scaffolding模板的优美之处在于生成后您可以按照您的意愿来扩展它。

    • Wijmo-增强编辑器模板 该模板使您可以通过日期选择器、数值输入框和滑动条快速的定制应用。您甚至可以添加其他自定义的模板。



开始使用

使用ComponentOne Studio for ASP.NET Wijmo制作MVC4应用程序,首先要做的是安装Studio for ASP.NET Wijmo

测试环境 VS2012、MVC4、Framework4.5、IE10、Studio for ASP.NET Wijmo2013V1

文件-新建项目

在安装了Studio for ASP.NET Wijmo2013V1 之后,在 VS2012 中选择新建项目。在 Web 选项卡中,您可以发现Studio for ASP.NET Wijmo2013V1。

clip_image002

好了,现在让我们运行程序看看初始效果。您可能对这个界面很熟悉。因为Wijmo MVC 4 工程模板是基于Microsoft内置模板创建。我们仅优化了标记和CSS样式为 Wijmo风格。

clip_image004

添加模型

下面,让我们使用Wijmo MVC Scaffolding模板创建一个简易的“ToDoList”。首先我们来添加模型。需要添加以下代码:

?

创建控制器和视图

在添加控制器和视图之前,编译项目。这将使Scaffolding模板识别新增的模型。现在,邮件点击Controllers文件夹,选择“添加控制器”,选择一下选项点击“添加”。

clip_image006

Scaffolding将会自动生成控制器和增删改查应用程序所需要的所有视图。最大的亮点是这些生成的文件为您的工程构建了起始的工程文件目录结构,当然你也可以修改它,Scaffolding模板的优美之处在于生成后您可以按照您的意愿来扩展它。

运行

仅仅通过以上步骤,我们就实现了简易的ToDoList。切换到ToDoList页面,应用程序会给模型创建数据源,首先展示给我们的是一张空表格。我们可以通过“创建新计划”按钮添加计划。

clip_image008

在创建视图中您会发现展现在眼前的是标准的EditorFor Helpers。然而我们已经在工程中添加了自定义编辑模板。所以如果使用日期或数值等类型时,Scaffolding模板会自动生成编辑器。下面自定义编辑器视图截图:

clip_image010

现在我们就完成了具有增删改查功能的MVC4应用程序。这些生成的文件为您的工程构建了起始的工程文件目录结构,当然你也可以修改它,Scaffolding模板的优美之处在于生成后您可以按照您的意愿来扩展它。


Demo源码下载:TahDoMvc4.zip


工具下载链接:Studio for ASP.NET Wijmo


--------------------------------------------------------------------------------------------------------------------

译者注:

本系列共9篇文章,翻译自Asp.Net MVC4 官方教程,由于本系列文章言简意赅,篇幅适中,从一个示例开始讲解,全文最终完成了一个管理影片的小系统,非常适合新手入门Asp.Net MVC4,并由此开始开发工作。9篇文章为:

1. Asp.Net MVC4 入门介绍

・ 原文地址:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/intro-to-aspnet-mvc-4

・ 译文地址:http://www.cnblogs.com/powertoolsteam/archive/2012/11/01/2749906.html

2. 添加一个控制器

・ 原文地址:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-controller

・ 译文地址:http://www.cnblogs.com/powertoolsteam/archive/2012/11/02/2751015.html

3. 添加一个视图

・ 原文地址:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-view

・ 译文地址:http://www.cnblogs.com/powertoolsteam/archive/2012/11/06/2756711.html

4. 添加一个模型

・ 原文地址:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-model

・ 译文地址:http://www.cnblogs.com/powertoolsteam/archive/2012/12/17/2821495.html

5. 从控制器访问数据模型

・ 原文地址:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/accessing-your-models-data-from-a-controller

・ 译文地址:http://www.cnblogs.com/powertoolsteam/archive/2013/01/11/2855935.html

6. 验证编辑方法和编辑视图

・ 原文地址:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/examining-the-edit-methods-and-edit-view

・ 译文地址:http://www.cnblogs.com/powertoolsteam/archive/2013/01/24/2874622.html

7. 给电影表和模型添加新字段

・ 原文地址:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-new-field-to-the-movie-model-and-table

・ 译文地址:http://powertoolsteam.blog.51cto.com/2369428/1140334

8. 给数据模型添加校验器

・ 原文地址:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-validation-to-the-model

・ 译文地址:http://powertoolsteam.blog.51cto.com/2369428/1147449

9. 查询详细信息和删除记录

・ 原文地址:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/examining-the-details-and-delete-methods

・ 译文地址:http://powertoolsteam.blog.51cto.com/2369428/1149311

10.第三方控件Studio for ASP.NET Wijmo MVC4 工具应用

・ 地址:http://powertoolsteam.blog.51cto.com/2369428/1196469

我们今天的关于asp.net与MVC4的路由原理和过程【学习笔记】.net mvc路由原理的分享就到这里,谢谢您的阅读,如果想了解更多关于ASP.NET Core MVC学习笔记、ASP.NET MVC , ASP.NET Web API 的路由系统与 ASP.NET 的路由系统是怎么衔接的?、asp.net mvc4 Controller与Action执行过程的研究(学习笔记)、Asp.Net MVC4入门指南(10):第三方控件Studio for ASP.NET Wijmo MVC4 工具应用的相关信息,可以在本站进行搜索。

本文标签: