I create the following attribute:
public class SpecificDataTypeAttribute : DataTypeAttribute
{
public SpecificDataType(DataType dataType, string field)
: base(dataType)
{
this.ErrorMessage = string.Format("{0} {1}", field, Messages.SpecificDataTypeAttribute);
}
}
And use like:
[SpecificDataType(DataType.DateTime, "Initial date")]
public DateTime? InitialDate { get; set; }
So, the message that is in Messages.SpecificDataTypeAttribute is "is in a incorrect format.". When i input a wrong date in InitialDate, i got the default error: "The value '12' is not valid for InitialDate.". Why? I put the breakpoint and the code is calling the SpecificDataType ctor.
You are going in wrong direction - in asp.net mvc, DataTypeAttribute does not define validation rules. It is more or less like UIHintAttribute - helps to specify which template to use when rendering property in edit or display modes.
Take a look at this answer to learn about customizing validation messages for system types
The value for PropertyValueInvalid is formatted, with {0} replaced by invalid value, and {1} with property name. So you can define it as
{1} is in invalid format
In MVC 4 is possible to localize default error message. http://weblogs.asp.net/imranbaloch/archive/2013/03/31/localizing-default-error-messages-in-asp-net-mvc-and-web-form.aspx
Related
Have a Json API model with a date property defined as:
[Required]
[DataType(DataType.Date, ErrorMessage = "Invalid expiry date")]
public DateTime ExpiryDate { get; set; }
when posting an incorrect value for the ExpiryDate, examples:
"ExpiryDate":"2020-02-31T00:00:00",
"ExpiryDate":"2020-01-99T00:00:00",
"ExpiryDate":"abc",
the ModelState.Values.Errors[0].ErrorMessage is empty. Instead there is Model exception that I can not return to the API consumer, looks ugly.
ModelState.Values.Errors[0].Exception = {"Could not convert string to DateTime: 2020-02-31T00:00:00. Path 'CardDetails.ExpiryDate', line 13, position 39."}
My question are: how can I make the data annotation generate an error instead of an exception? Why is not the current data annotation giving an error, is not the job of [DataType(DataType.Date...] to do that?
The main issue here is that a DateTime value in JSON (at least according to the JSON parser) has a specific format, and not following that format is a parsing error, which is what's currently happening.
I think you'll have to take the value in as a string and do a conversion and validation on top of it. There's a couple of options. One is a custom ValidationAttribute like #erikscandola mentioned. Another is to implmenent IValidatableObject interface on your model.
You could also convert the model property to a string and simply do a check in the controller action:
DateTime expiryDate;
if (!DateTime.TryParse(model.ExpiryDate, out expiryDate))
{
ModelState.AddModelError("", string.Format(
"The given ExpiryDate '{0}' was not valid", model.ExpiryDate));
}
The approach depends upon how much reuse you need of the validation logic.
You should create a custom data annotation:
public class RequiredDateTimeAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
// Here your code to check date in value
}
}
We have the following
public class Model
{
[Required]
public decimal Cost { get;set; }
}
When we receive an invalid value, such as "dasd" or whatever else, I return a BadRequest:
public HttpResponseMessage Post(Model data)
{
if (!ModelState.IsValid)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
But in the response, the json looks like:
{
"message": "The request is invalid.",
"modelState": {
"data.cost": ["Could not convert string to decimal: dasd. Path 'cost', line 1, position 181."],
}
}
First of all, how can I get rid of the data. prefix?
Also, I would like to implement a custom validator, something like MustBeDecimal attribute that could allow me to return more userfriendly error
message.
The Web API framework binds "dasd" to a decimal and it fails because it is not possible to convert "dasd" to decimal. Obviously, binding has to happen before validation. If you change decimal to string, binding will be okay and your regex validation will run and ModelState will be invalid but in this case also, the prefix that do you do not want to be present will be present.
The Validate method of DefaultBodyModelValidator has this keyPrefix parameter, which is used to create the prefix. Unfortunately, this method is not marked virtual for you to override. So, you will need to do something like this (see the first part).
You can set an errormessage on the RegularExpression data-annotation attribute. Is there any reason you can't use this?
public class Model
{
[Required]
[RegularExpression(#"^\d+.\d{0,2}$",ErrorMessage = "You didn't enter a decimal!")]
public decimal Cost { get;set; }
}
I have an object which has properties decorated with Validation attributes. My validation is working correctly, however I need an additional method which performs string manipulation based on those attributes.
Consider the following object:
public class Pupil
{
/// <summary>
///
/// </summary>
public Pupil()
{
}
[NotNullValidator]
[StringLengthValidator(0, 54, MessageTemplate = "Invalid value '{0}' for {1}, Max length: {5}")]
public string Name{ get; set; }
[NotNullValidator]
[StringLengthValidator(0, 2, MessageTemplate = "Invalid value '{0}' for {1}, Max length: {5}")]
public string Gender{ get; set; }
}
I want to be able to manipulate the "Name" based on the StringLengthValidator attribute and its arguments. For example:
///Takes a Pupil object in
public static void DoManipulation(object value)
{
foreach(var property in value.GetType().GetProperties())
{
if(property.Name == "Name")
{
var att = property.GetCustomAttributes(typeof(StringLengthValidator), false);
var length = ((StringLengthValidator)att[0]).UpperBound;
}
}
}
The value of "length" is coming up as null, rather than 54. How do I get the value out?
Hopefully this makes sense, thanks.
A
This works for me, are you getting the same StringLengthValidator attribute that you think you are? (is this your custom class or the one from Enterprise Lib?
In my case, I created a custom class StringLengthValidator
The idea behind all this is that the value 54 can be changed, right? Otherwise you could just hard code 54.
Take a look on how to contol validation in the web.config with the tag so you can add the 54 to the web.config and read it from your application
Here is an example, look for the first approach, Rule sets in Configuration
As I was looking for it today with my co-worker and we did not find a proper solution to our problem, here is the answer, for the next one who is in trouble.
The attribute name is StringLengthValidator, but if you check at the class name it is StringLengthValidatorAttribute, so in order to get the proper attribute, you need to call the function GetCustomAttributes this way :
property.GetCustomAttributes(typeof(StringLengthValidatorAttribute), false)
This will fetch correctly the attribute and then you will be able to get the value of UpperBound
Motive : I have a decimal property in a class which I wish to validate so that it adheres to regex "^\d{1,7}.\d{2,7}$"
So i have applied the Regex validator attribute to the property
[RegexValidator(#"^\d{1,7}\.\d{2,7}$"...)]
public Decimal MyDecimalProperty { get; set; }
Then, via propertyproxyvalidator on my asp.net page i have tied a textbox validation to this property type.
<cc1:PropertyProxyValidator ID="MyValidator" runat="server" ControlToValidate="MyTextBox"
PropertyName="MyDecimalProperty" SourceTypeName="Myclass, Mydll"></cc1:PropertyProxyValidator>
At runtime i get this error when validation gets performed:
"Value to validate is not of the
expected type: expected System.String
but got System.Decimal instead."
Any idea how to get around this issue, or an alternate to achieve my motive ?
Regular expressions are built to work on strings, not numeric types. Perhaps you need something like this instead:
public Decimal MyDecimalProperty { get; set; }
[RegexValidator(#"^\d{1,7}\.\d{2,7}$")]
public string MyDecimalPropertyString
{
get
{
return this.MyDecimalProperty.ToString();
}
}
Also, update the PropertyName attribute of the PropertyProxyValidator.
I've tried to override error message when input incorrect data type in input field on HTML form.
For example I have the model like this.
public class Person
{
public string FirstName {get;set;}
public int Age {get;set;}
}
For view, I put text input for Age to get it value.
When type some string in Age text box like 'test' and press submit button.
I got this error message
The value 'xxx' is not valid for Age
However, I want to change this message and try many way. There 's nothing effect this message value.
Please could you help me to solve this problem.
After spending a couple of hours looking around, I see no one really has a suitable answer to this. I have found a solution that works for me so I thought I'd post it.
The problem for me was validating a nullable int. When a non valid value was entered by the user, the default message "The value 'dsfd' is not valid for Issue'.
The solution I came up with was to remove the errors from the model state and add my own.
The classes to perform the validation are below:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false)]
public class ValidInteger : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null || value.ToString().Length == 0)
{
return ValidationResult.Success;
}
int i;
return !int.TryParse(value.ToString(), out i) ? new ValidationResult(ErrorMessage) : ValidationResult.Success;
}
}
public class ValidIntegerValidator : DataAnnotationsModelValidator<ValidInteger>
{
public ValidIntegerValidator(ModelMetadata metadata, ControllerContext context, ValidInteger attribute)
: base(metadata, context, attribute)
{
if(!attribute.IsValid(context.HttpContext.Request.Form[attribute.ObjectId]))
{
var propertyName = metadata.PropertyName;
context.Controller.ViewData.ModelState[propertyName].Errors.Clear();
context.Controller.ViewData.ModelState[propertyName].Errors.Add(attribute.ErrorMessage);
}
}
}
Don't forget you'll also need to register the adapter in the global Application_Start()
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(ValidInteger), typeof(ValidIntegerValidator));
And decorate your property with the new attribute
[ValidInteger(ErrorMessage = "Please enter a valid number for issue")]
The response from wechel dated February 27, 12 worked for me, except that I needed to replace the line
if(!attribute.IsValid(context.HttpContext.Request.Form[attribute.ObjectId]))
with
if (!attribute.IsValid(context.HttpContext.Request.Form[metadata.PropertyName]))
I am guessing that the change is needed because I am using MVC4 and the code snippet provided was written in an earlier version of MVC?
You can use DataAnnotations to override the default error messages in MVC, as well as provide your own validation for whatever fields you need to. See the following:
http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx
http://www.asp.net/mvc/tutorials/validation-with-the-data-annotation-validators-cs
If you are using EF, you will need to create a MetaData class off the EF generated class and then add the data annotations. The end of the 2nd article covers how to write these classes for entity framework.
In your specific case you will want to do something like:
using System.ComponentModel.DataAnnotations;
public class Person
{
public string FirstName {get;set;}
[Range(0, 110, ErrorMessage = "<your error message>")]
public int Age {get;set;}
}
UPDATE I did forget one thing that is easy to overlook, you need to include the following JS files for the data annotations to be picked up client side without having to do a post:
MicrosoftAjax.js
MicrosfotMvcValidation.js
These should be stock in the Scripts folder of your project (if you are using MVC 2), and you need to include them either on your page or in your master page,
<script src="<%= Url.Content("~/Scripts/MicrosoftAjax.js") %>" type="text/javascript"></script>
<script src="<%= Url.Content("~/Scripts/MicrosoftMvcValidation.js") %>" type="text/javascript"></script>
you will also need to include
<% Html.EnableClientValidation(); %>
on the page in question.
The client side validation will not let you submit the form until all fields meet validation requirements, as Kaspars Ozols points out you will still need to call Model.IsValid on your controller.
I just wanted to show the Range attribute error message so I used the answer from wechel and Christna and changed it so the RangeAttribute is used. After adding the Validator class, only a custom Validator needs to be created and registered in the global.asax as shown in wechel's answer.
You also need to add a validation message with name "FieldRangeValidation" to your resource bundle. In my project it contains the following text: "Value must be between {0} and {1}"
public class ValidIntegerRangeValidator : DataAnnotationsModelValidator<RangeAttribute>
{
public ValidIntegerRangeValidator(ModelMetadata metadata, ControllerContext context, RangeAttribute attribute)
: base(metadata, context, attribute)
{
try
{
if (attribute.IsValid(context.HttpContext.Request.Form[metadata.PropertyName]))
{
return;
}
}
catch (OverflowException)
{
}
var propertyName = metadata.PropertyName;
context.Controller.ViewData.ModelState[propertyName].Errors.Clear();
context.Controller.ViewData.ModelState[propertyName].Errors.Add(string.Format(Resources.Resources.FieldRangeValidation, attribute.Minimum, attribute.Maximum));
}
}
if (ModelState["Age"].Errors.Count > 0)
{
ModelState["Age"].Errors.Clear();
ModelState["Age"].Errors.Add("<your error message>");
}