Пишем код

Заметки о .net разработке

Локализация сообщений о переполнении int в MVC3

without comments

Довольно давно я уже писал о локализации MVC3-сайтов, уделив достаточно большое внимание способу перевода валидационных сообщений на другие языки.

Однако недавно в проекте я столкнулся с одной ошибкой валидации, перевести которую оказалось не так просто. Вот пример этой ошибки:

Как нетрудно заметить, я просто ввел в числовое поле очень большое значение. При этом сам факт возникновения ошибки меня вполне устраивал (большие значение обрабатывать и не планировалось), а вот текст на «иностранном» тестеров слегка смутил :)


Stackoverflow по этому поводу ничего не ответил, поэтому пришлось искать решение своими силами.

Более простых способов, чем перегрузить model-binder для числовых значений мне в голову с ходу не пришло. Таким образом решение выродилось в модел-байндер и регистрацию его в global.asax:

//регистрация где-нибудь в районе Application_Start:
System.Web.Mvc.ModelBinders.Binders.Add(typeof(int), new NumericModelBinder());
System.Web.Mvc.ModelBinders.Binders.Add(typeof(int?), new NumericModelBinder());
System.Web.Mvc.ModelBinders.Binders.Add(typeof(byte), new NumericModelBinder());
System.Web.Mvc.ModelBinders.Binders.Add(typeof(byte?), new NumericModelBinder());
//...

//Собственно байндер:
    /// <summary>
    /// выдает локализованные ошибки о переполнении (слишком большое число, введенное в поле int)
    /// </summary>
    public class NumericModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            double parsedValue;
            if (double.TryParse(value.AttemptedValue, out parsedValue))
            {
                double minValue = 0;
                double maxValue = 0;
                var modelType = Nullable.GetUnderlyingType(bindingContext.ModelType) ?? bindingContext.ModelType;
                if (modelType == typeof(byte))
                {
                    minValue = byte.MinValue;
                    maxValue = byte.MaxValue;
                }
                if (modelType == typeof(int))
                {
                    minValue = int.MinValue;
                    maxValue = int.MaxValue;
                }
                if (modelType == typeof(short))
                {
                    minValue = short.MinValue;
                    maxValue = short.MaxValue;
                }
                if (modelType == typeof(long))
                {
                    minValue = long.MinValue;
                    maxValue = long.MaxValue;
                }
                if (modelType == typeof(Int64))
                {
                    minValue = Int64.MinValue;
                    maxValue = Int64.MaxValue;
                }
                if ((minValue != 0 || maxValue != 0) && (parsedValue < minValue || parsedValue > maxValue))
                {
                    var error = GetUserResourceString(controllerContext, "PropertyValueInvalid") ?? "The value '{0}' is invalid.";
                    bindingContext.ModelState.AddModelError(bindingContext.ModelName, string.Format(error, value.AttemptedValue, bindingContext.ModelMetadata.DisplayName));
                }
            }
            return base.BindModel(controllerContext, bindingContext);
        }

        private static string GetUserResourceString(ControllerContext controllerContext, string resourceName)
        {
            string result = null;

            if (!String.IsNullOrEmpty(ResourceClassKey) && (controllerContext != null) && (controllerContext.HttpContext != null))
            {
                result = controllerContext.HttpContext.GetGlobalResourceObject(ResourceClassKey, resourceName, CultureInfo.CurrentUICulture) as string;
            }

            return result;
        }
    }

Локализованные строки берутся, по аналогии с предыдущим постом, из соответствующего ресурса. В простых случаях можно, конечно, и просто «захардкодить» текст прямо внутри байндера, избавившись от функции `GetUserResourceString`.

Будет любопытно узнать, если кому-то еще это пригодиться :)
P.S. Чтобы добавить к этой ошибке еще и клиентскую валидацию, придется подправлять дефолтные адаптеры для jquery.validation. Мне это в данном случае показалось излишним, и для простоты последующих обновлений я решил не модифицировать jquery.validation а ограничиться в данном случае серверной валидацией.

Опубликовать в Facebook
Опубликовать в Google Plus

Written by Shaddix

Июнь 23rd, 2012 at 6:31 пп

Posted in .net,MVC

Leave a Reply