Swagger spec's validation overriding FluentValidation - c#

I am having the following problem, and so far haven't been able to find an effective solution for it. I have the following code in my Swagger spec:
SomeRequest:
type: object
properties:
client_date_of_birth:
type: string
format: date
example: "2021-09-17"
required:
- client_date_of_birth
This of course works fine, but the problem is introduced when I try to use FluentValidation instead of this approach. My FluentValidation code looks like this:
.RuleFor(i => i.ClientDateOfBirth)
.NotEmpty().WithMessage("Client's Date of Birth is empty")
.InclusiveBetween(new LocalDate(1870, 01, 01), todayDate);
I wish to display the FluentValidation message instead of the generic one from Swagger, but so far I haven't found a good way to do this.
If I remove client_date_of_birth as a required field, I will also open up the API's contract to allow this field as a null value (which I want to deal with at API level, not FluentValidation level) and if I set it up as a string instead of a date, I will also be opening up the API's contract to potentially unwanted behavior. Is there a way to override this particular behavior of Swagger with the FluentValidation's implementation?
Just as a clarification: I know I will get the FluentValidation error message if I remove the client_date_of_birth as a required field in the Swagger spec. My question is more about: can I override Swagger's behavior with FluentValidation's? the reason I want to keep it in both places is so the rules of the API's contract are transparent and written in the spec itself, rather than obscure and hidden in the code, even if I will be eventually using said code.

Related

How to use C# FluentValidation ValidationContext.RootContextData

I'm new to FluentValidation and am trying to create a validator that accepts some context/parameters at validate time. I've created a custom validator and in the constructor I have something like:
RuleFor(request => request.someField).Custom((request, context) => {
var foo = context.ParentContext.RootContextData["someDependency"];
});
And in the calling code I do:
var validator = new FooValidator();
var context = new ValidationContext<SomeRequest>(request);
context.RootContextData["someDependency"] = someDependency;
validator.Validate(context);
which causes:
System.Collections.Generic.KeyNotFoundException: The given key 'someDependency' was not present in the dictionary.
Any ideas? The reason I want to pass in some context parameters is that they come from the database. If I instead pass that into the validator constructor, then by the time the validate method is called, those context parameters might be out of date. I also don't want to do the fetching from the database in the validator constructor as I will also need to fetch the same data before/after the validate method is called, and database caching is not possible in this scenario, so I'd like to avoid the unnecessary database roundtrips. I've read and am doing what seems to be the same as what is described https://docs.fluentvalidation.net/en/latest/advanced.html#root-context-data
As mentioned in my OP comment, the code looks sound but it's likely failing at the MVC validation pipeline stage and never makes it to your Validate invocation. With the former as it stands you've not added your dependency to the dictionary so it barfs.
There's probably a couple of ways to solve it. My first thought would be to introduce a rule set so this rule only executes server-side as part of your Validate invocation. There's a whole section on rule sets in the doco which covers it pretty well. You may need to combine it with a CustomizeValidator attribute so that the rule set doesn't get executed in the MVC validation pipeline (I've never had to when using a server-side rule set but I've mentioned it for completeness).
The nice thing with this is that you probably won't need to change much of your existing code; you've mentioned you load a number of dependencies into the validation context so it could be a good fit.
Another methodology that looks good, but one that I haven't tried myself, would be to populate the validation context in the BeforeMvcValidation validation interceptor. The value of this option is going to depend on how you gather those dependencies and whether they are used for anything other than validation. It'd probably require more effort than a rule set based on your implementation description as well.

Custom JSON converter for Noda Time

I'm developing for .NET Core, so I'm using the unstable latest alpha of Noda Time (2.0.0-alpha20160729). Data is being handled with JSON.
I will be accepting user input for a date of birth field, and I want to support multiple input formats in case the user can't follow directions gets confused. For example, I want to accept both MM-dd-yyyy and M/d/yy, among others.
The serialization docs specifically state the following:
Custom converters can be created easily from patterns using NodaPatternConverter.
I cannot for the life of me figure out how to do this though. From what I understand, I will need to implement both NodaPatternConverter<LocalDate> itself, IPattern<LocalDate> for parsing, and Action<T> for validation.
I started writing IPattern.Parse<LocalDate>, but that function returns ParseResult<LocalDate> which is apparently inaccessible. It cannot be instantiated or used in any way that I have found. I'm therefore stuck.
How do I properly create a custom JSON converter for Noda Time?
Firstly, this doesn't sound like the right thing to do in a JSON converter. If you're accepting user input directly in your JSON, that should be treated as a string, and parsed later, IMO. JSON is a machine-to-machine format, not a human-to-machine format. Assuming this is a web app, you might want to use moment.js to parse the data at the client and reformat it as ISO-8601. Alternatively, deserialize it as a string and then convert it in your server-side code.
Anyway, for a JSON converter you only need to implement IPattern<LocalDate> - you don't need to implement NodaPatternConverter<LocalDate> as that already exists. You just need:
var pattern = ...;
var converter = new NodaPatternConverter<LocalDate>(pattern);
Now, to implement your pattern, you probably want to actually create it out of existing patterns - write an implementation which delegates to one IPattern<LocalDate> after another until the result is a ParseResult<T> which is successful - or return the final unsuccessful ParseResult<T>. Note that ParseResult<T> isn't inaccessible - but you can't (currently) create your own instance of it. That's something I should probably address, but in this case you don't really need to.
The code you need already exists but isn't exposed - you want the Parse part of CompositePattern. To implement the Format part, you could just use the first of your patterns to format the value... if you even need to.

Custom error format for fluent validation error

I followed Fluent documentation and I haven't found any option to overwrite default error format that is something like "'{propertyName}' must be something.". I have many validators and I won't rewrite them to .WithMessage("...").
Is there any option to change default message format?
I would like to have a similar interface to PropertyNameResolver that offers smart interface to change property name.
You can overwrite the messages globally by modifying the ValidatorOptions.ResourceProviderType as noted on the localization page.
You only have to define the strings that you would like to change as it will use the default string if no override is specified.

FluentValidation : set a default value on error

I'm trying to set a default value to a field with FluentValidation when the provided value is invalid (ex: if the provided language isn't supported, put an error message and default the language to english). I know it's not a good practice to change a value in a validation but in the case of language, I need it to be properly set for further validations and to display the error messages.
How can I do that ?
Thanks
You can't do this within a fluent validation validator. The validator class should only be concerned with providing validation: this is its single responsibility.
To do what you describe, you could perform the validation and then check for the language related error at which point you could set the default value.
Or perhaps you could default the language value prior to validation - it's difficult to make a recommendation without any code.

IDataErrorInfo and the property validation of an object

Since I am trying to learn WPF I see more and more the use of the interface IDataErrorInfo to bind the error to the interface. My problem is that I usually put the validation of the data in the setter of the property and not in a method like IDataErrorInfo.this[string columnName]... Here is a blog I have found that have make me confuse.
What is the good way to proceed in .Net 3.5 to validate data object? Do I need to implement validations in method called by the Setter AND the IDataErrorInfo? Or just the IDataErrorInfo? Or in the setter call the IDataErrorInfo?
Example: I have a firstname string that can have only 3 to 50 chars. Do I put the string validation in the setter (What I would do usually) or now I can simply use the IDataErrorINfo.this method, check the property name and return a String Error when the data is not the good length? I found more intuitive to throw an error in the setter and not using the Interface but most example I see use the IDataErrorInfo interface.
If you throw an exception in the setter, then IDataErrorInfo is redundant since it can't (in theory) get into an illegal state. IDataErrorInfo allows you to accept all input, but tell the user that there is a problem. The nice thing about this is that it allows less interruption to the UI (as the user can continue to enter data even though one field is in error and marked as such), and it is easy to report multiple errors at once - visually, rather than by message-boxes etc.
However, if you go this route you need to be sure to validate that the object is OK before saving it to a database, etc.
You could do this by checking .Error from your business logic (and check that it is null/empty), assuming that you write .Error to report all errors. Or a similar Validate() method.
I believe that the IDataError allows for a much richer user experience. Like Marc said, it allows less interruption, especially when editing a grid eg. a list of Customer objects.
I recommend you download the CSLA.net framework from www.lhotka.net, developed by Rocky Lhotka (He is the author of Expert C# 2008 Business Objects). This framework supports validation rules and each business object implements the IDataError. Each time a property is changed, the rules for that property are validated. If the property value is invalid, the object state will become InValid, causing a exception to be thrown whenever the Save() method is invoked.
His framework also supports n-level undo. When you start editing a business object, a snapshot of the object is taken (including the broken rules). So if you decide to rollback your changes, the state of the object returns to the previous state - even the broken rules!

Categories