Web API Model Validation - Error Message is empty - asp.net-web-api2

I'm using standard model validation attributes (e.g. Required) as a first line of defense against invalid model, later on I have additional checks that can not be done by using standard model validation attributes.
I have one ActionFilterAttribute where I check for model state, null model properties and so on..
What is strange here is the following
in method OnActionExecuting we have HttpActionContext and there is ModelState property which has property IsValid indicating if model is valid or not (excluding cases when property of model is null, but that I cover later on...).
So, when the model is not valid, than actionContext.ModelState.First().Value.Errors (collection of ModelError) contains all model validation errors. So, ModelError contains ErrorMessage and Exception attributes, and when I check for each of them, ErrorMessage is not initialized but Exception property is initialized and contains details of validation failure. Further more, ModelError class has 3 constructors:
public ModelError(Exception exception);
public ModelError(string errorMessage);
public ModelError(Exception exception, string errorMessage);
So it seems that for some reason the first one is called by framework, instead of the last one.
Any ideas why this happens? What can I do in order to get ErrorMessage Initialized?
Regards,
Novak

Related

Why is my complex [FromBody] parameter null?

I am having trouble with the [FromBody] parameter of my method not binding.
example C#:
[Route("api/path")]
[HttpPost]
public void Post([FromBody] ComplexType param)
{
// param is null
}
public class ComplexType
{
public string name { get; set;}
}
I've checked the POST body content and content-type and it looks correct.
Why is it null despite throughly checking that the data being posted and content type all match what is expected?
N.B. This is a deliberatly vague question since I was having a lot of trouble diagnosing an issue and I couldn't find a suitable question and answer.
When I eventually found the problem I kicked myself for it, but I feel the need to share how I found the problem to hopefully spare others the pain.
As it happens there may well be nothing wrong with the example given.
In my case there was a problem with the definition of the complex type, I had a parameter marked as string while it should have been string[] and so the JSON parsed did not match the model.
The important part though is how I found this out:
When debugging any API method there is the magic ModelState property.
This property gives you information about any failures that occur while binding the data received to the expected parameters.
e.g:
here we can see the parameter (uploaded), and the property within that parameter which failed to bind correctly.
Check the definition of that property and you'll probably find an error.

How to create a custom validator in ASP.NET Core that fires for invalid input too?

I have created a custom validator for a DateTime field in ASP.NET Core 3.1 as shown below:
[CustomDate]
public DateTime DOB { get; set; }
public class CustomDate : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
//… some logic
}
}
However, my problem is that this custom validator fires only when I put a date value in the text box control. It does not fire for invalid inputs like e.g. when I put a string 'aaa' in the text box.
My question is how to make this custom validator fire even for invalid inputs like 'string', etc.
The reason is I want to make this custom validator replace [Required], [ReqularExpression], etc. A sort of 'One ring (validator) to rule them all'. How can I achieve that?
TL;DR: When you submit a value that can't be converted to DateTime, model binding fails. Since there is already a validation error associated with the property, subsequent validation—including your CustomDate validator—doesn't fire. Your property is still getting validated, however: If you enter a value of aaa, ModelState.IsValid will return false.
The code you had originally posted should be working fine—but I suspect it's not working the way you're expecting it to. Most notably, your confusion likely stems from the following statement:
"…this custom validator fires only when I put a date value in the text box control."
That is also true! Let me walk through the process.
Original Code
To help illustrate this, I hope you don't mind me resurrecting your original code sample, as it's useful to have concrete reference to work off of.
[CustomDate]
public DateTime DOB { get; set; }
public class CustomDate : Attribute, IModelValidator
{
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
{
if (Convert.ToDateTime(context.Model) > DateTime.Now)
return new List<ModelValidationResult> {
new ModelValidationResult("", "Invalid - future date")
};
else if (Convert.ToDateTime(context.Model) < new DateTime(1970, 1, 1))
return new List<ModelValidationResult> {
new ModelValidationResult("", "Invalid - date is less than 1970 year")
};
else
return Enumerable.Empty<ModelValidationResult>();
}
}
Validation Process
Before I walk through the process, there are four underlying considerations that are important to be aware of here:
Model binding occurs before model validation.
Model binding will raise its own validation errors if binding fails.
Validation attributes are only evaluated on properties that remain IsValid.
The ModelValidationContext.Model property is typed to the validated property—so, in this case, a DateTime value.
Use Case #1: Invalid Value
Given these considerations, here's what's happening when you submit a value of e.g. aaa in the field mapped to your validated DOB property:
The model binder attempts to bind a value of aaa to a DateTime property.
The model binder fails, adding a ModelError to your ModelStateDictionary.
Your CustomDate validator never fires because the field has already failed validation.
Use Case #2: Missing Value
It's instructive to look at another test case. Instead of putting in aaa, just don't put a value in at all. In this case, the process looks a bit different:
The model binder doesn't find a value for your DateTime property, so no binding occurs.
Your model's property initializes to DateTime's default value of 0001-01-01 00:00:00.
Your CustomDate validator fires, adding a ModelError because "Invalid - date is less than 1970 year".
Analysis
As you can see above, it is true that your CustomDate validator isn't firing when a bogus date is submitted. But that doesn't mean that validation isn't occurring. Instead, validation has already happened and failed. If you enter a valid date—or don't enter a date at all—then a model binding error won't occur, and your CustomDate validator will be executed as expected.
Revisiting Your Question
"How to make this custom validator to fire even for invalid inputs like 'string' etc."
Ultimately, I haven't answered that question. But I think my answer will explain why that's happening, and why your input is still getting validated despite that. Keep in mind that even if your CustomDate validator did fire, it would act the same as if you hadn't submitted a value at all, since the context.Model value would have defaulted to 0001-01-01 00:00:00. The main difference is that you're not getting the same error message, since the error is coming from a different source.
Forcing Validation
I don't recommend this, but if you really wanted your CustomDate validator to fire, you could apply it to a string property instead. In that case, model binding wouldn't fail and your CustomDate validator would still get called. If you pursue this, you'll need to put in additional validation to ensure that the date is in the correct format (e.g., by preempting or handling InvalidFormatExceptions). But, of course, your date would be stored as a string, which likely isn't what you want.
Code Suggestions
This is a bit outside the scope of your original question, but while I'm here I'd recommend the following:
You won't need to do a Convert.ToDateTime() in your validator; your context.Model field is already a DateTime. You just need to cast it back to a DateTime object (e.g., (DateTime)context.Model) so your code knows that.
At minimum, you should consider using <input type="date" /> (reference) which, on most browsers, will restrict input to a correct date while also providing a basic date picker.
Alternatively, there are a number of more sophisticated date/time controls written in JavaScript that you might consider implementing if you require more control over the presentation and client-side validation.

FileHelpers and data type exception

I'm using FileHelpers library to import a tab delimited file. I have enabled the ErrorMode.SaveAndContinue. The issue I'm facing is that when particular fields has invalid data type
[FieldConverter(ConverterKind.Decimal)]
public decimal? Price;
(e.g decimal? Price field has string value in it xxxxxx) in a single record, the library only checks against the first field value and throws internal exception which is logged into the ErrorManager.Errors.
I need to check the remaining fields as well on the same row and log their errors as well in the same cycle.
How can I do that?
** Please note that I have tried to writing CustomConverter but again I need to throw ConvertException so that it gets catched by ErrorManager and it just moves to next row.
The AfterReadRecord does not gets called because of the exception
That behavior is by design from the first versions of the library, the exception is throw when found the problem or the error is logged if ErrorManager is present
The best way to solve your problem is to use a string field for Price and later do the validations you need in the AfterReadRecord method
For example
public class YourClass: INotifyRead
{
[FieldConverter(ConverterKind.Decimal)]
public string Price;
void AfterReadRecord (...)
// Validate inside this method
}

$model->validate() returns true without any attribute assignments?

I have a model with many attributes, I want to validate before saving it. So at some I would have only model initialization but no attribute assignments
$model=new patient(); $model->validate();
yet it returns true, I have many attributes set to required. I don't understand how does this happen?
Is there a way to validate a model before saving it even though no attributes are assigned to the model?
Your have validation rules but You may Miss the scenario's on validate .
Check the validators on your current object model by this
print_r($model->validatorList);
refer this link
You can validate a model at any time once it is created.
Let's look at the sequence of events :-
You have created a model class :
class User extends CActiveRecord
{
...
}
This creates a template for the patient class, which inherits from CActiveRecord, which means it has inherited functions available to it. So, while your class files does not the activate() function, it inherits it from the CActiveRecord class.
Now you create an instance of the class
$patient1 = new patient;
This means there is now some space in memory that exists for patient1. It has values. Some may be empty and some not (for example, they may have a default value).
So, at any time, you could
$model->validate();
This would change the model to include the error messages. To get the errors that have been raise, use
$allErrors = $model->getErrors()
print_r($all_errors); // This will show you the structure.
To print the errors in a neat way, use
echo CHtml::errorSummary($model);

How to check if field exists that has isRequired set to true in WCF

I have a DataMember(isRequired:=True) that property is called FillerInd. Now how do you tell if that field is empty or does not exist? I was reading online and you have to do some Serialization and Deserialization of XML if it has the field required missing it will throw the exception MissingMethodException. I have no idea of how to implement this or if this is the best route to take. Any help to put me into correct path I would really appreciate it.
The DataMember property IsRequired tells you whether the property is required on the wire: whether it should be present in the message or not. A typical situation where this would cause exceptions is when the service updates the DataContract by adding a property with IsRequired = true, without notifying the clients. The clients will send serialized objects that don't include the property, causing an exception to be thrown.
A less typical situation but easy way to reproduce this problem using a property of a basic type is to explicitly set the EmitDefaultValue property to false. If you do this, and then try to call an operation with an object of that DataContract, an exception may be thrown. Consider this example:
// Data Contract
[DataContract]
public class Animal
{
[DataMember(IsRequired = true, EmitDefaultValue = false)]
public string Name;
}
// IService
[OperationContract]
int GetIdentifier(Animal animal);
// Client operation
int id = client.GetIdentifier(new Animal()); // Causes exception
This will cause a SerializationException (in turn causing a CommunicationException) with a message along these lines:
Member Name in type ....Animal cannot be serialized. This exception is usually caused by trying to use a null value where a null value is not allowed. The 'Name' member is set to its default value (usually null or zero). The member's EmitDefault setting is 'false', indicating that the member should not be serialized. However, the member's IsRequired setting is 'true', indicating that it must be serialized. This conflict cannot be resolved. Consider setting 'Name' to a non-default value. Alternatively, you can change the EmitDefaultValue property on the DataMemberAttribute attribute to true, or changing the IsRequired property to false.
Note that if you bypass this issue client side (e.g. by editing the reference.cs for a generated service reference so that the DataMember is not required anymore on the client side), then the service will encounter an exception when deserializing, which will cause a Fault, I believe.
Turning to your specific remarks and questions.
You don't have to do anything to tell if the field is empty: the WCF framework handles this for you. If needed I suppose you could hook into the (de)serialization to get the heads up on this issue, or even inspect the message before it's handled.
As I said, this situation will cause SerializationExceptions and CommunicationExceptions, not a MissingMethodException.
Whether you need "IsRequired" and whether it's the "best route to take" I wouldn't know. This depends on your situation, of course.