示例#1
0
        /// <summary>
        /// Gets the properties of the specified type.
        /// </summary>
        protected virtual IEnumerable <ViewModelPropertyMap> GetProperties(Type type)
        {
            foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(p => p.Name))
            {
                if (property.GetCustomAttribute <JsonIgnoreAttribute>() != null)
                {
                    continue;
                }

                var propertyMap = new ViewModelPropertyMap()
                {
                    PropertyInfo          = property,
                    Name                  = property.Name,
                    ViewModelProtection   = ProtectMode.None,
                    Type                  = property.PropertyType,
                    TransferAfterPostback = property.GetMethod != null && property.GetMethod.IsPublic,
                    TransferFirstRequest  = property.GetMethod != null && property.GetMethod.IsPublic,
                    TransferToServer      = property.SetMethod != null && property.SetMethod.IsPublic,
                    JsonConverter         = GetJsonConverter(property),
                    Populate              = ViewModelJsonConverter.IsComplexType(property.PropertyType) && !ViewModelJsonConverter.IsEnumerable(property.PropertyType) && property.GetMethod != null
                };

                foreach (ISerializationInfoAttribute attr in property.GetCustomAttributes().OfType <ISerializationInfoAttribute>())
                {
                    attr.SetOptions(propertyMap);
                }

                var bindAttribute = property.GetCustomAttribute <BindAttribute>();
                if (bindAttribute != null)
                {
                    propertyMap.Bind(bindAttribute.Direction);
                    if (bindAttribute.Name != null)
                    {
                        propertyMap.Name = bindAttribute.Name;
                    }
                }

                var viewModelProtectionAttribute = property.GetCustomAttribute <ProtectAttribute>();
                if (viewModelProtectionAttribute != null)
                {
                    propertyMap.ViewModelProtection = viewModelProtectionAttribute.Settings;
                }

                propertyMap.ClientExtenders =
                    property.GetCustomAttributes <ClientExtenderAttribute>()
                    .OrderBy(c => c.Order)
                    .Select(extender => new ClientExtenderInfo()
                {
                    Name = extender.Name, Parameter = extender.Parameter
                })
                    .ToList();

                var validationAttributes = validationMetadataProvider.GetAttributesForProperty(property);
                propertyMap.ValidationRules = validationRuleTranslator.TranslateValidationRules(property, validationAttributes).ToList();

                propertyMap.ValidateSettings();

                yield return(propertyMap);
            }
        }
示例#2
0
        /// <summary>
        /// Gets the properties of the specified type.
        /// </summary>
        protected virtual IEnumerable <ViewModelPropertyMap> GetProperties(Type type)
        {
            foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(p => p.Name))
            {
                if (property.GetCustomAttribute <JsonIgnoreAttribute>() != null)
                {
                    continue;
                }

                var propertyMap = new ViewModelPropertyMap()
                {
                    PropertyInfo          = property,
                    Name                  = property.Name,
                    ViewModelProtection   = ProtectMode.None,
                    Type                  = property.PropertyType,
                    TransferAfterPostback = property.GetMethod != null && property.GetMethod.IsPublic,
                    TransferFirstRequest  = property.GetMethod != null && property.GetMethod.IsPublic,
                    TransferToServer      = property.SetMethod != null && property.SetMethod.IsPublic,
                    JsonConverter         = GetJsonConverter(property),
                    Populate              = ViewModelJsonConverter.IsComplexType(property.PropertyType) && !ViewModelJsonConverter.IsEnumerable(property.PropertyType) && property.GetMethod != null
                };

                var bindAttribute = property.GetCustomAttribute <BindAttribute>();
                if (bindAttribute != null)
                {
                    propertyMap.TransferAfterPostback      = bindAttribute.Direction.HasFlag(Direction.ServerToClientPostback);
                    propertyMap.TransferFirstRequest       = bindAttribute.Direction.HasFlag(Direction.ServerToClientFirstRequest);
                    propertyMap.TransferToServer           = bindAttribute.Direction.HasFlag(Direction.ClientToServerNotInPostbackPath) || bindAttribute.Direction.HasFlag(Direction.ClientToServerInPostbackPath);
                    propertyMap.TransferToServerOnlyInPath = !bindAttribute.Direction.HasFlag(Direction.ClientToServerNotInPostbackPath) && propertyMap.TransferToServer;
                }

                var viewModelProtectionAttribute = property.GetCustomAttribute <ProtectAttribute>();
                if (viewModelProtectionAttribute != null)
                {
                    propertyMap.ViewModelProtection = viewModelProtectionAttribute.Settings;
                }

                var clientExtenderList = property.GetCustomAttributes <ClientExtenderAttribute>();
                if (clientExtenderList.Any())
                {
                    clientExtenderList
                    .OrderBy(c => c.Order)
                    .ToList()
                    .ForEach(extender => propertyMap.ClientExtenders.Add(new ClientExtenderInfo()
                    {
                        Name = extender.Name, Parameter = extender.Parameter
                    }));
                }

                var validationAttributes = validationMetadataProvider.GetAttributesForProperty(property);
                propertyMap.ValidationRules = validationRuleTranslator.TranslateValidationRules(property, validationAttributes).ToList();

                yield return(propertyMap);
            }
        }
示例#3
0
        /// <summary>
        /// Populates the view model from the data received from the request.
        /// </summary>
        /// <returns></returns>
        public void PopulateViewModel(IDotvvmRequestContext context, string serializedPostData)
        {
            // get properties
            var     data = context.ReceivedViewModelJson = JObject.Parse(serializedPostData);
            JObject viewModelToken;

            if (data["viewModelCacheId"] != null)
            {
                if (!context.Configuration.ExperimentalFeatures.ServerSideViewModelCache.IsEnabledForRoute(context.Route.RouteName))
                {
                    throw new InvalidOperationException("The server-side viewmodel caching is not enabled for the current route!");
                }

                viewModelToken    = viewModelServerCache.TryRestoreViewModel(context, (string)data["viewModelCacheId"], (JObject)data["viewModelDiff"]);
                data["viewModel"] = viewModelToken;
            }
            else
            {
                viewModelToken = (JObject)data["viewModel"];
            }

            // load CSRF token
            context.CsrfToken = viewModelToken["$csrfToken"]?.Value <string>();

            ViewModelJsonConverter viewModelConverter;

            if (viewModelToken["$encryptedValues"] != null)
            {
                // load encrypted values
                var encryptedValuesString = viewModelToken["$encryptedValues"].Value <string>();
                viewModelConverter = new ViewModelJsonConverter(context.IsPostBack, viewModelMapper, context.Services, JObject.Parse(viewModelProtector.Unprotect(encryptedValuesString, context)));
            }
            else
            {
                viewModelConverter = new ViewModelJsonConverter(context.IsPostBack, viewModelMapper, context.Services);
            }

            // get validation path
            context.ModelState.ValidationTargetPath = data.SelectToken("additionalData.validationTargetPath")?.Value <string>();

            // populate the ViewModel
            var serializer = CreateJsonSerializer();

            serializer.Converters.Add(viewModelConverter);
            try
            {
                viewModelConverter.Populate(viewModelToken.CreateReader(), serializer, context.ViewModel);
            }
            catch (Exception ex)
            {
                throw new Exception($"Could not deserialize viewModel of type { context.ViewModel.GetType().Name }. {GeneralViewModelRecommendations}", ex);
            }
        }
        /// <summary>
        /// Builds the view model for the client.
        /// </summary>
        public void BuildViewModel(DotvvmRequestContext context)
        {
            // serialize the ViewModel
            var serializer = CreateJsonSerializer();
            var viewModelConverter = new ViewModelJsonConverter(context.IsPostBack, viewModelMapper)
            {
                UsedSerializationMaps = new HashSet<ViewModelSerializationMap>()
            };
            serializer.Converters.Add(viewModelConverter);
            var writer = new JTokenWriter();
            try
            {
                serializer.Serialize(writer, context.ViewModel);
            }
            catch (Exception ex)
            {
                throw new Exception($"Could not serialize viewModel of type { context.ViewModel.GetType().Name }. Serialization failed at property { writer.Path }. {GeneralViewModelRecomendations}", ex);
            }

            // persist CSRF token
            writer.Token["$csrfToken"] = context.CsrfToken;

            // persist encrypted values
            if (viewModelConverter.EncryptedValues.Count > 0)
                writer.Token["$encryptedValues"] = viewModelProtector.Protect(viewModelConverter.EncryptedValues.ToString(Formatting.None), context);

            // serialize validation rules
            var validationRules = SerializeValidationRules(viewModelConverter);

            // create result object
            var result = new JObject();
            result["viewModel"] = writer.Token;
            result["url"] = context.OwinContext.Request.Uri.PathAndQuery;
            result["virtualDirectory"] = DotvvmMiddleware.GetVirtualDirectory(context.OwinContext);
            if (context.ResultIdFragment != null)
            {
                result["resultIdFragment"] = context.ResultIdFragment;
            }
            if (context.IsPostBack || context.IsSpaRequest)
            {
                result["action"] = "successfulCommand";
                var renderedResources = new HashSet<string>(context.ReceivedViewModelJson?["renderedResources"]?.Values<string>() ?? new string[] { });
                result["resources"] = BuildResourcesJson(context, rn => !renderedResources.Contains(rn));
            }
            else
            {
                result["renderedResources"] = JArray.FromObject(context.ResourceManager.RequiredResources);
            }
            // TODO: do not send on postbacks
            if (validationRules.Count > 0) result["validationRules"] = validationRules;

            context.ViewModelJson = result;
        }
        /// <summary>
        /// Serializes the validation rules.
        /// </summary>
        private JObject SerializeValidationRules(ViewModelJsonConverter viewModelConverter)
        {
            var validationRules = new JObject();

            foreach (var map in viewModelConverter.UsedSerializationMaps)
            {
                var rule = new JObject();
                foreach (var property in map.Properties.Where(p => p.ClientValidationRules.Any()))
                {
                    rule[property.Name] = JToken.FromObject(property.ClientValidationRules);
                }
                if (rule.Count > 0)
                {
                    validationRules[map.Type.ToString()] = rule;
                }
            }
            return(validationRules);
        }
        public string BuildStaticCommandResponse(IDotvvmRequestContext context, object result)
        {
            var serializer         = CreateJsonSerializer();
            var viewModelConverter = new ViewModelJsonConverter(context.IsPostBack, viewModelMapper)
            {
                UsedSerializationMaps = new HashSet <ViewModelSerializationMap>()
            };

            serializer.Converters.Add(viewModelConverter);
            var writer = new JTokenWriter();

            try
            {
                serializer.Serialize(writer, result);
            }
            catch (Exception ex)
            {
                throw new Exception($"Could not serialize viewModel of type { context.ViewModel.GetType().Name }. Serialization failed at property { writer.Path }. {GeneralViewModelRecommendations}", ex);
            }
            return(writer.Token.ToString(JsonFormatting));
        }
        /// <summary>
        /// Populates the view model from the data received from the request.
        /// </summary>
        /// <returns></returns>
        public void PopulateViewModel(IDotvvmRequestContext context, string serializedPostData)
        {
            // get properties
            var data           = context.ReceivedViewModelJson = JObject.Parse(serializedPostData);
            var viewModelToken = (JObject)data["viewModel"];

            // load CSRF token
            context.CsrfToken = viewModelToken["$csrfToken"].Value <string>();

            ViewModelJsonConverter viewModelConverter;

            if (viewModelToken["$encryptedValues"] != null)
            {
                // load encrypted values
                var encryptedValuesString = viewModelToken["$encryptedValues"].Value <string>();
                viewModelConverter = new ViewModelJsonConverter(context.IsPostBack, viewModelMapper, JObject.Parse(viewModelProtector.Unprotect(encryptedValuesString, context)));
            }
            else
            {
                viewModelConverter = new ViewModelJsonConverter(context.IsPostBack, viewModelMapper);
            }

            // get validation path
            context.ModelState.ValidationTargetPath = data["validationTargetPath"].Value <string>();

            // populate the ViewModel
            var serializer = CreateJsonSerializer();

            serializer.Converters.Add(viewModelConverter);
            try
            {
                viewModelConverter.Populate(viewModelToken, serializer, context.ViewModel);
            }
            catch (Exception ex)
            {
                throw new Exception($"Could not deserialize viewModel of type { context.ViewModel.GetType().Name }. {GeneralViewModelRecommendations}", ex);
            }
        }
        /// <summary>
        /// Builds the view model for the client.
        /// </summary>
        public void BuildViewModel(IDotvvmRequestContext context)
        {
            // serialize the ViewModel
            var serializer         = CreateJsonSerializer();
            var viewModelConverter = new ViewModelJsonConverter(context.IsPostBack, viewModelMapper)
            {
                UsedSerializationMaps = new HashSet <ViewModelSerializationMap>()
            };

            serializer.Converters.Add(viewModelConverter);
            var writer = new JTokenWriter();

            try
            {
                serializer.Serialize(writer, context.ViewModel);
            }
            catch (Exception ex)
            {
                throw new Exception($"Could not serialize viewModel of type { context.ViewModel.GetType().Name }. Serialization failed at property { writer.Path }. {GeneralViewModelRecommendations}", ex);
            }

            // persist CSRF token
            writer.Token["$csrfToken"] = context.CsrfToken;

            // persist encrypted values
            if (viewModelConverter.EncryptedValues.Count > 0)
            {
                writer.Token["$encryptedValues"] = viewModelProtector.Protect(viewModelConverter.EncryptedValues.ToString(Formatting.None), context);
            }

            // serialize validation rules
            bool useClientSideValidation = context.Configuration.ClientSideValidation;
            var  validationRules         = useClientSideValidation ?
                                           SerializeValidationRules(viewModelConverter) :
                                           null;

            // create result object
            var result = new JObject();

            result["viewModel"]        = writer.Token;
            result["url"]              = context.HttpContext.Request.Url.PathAndQuery;
            result["virtualDirectory"] = context.HttpContext.Request.PathBase.Value?.Trim('/') ?? "";
            if (context.ResultIdFragment != null)
            {
                result["resultIdFragment"] = context.ResultIdFragment;
            }
            if (context.IsPostBack || context.IsSpaRequest)
            {
                result["action"] = "successfulCommand";
                var renderedResources = new HashSet <string>(context.ReceivedViewModelJson?["renderedResources"]?.Values <string>() ?? new string[] { });
                result["resources"] = BuildResourcesJson(context, rn => !renderedResources.Contains(rn));
            }
            else
            {
                result["renderedResources"] = JArray.FromObject(context.ResourceManager.RequiredResources);
            }
            // TODO: do not send on postbacks
            if (validationRules?.Count > 0)
            {
                result["validationRules"] = validationRules;
            }

            context.ViewModelJson = result;
        }
        /// <summary>
        /// Populates the view model from the data received from the request.
        /// </summary>
        /// <returns></returns>
        public void PopulateViewModel(DotvvmRequestContext context, string serializedPostData)
        {

            // get properties
            var data = context.ReceivedViewModelJson = JObject.Parse(serializedPostData);
            var viewModelToken = (JObject)data["viewModel"];

            // load CSRF token
            context.CsrfToken = viewModelToken["$csrfToken"].Value<string>();

            ViewModelJsonConverter viewModelConverter;
            if (viewModelToken["$encryptedValues"] != null)
            {
                // load encrypted values
                var encryptedValuesString = viewModelToken["$encryptedValues"].Value<string>();
                viewModelConverter = new ViewModelJsonConverter(context.IsPostBack, viewModelMapper, JObject.Parse(viewModelProtector.Unprotect(encryptedValuesString, context)));
            }
            else viewModelConverter = new ViewModelJsonConverter(context.IsPostBack, viewModelMapper);

            // get validation path
            context.ModelState.ValidationTargetPath = data["validationTargetPath"].Value<string>();

            // populate the ViewModel
            var serializer = CreateJsonSerializer();
            serializer.Converters.Add(viewModelConverter);
            try
            {
                viewModelConverter.Populate(viewModelToken, serializer, context.ViewModel);
            }
            catch (Exception ex)
            {
                throw new Exception($"Could not deserialize viewModel of type { context.ViewModel.GetType().Name }. {GeneralViewModelRecomendations}", ex);
            }
        }
 /// <summary>
 /// Serializes the validation rules.
 /// </summary>
 private JObject SerializeValidationRules(ViewModelJsonConverter viewModelConverter)
 {
     var validationRules = new JObject();
     foreach (var map in viewModelConverter.UsedSerializationMaps)
     {
         var rule = new JObject();
         foreach (var property in map.Properties.Where(p => p.ClientValidationRules.Any()))
         {
             rule[property.Name] = JToken.FromObject(property.ClientValidationRules);
         }
         if (rule.Count > 0) validationRules[map.Type.ToString()] = rule;
     }
     return validationRules;
 }