• asp.net mvc源码分析BeginForm方法 和ClientValidationEnabled 属性


    在上篇文章asp.net mvc源码分析-RenderAction和RenderPartial我们提到了一个常用的RenderAction方法,除了它我们还会经常遇到表单提交,这时我们通常会用到BeginForm。让我们来看看你BeginForm是如何使用的

    运行结果就是生成form表单

    一般我们的表单提交都涉及到强类型,所以一般需要@model MvcApp.Controllers.UserInfo指令,那我们来看看你用@using (Html.BeginForm()) 和Html.BeginForm();、Html.EndForm();这两种用法有什么区别。

    我们找到BeginForm返回的是一个MvcForm,而MvcForm的一定如下: public class MvcForm : IDisposable

    可见使用using最后调用的是MvcForm的Dispose方法:

     protected virtual void Dispose(bool disposing) {
                if (!_disposed) {
                    _disposed = true;
                    _writer.Write("</form>");
                    // output client validation and restore the original form context
                    if (_viewContext != null) {
                        _viewContext.OutputClientValidation();
                        _viewContext.FormContext = _originalFormContext;
                    }
                }
            }

    这里的_disposed默认是false,_writer是viewContext.Writer或则httpResponse.Output都表示当前的输出流。那么我们再来看看EndForm吧:

     public static void EndForm(this HtmlHelper htmlHelper) {
                htmlHelper.ViewContext.Writer.Write("</form>");
                htmlHelper.ViewContext.OutputClientValidation();
            }

    可见EndForm和MvcForm的Dispose方法完全等价

    我们来看看你BeginForm是如何实现的,

     public static MvcForm BeginForm(this HtmlHelper htmlHelper) {
                // generates <form action="{current url}" method="post">...</form>
                string formAction = htmlHelper.ViewContext.HttpContext.Request.RawUrl;
                return FormHelper(htmlHelper, formAction, FormMethod.Post, new RouteValueDictionary());

            }

       public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName, RouteValueDictionary routeValues, FormMethod method, IDictionary<string, object> htmlAttributes) {
                string formAction = UrlHelper.GenerateUrl(null /* routeName */, actionName, controllerName, routeValues, htmlHelper.RouteCollection, htmlHelper.ViewContext.RequestContext, true /* includeImplicitMvcValues */);
                return FormHelper(htmlHelper, formAction, method, htmlAttributes);

            }

    这里的FormHelper方法比较简单,里面有一句需要注意一下  bool traditionalJavascriptEnabled = htmlHelper.ViewContext.ClientValidationEnabled && !htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled;这里ClientValidationEnabled 和UnobtrusiveJavaScriptEnabled默认都是true,所以traditionalJavascriptEnabled 为false。

    上面有个GenerateUrl方法,这个方法也很简单,关键代码就3句。

    RouteValueDictionary mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, requestContext.RouteData.Values, routeValues, includeImplicitMvcValues);
                VirtualPathData vpd = routeCollection.GetVirtualPathForArea(requestContext, routeName, mergedRouteValues);
                string modifiedUrl = PathHelpers.GenerateClientUrl(requestContext.HttpContext,vpd.VirtualPath);

    看看这里我们就用到了VirtualPathData的VirtualPath属性了

    FormExtensions中有一个BeginRouteForm方法,该方法的使用方式和BeginForm方法差不多,就跳过了。现在我们来看看ClientValidationEnabled 和UnobtrusiveJavaScriptEnabled默认为什么是true?这2个属性都是调用ScopeCache实例的对应属性,而获取ScopeCache时通过ScopeCache的Get方法来完成的。该Get方法代码很简单:

      public static ScopeCache Get(IDictionary<object, object> scope, HttpContextBase httpContext) {
                    if (httpContext == null && System.Web.HttpContext.Current != null) {
                        httpContext = new HttpContextWrapper(System.Web.HttpContext.Current);
                    }
    
                    ScopeCache result = null;
                    scope = scope ?? ScopeStorage.CurrentScope;
    
                    if (httpContext != null) {
                        result = httpContext.Items[_cacheKey] as ScopeCache;
                    }
    
                    if (result == null || result._scope != scope) {
                        result = new ScopeCache(scope);
    
                        if (httpContext != null) {
                            httpContext.Items[_cacheKey] = result;
                        }
                    }
    
                    return result;
                }
            }

    ScopeStorage的CurrentScope属性是又是怎么来的了?

            public static IScopeStorageProvider CurrentProvider {
                get {  return _stateStorageProvider ?? _defaultStorageProvider; }
                set {   _stateStorageProvider = value; }
            }
            public static IDictionary<object, object> CurrentScope {
                get {
                    return CurrentProvider.CurrentScope;
                }
            }
    默认来自于StaticScopeStorageProvider的CurrentScope,该属性默认返回的是一个没有成员的IDictionary<object, object>实例。那么这里的CurrentProvider 默认是个说明东西了,System.Web.WebPages项目中的PreApplicationStartCode有这么一句  ScopeStorage.CurrentProvider = new AspNetRequestScopeStorageProvider(); 在AspNetRequestScopeStorageProvider的够着函数有这么一句ApplicationScope = new ApplicationScopeStorageDictionary(); 在ApplicationScopeStorageDictionary的构造函数中有    public ApplicationScopeStorageDictionary()  : this(new WebConfigScopeDictionary()) {   },WebConfigScopeDictionary的够着函数又有    public WebConfigScopeDictionary()  :this(WebConfigurationManager.AppSettings) {  }这么一句.

    不知道大家是否还记得在ControllerBase的Execute方法中有这么一句 using (ScopeStorage.CreateTransientScope()) { ExecuteCore(); }

    public static IDisposable CreateTransientScope() { 
    return CreateTransientScope(new ScopeStorageDictionary(baseScope: CurrentScope));
    }
      public static IDisposable CreateTransientScope(IDictionary<object, object> context) {
                var currentContext = CurrentScope;
                CurrentProvider.CurrentScope = context;
                return new DisposableAction(() => CurrentProvider.CurrentScope = currentContext); // Return an IDisposable that pops the item back off

            }

    现在我们知道了IScopeStorageProvider 的CurrentProvider是什么东西了,是一个AspNetRequestScopeStorageProvider,里面的数据有一部分从WebConfigurationManager.AppSettings这里取出来的。

    我想大家现在该明白为什么这个ClientValidationEnabled 默认是true了吧

  • 相关阅读:
    判断&数学&生活
    Tomcat7源码环境搭建
    CentOS 7 下使用虚拟环境Virtualenv安装Tensorflow cpu版记录
    Quartz学习笔记1:Quartz概述
    Docker学习笔记2: Docker 概述
    大数据基础知识问答----spark篇,大数据生态圈
    [MSSQL] [EntityFramework(.Net Core)] 自增长id字段,无法插入数据
    [json-server] RESTful API 中,取主数据时,同时获取多个关联子表的数据
    前后端分离开发之前端自己的API(DB)---- (2)
    前后端分离开发之前端自己的API(DB)---- (1)
  • 原文地址:https://www.cnblogs.com/majiang/p/2767920.html
Copyright © 2020-2023  润新知