Uppercase attribute that converts the input to uppercase

时光总嘲笑我的痴心妄想 提交于 2019-11-29 06:57:13

I have managed to get this working, to a point, so here's my solution for others to appraise.

Once point to note was that the full solution couldn't be achieved because I couldn't get the Modelmetadata inside the StringValidation.IsValid() attribute. The particular issue I had here was that I could get the Metadata, however I could not get the PropertyName from it, only the DisplayName. There were multiple options out there, but the fact that some of my properties have the same DisplayName means that I couldn't be sure that the ProprtyName was the one I was actually validating.

Here's the code for the ValidationAttribute:

public class StringValidationAttribute : ValidationAttribute, IClientValidatable, IMetadataAware {

    private bool _uppercase;

    public StringValidationAttribute(bool uppercase = false) {
         _uppercase = uppercase;
    }

    ...

    public void OnMetadataCreated(ModelMetadata metadata)
    {
        metadata.AdditionalValues["Uppercase"] = _uppercase;
    }
}

I then created a new IModelBinder implementation:

public class StringBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {           
        ValueProviderResult result = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

        if (result == null)
            return null;

        if (bindingContext.ModelMetadata.AdditionalValues.ContainsKey("Uppercase")) {
            if ((bool)bindingContext.ModelMetadata.AdditionalValues["Uppercase"]])
                return result.AttemptedValue.ToUpper();
        }

        return result.AttemptedValue;
    }
}

And registered that in myGlobal.asax file:

ModelBinders.Binders.Add(typeof(string), new StringBinder());

The code so far will cause any string input coming into MVC to be converted to Uppercase if it has StringValidationAttribute attached to it on the model, and where the uppercase indicator has been set.

Next, to achieve my desire of making the html forms be uppercase too, I implemented a new EditorTemplate named string.cshtml. In this view I added:

RouteValueDictionary htmlAttributes = new RouteValueDictionary();
if ((bool)ViewData.ModelMetadata.AdditionalValues["Uppercase"]) {
    htmlAttributes.Add("class", "Uppercase");
}
@Html.TextBox("", Model, htmlAttributes)

With the CSS as;

.Uppercase {
    text-transform: uppercase;
}

Hope this post helps some others out there.

For Web API purpose it is better to convert the incoming json to uppercase or lowercase.

    public class ToUpperCase : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(string);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            return reader.Value.ToString().ToUpper();
        }            
    }



    [Display(Name = "PNR NAME")]
    [JsonConverter(typeof(Annotations.ToUpperCase))]
    public string PNR { get; set; }

OR Globally;

  protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        //.......... others



        JsonMediaTypeFormatter jsonFormatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
        JsonSerializerSettings jSettings = new Newtonsoft.Json.JsonSerializerSettings();
        jSettings.Converters.Add(new UpperCaseStringConverter());
        jsonFormatter.SerializerSettings = jSettings;
    }

You're right, ValidationAttribute is not the right fit. It seems like doing this at the Model Binding stage would be a better idea. See this article for a detailed explanation of how to customize this behavior.

Based on the information provided there, I believe you should be able to create an attribute based on CustomModelBinderAttribute like this:

[AttributeUsage(AttributeTargets.Property)]
public class UppercaseAttribute : CustomModelBinderAttribute
{
    public override IModelBinder GetBinder()
    {
        return new UppercaseModelBinder();
    }

    private class UppercaseModelBinder : DefaultModelBinder
    {
        public override object BindModel(
            ControllerContext controllerContext,
            ModelBindingContext bindingContext)
        {
            var value = base.BindModel(controllerContext, bindingContext);
            var strValue = value as string;
            if (strValue == null)
                return value;
            return strValue.ToUpperInvariant();
        }
    }
}

I have not tested this. Let me know if it works or not.

NOTE: I'm adding on to this post because until I discovered the approach I now use, I read this and tried all above unsuccessfully.

I generally use a two part process when dealing with forcing text data to be formatted as uppercase. 1. at the view and 2. at the controller

  1. At the view layer so that the user knows data is going to be used in the uppercase form. This can be down through htmlAttributes used in the EditorFor HTML helper.

    @HTML.EditorFor(model => model.Access_Code, new { htmlAttributes = new  Style= "text-transform:uppercase"}})
    

Now this only forces the data seen and entered by the user to uppercase and not the data sent to the server. To do that requires some code in the associated method in the controller.

  1. I add the ToUpper() method to the target attribute of the object being passed back to the contoller. Here is hypothetical example showing this.

    public ActionResult verify(int? id)
          {
            var userData = db.user.Where (i=> i.userID == id).Single();
    
            userData.Access_Code = userData.Access_Code.ToUpper();
    
       ...
     }
    
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!