NancyFX Doesn't Recognize Custom Data Annotations - c#

I am using NancyFX for my API Gateway and I have a model such as the following:
public class CreatePerson
{
[Required]
public string FirstName {get;set;}
[Required]
public string LastName {get;set;}
[Required]
[Phone]
public string Phone {get;set;}
[Required]
[MyCustomValidationRule]
public string ImagePath {get;set;}
}
It uses both a custom MyCustomValidationRule attribute as well as the provided Phone and Required attributes.
In my module I have the following:
public class PersonModule
{
public PersonModule()
{
Post["/",true] = async (context,cancel)=>
{
var request = this.BindAndValidate<CreatePerson>();
if(!ModelValidationResult.IsValid)
{
//THIS NEVER HITS
}
}
}
}
The [Required] attributes are working and if I omit any of the properties, it works fine. However, if I pass in an invalid phone (such as sdfsdgsdg or I do something that clearly breaks MyCustomValidationRule attribute, it is not caught. Furthermore, I have placed a breakpoint in the constructor of the MyCustomValidationRule attribute, and it never hits.
Why is this not hitting?

#TheJediCowboy. I had the same problem in my project.
I can't check my theory now, but I think you should add ValidationAdapter for each kind of attribute, example you can find on github
I think only 4 validation attribute will work in default package(Range, Regex, Required, StringLength), please check list of validation adapters github
For custom attributes required add custom adapter, I think.
UPDATED
This solution checked and works good.
For validation attribute (except Range, Regex, Required and StringLength) required add validation adapter (or override exiting). Nancy use all validation adapters that are in the project.

Related

Localise Display DataAnnotation without the Name Attribute in .NET Core 5 ViewModel

I've
[Required]
[Display(Name ="Email")]
public string Email { get; set; }
[Required]
[Display (Name = "Password")]
public string Password { get; set; }
In my ViewModel. I'm able to localize this. Additionally, I was able to put a different localization to the 'Required' message [without specifying ResourceType and ResourceName manually] than the default Microsoft message using the resource file. How I did that? Here is the link:
https://stackoverflow.com/a/41385880/931305
Now, I want to remove the 'Name' attribute of the 'Display'. Because most of the time Display Name is always going to be the same as the actual Property name. If you notice both are 'Email'/'Password'. So it will make the code looking clean.
I was able to do this in classic ASP.NET MVC. Here is the link:
https://haacked.com/archive/2011/07/14/model-metadata-and-validation-localization-using-conventions.aspx/
Now, how do I do this in .NET Core 5? I'm unable to use IValidationAttributeAdapterProvider to inject 'Display'. [I was getting all 'validation' attributes, but not Display]
The best and standard solution is to using localization in ASP.NET Core application.
In summary, the steps to localize your application are rough as follows:
Add the required localization services
Configure the localization middleware and if necessary a culture
provider
Inject IStringLocalizer into your controllers and services to
localize strings
Inject IViewLocalizer into your views to localize strings in views
Add resource files for non-default cultures
Add a mechanism for users to choose their culture
Take a look at this article for a detailed walkthrough.
You could write an attribute like so
public sealed class MyDisplayAttribute : DisplayNameAttribute, IModelAttribute
{
private string _name = string.Empty;
public MyDisplayAttribute(string displayName)
{
_name= displayName;
}
public override string DisplayName => _name;
public string Name => nameof(MyDisplayAttribute);
}
usage:
public class MyModel
{
[MyDisplay("MyString")]
public string MyString { get; set; }
}

Validation in a layered application ASP.NET Core

I'm designing a layered web application with an MVC, Service and Repository layer, however I'm having trouble knowing where to put validation logic that allows me to take advantage of .NET Core built in form validation (eg ModelStateDictionary), while following the DRY principle.
The first and most obvious approach is to use a ViewModel that has the appropriate data annotations:
public class VendorViewModel
{
public long Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Phone { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string Address { get; set; }
public DateTime? VerifiedAt { get; set; }
}
Then my controller action would look like this
public async Task<IActionResult> Create([FromForm] VendorViewModel model)
{
await AuthorizePolicyAsync(AuthorizationPolicyTypes.Vendor.Create);
if (!ModelState.IsValid) //Validation problems, so re-display the form.
return View(model);
await _vendorservice.CreateVendorAsync(model.Name,model.Phone,model.Email,model.Address,null);
return RedirectToAction(nameof(Index));
}
This works fine, however there are a couple problems:
This only supports basic validation such as checking character length, etc. In the particular example above, I want to validate that model.Address is a valid address according to google maps and also contains a city that the application is aware of, which means this kind of validation should be moved to the service layer to keep the Controller "thin".
The service layer is now missing any validation logic, and assumes that it is always being passed valid data. This seems wrong to me since it seems like the service layer should be responsible for keeping the system in a consistent valid state. A solution to this would be to also add validation logic to the service layer, but that seems to violate the DRY principle in my opinion.
The second approach would be to move all of the validation logic to the service layer and move all my data annotations to the actual domain object Vendor. This way each operation could validate the model based on the data annotations, and also apply any more complex logic such as validating the address with google maps as previously mentioned. However, I'm not sure how I can validate an annotated object in the same manner that a MVC Controller does and pass back a dictionary to the controller. This functionality seems to be specific to MVC and would introduce a dependency on MVC in my service layer which is undesirable.
Is there anyway I can elegantly move validation logic to the service layer while
taking advantage of data annotations and MVC's built in ModelStateDictionary? How do I get the list of errors back to the controller? Do I throw an exception and catch it in the controller if any validation errors occur?
I have seen several questions asking a similar question, but I'm not satisfied with any of the answers. Other answers seem to involve writing validation logic manually and not taking advantage of data annotations. Is this what I should resort to?
You can create your own custom validation attributes in addition to what are available out of the box such as Required,Range,StringLength,etc.
I will provide an example below :
public class ValidateAddressAttribute : Attribute, IModelValidator
{
public bool IsRequired => true;
public string ErrorMessage { get; set; } = "Address is not valid";
public IEnumerable<ModelValidationResult>Validate(ModelValidationContext context)
{
List<ModelValidationResult> validationResults = new List<ModelValidationResult>();
string address = context.Model as string;
if(!IsAddressValid(address))
{
validationResults.Add(new ModelValidationResult("", ErrorMessage));
}
return validationResults;
}
private bool IsAddressValid(string address)
{
bool isAddressValid;
//set isAddressValid to true or false based on your validation logic
return isAddressValid;
}
}
You can now apply this attribute on your address property as follows :
[Required]
[ValidateAddress(ErrorMessage="Invalid Address")]
public string Address { get; set; }

Using Required and JsonRequired in ASP.NET Core Model Binding with JSON body

I'm using ASP.NET Core 2.0, and I have a request object annotated like this:
public class MyRequest
{
[Required]
public Guid Id { get; set; }
[Required]
public DateTime EndDateTimeUtc { get; set; }
[Required]
public DateTime StartDateTimeUtc { get; set; }
}
And in my controller:
public async Task<IActionResult> HandleRequest([FromBody] MyRequest request)
{ /* ... */ }
I noticed an issue with model binding: When I send a request containing the header Content-Type set to application/json and a an empty body, as I expect, the request in my controller is null and ModelState.IsValid is false.
But when I have a body like this:
{
"hello": "a-string-value!"
}
my request is NOT null, it has default values for everything, and ModelState.IsValid is true
This is happening of course while I'm missing all the Required properties, and the only existing one's name doesn't match a property there (even the type for this single parameter is string, which doesn't match any type on my model).
So in a way, those Required attributes seem to be working if there's nothing in my request, but they don't do anything if my request is not empty!
As I was preparing this question, I noticed that there's also a JsonRequired attribute, and it seems to take care of the properties being present.
So, what's the difference between Required and JsonRequired?
For correct work of Required attribute, you should make the properties nullable:
public class MyRequest
{
[Required]
public Guid? Id { get; set; }
[Required]
public DateTime? EndDateTimeUtc { get; set; }
[Required]
public DateTime? StartDateTimeUtc { get; set; }
}
Now if you send request with missing Id, EndDateTimeUtc or StartDateTimeUtc, corresponding field will be set to null, ModelState.IsValid will be set to false and ModelState will contain error(s) description, e.g. The EndDateTimeUtc field is required.
JsonRequired attribute is specific to JSON.Net. It plays during deserialization, while Required attribute (as other attributes from System.ComponentModel.DataAnnotations namespace) plays after model is deserialized, during model validation. If JsonRequired attribute is violated, the model will not be deserialized at all and corresponding action parameter will be set to null.
The main reason why you should prefer Required attribute over JsonRequired is that JsonRequired will not work for other content types (like XML). Required in its turn is universal since it's applied after the model is deserialized.
When you use [FromBody] as binding source, the Model properties will get default values and [BindRequired] will be ignored.
There is a related issue on "Problems with Parameter Validation".
In this case is better to use [JsonRequired] instead of [BindRequired] to enforce binding properties.
Note that [JsonRequired] affects serialization and deserialization both.
Yes, the difficulty is that if you choose Required, the semantics for the client of the web request change incorrectly - i.e. you are saying you can pass in a null, when you really need a proper value.
Using JsonRequired sorts this, but this is provided by NewtonSoft, so stops working when you upgrade to .Net Core 3. This is because .Net Core 3 uses its own Json parser instead of NewtonSoft.

Required attribute on custom classes

I am building an MVC 5 app and have come to the point where I need to validate user input.
I would like to apply a [Required] attribute to a class that is not a built-in data type. Here is a snippet of my view model:
public class GraffitiViewModel : EformBase
{
[Required(ErrorMessage = "Please select yes or no")]
public RadioButtonList<YesNoType> GraffitiOffensive { get; set; }
[Required(ErrorMessage = "Please select yes or no")]
public RadioButtonList<YesNoType> GraffitiTag { get; set; }
// ... more stuff here
}
The RadioButtonList is a class that emits HTML markup for corresponding C# radio button definitions. The [Required] attribute is not working in this context. Is there a way I can extend either my RadioButtonList class, or the [Required] attribute, so I don't have to modify my ViewModel?
I am thinking along the lines of a custom attribute that will perform this validation or a method in my RadioButtonList that will return a bool indicating whether or not the validation succeeded.
Looking forward to your responses!
M
The [Required] attribute should fire if:
the property is null
OR
the property is a string type, which is either empty or whitespace
See MSDN for more details.
Alternatively you can use the code here to create a custom attribute which fires on whatever conditions you decide.

How to create a custom Compare attribute with client-side validation?

The title says it all, but I'll add a bit of background here.
Until recently, I've been using MVC's already-written CompareAttribute to compare two values, in this case a password and its confirmation. It's worked well, except this attribute does not display the display name, set by the [Display(Name = "Name")] attribute of the property being compared.
Here are the two properties being compared:
[Required]
[Display(Name = "New Password")]
public string New { get; set; }
[Compare("New")]
[Display(Name = "Confirm Password")]
public string ConfirmPassword { get; set; }
The validation message reads as follows:
'Confirm Password' and 'New' do not match.
This works, but it's obviously not as good as it should be. The New should read as New Password, as specified by the Display attribute.
I have gotten this working, although not completely. The following implementation (for some reason) fixes the issue of not getting the specified name of the property, but I'm not sure why:
public class CompareWithDisplayNameAttribute : CompareAttribute
{
public CompareWithDisplayNameAttribute(string otherProperty)
: base(otherProperty)
{
}
}
Now, even though this works, client-side validation does not work. I've received an answer in another question that suggests using something like this
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(CompareWithDisplayName), typeof(CompareAttributeAdapter))
in my Global.asax, however the CompareAttributeAdapter doesn't actually exist.
So here I am. I've got the Display attribute being used properly by my custom CompareWithDisplayName attribute, but client-side validation missing altogether.
How can I make client-side validation work with this solution in the cleanest way possible?
If you want your custom compare attribute to work with clientside validation you will need to implement IClientValidatable. This has GetValidationRules which is where you can do any custom validation you might wish.
Example
public class CompareWithDisplayNameAttribute : CompareAttribute, IClientValidatable
{
public CompareWithDisplayNameAttribute(string otherProperty)
: base(otherProperty)
{
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
ModelMetadata metadata, ControllerContext context)
{
// Custom validation goes here.
yield return new ModelClientValidationRule();
}
}

Categories