欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-12-01 17:56:51
  运行结果就是生成form表单   一般我们的表单提交都涉及到强类型,所以一般需要@model MvcApp.Controllers.UserI...

 

asp.net mvc源码分析-BeginForm方法 和ClientValidationEnabled 属性
运行结果就是生成form表单

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


一般我们的表单提交都涉及到强类型,所以一般需要@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方法代码很简单:


[csharp] view plaincopyprint?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; 
          } 
      } 

  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这里取出来的。

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

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