Validating enums with FluentValidation - asp.net

I'm using FluentValidation.WebApi 6.2.1.0 in Web API project. Is there a way to validate enum with FluentValidation and return custom message?
my controller action is as following,
public IHttpActionResult Get([FromUri]CheckUpdateVM info)
{
...
}
My Model,
[Validator(typeof(CheckUpdateVMValidator))]
public class CheckUpdateVM
{
public DeviceTypes Device { get; set; }
}
I'm looing for something like this,
public class CheckUpdateVMValidator : AbstractValidator<CheckUpdateVM>
{
public CheckUpdateVMValidator()
{
RuleFor(x => x.Device).Must(x => Enum.IsDefined(typeof(DeviceTypes), x)).WithMessage("xxx");
}
}
With above code, Model binder validate the value of "Device" parameter and response with an error. but I can't customize the error message. (If I set the "Device" property type to string, this works fine.)

Creating custom validator could be better approach in this scenario.
public class DeviceEnumValidator<T> : PropertyValidator {
public DeviceEnumValidator()
: base("Invalid Enum value!") { }
protected override bool IsValid(PropertyValidatorContext context) {
DeviceTypes enumVal= (DeviceTypes) Enum.Parse(typeof(DeviceTypes), context.PropertyValue);
if (!Enum.IsDefined(typeof(DeviceTypes), enumVal)
return false;
return true;
}
}
To use DeviceEnumValidator you can call SetValidator when defining a validation rule.
public CheckUpdateVMValidator()
{
RuleFor(x => x.Device).SetValidator(new DeviceEnumValidator<DeviceTypes>());
}

Related

Request cannot be converted while using AbstractValidator as a base class

My project throws a convert error when I use fluentvalidation class as a request model.
I have a BaseRequest. It is inherited from AbstractValidatator
public abstract class BaseRequest<TModel> : AbstractValidator<TModel>
{
}
Another model is CreateRuleRequest and it is inherited from BaseRequest. And also it includes some rule for its properties.
public class CreateRuleRequest : BaseRequest<CreateRuleRequest>
{
public CreateRuleRequest()
{
this.RuleFor(k => k.Rules).NotEmpty();
}
public List<RuleModel> Rules { get; set; }
}
And I am using an Extension to load all rules that are inherited from BaseRequest. Actually, AbstractValidator extends from IValidator so that I can catch all the classes that includes rules.
public static IServiceCollection AddValidation(this IServiceCollection services)
{
var validators = AppDomain.CurrentDomain.GetAssemblies().SelectMany(s =>
s.GetTypes())
.Where(t => t.GetInterfaces().Contains(typeof(IValidator))).Where(k =>
k.GetInterfaces().Contains(typeof(IQuery)) ||
k.GetInterfaces().Contains(typeof(ICommand)));
foreach (var validator in validators)
{
services.AddValidatorsFromAssemblyContaining(validator);
}
return services;
}
Lastly, I have a controller that has a Create method. This method is using CreateRuleRequest as a parameter.
public async Task<IActionResult> Create([FromBody] CreateRuleRequest command) =>
and the application throws The JSON value could not be converted CreateRuleRequest. I am wondering why? is it mandatory to create another model binder or is there any other way

FluentValidation - PolymorphicValidator: How to avoid validator duplication?

I have an entity called "Role" in my asp.net core 6 Web API application. Below is the DTO of the "Role" entity
public class CreateUpdateRoleDto
{
public Guid Id { get; set; }
.....
}
and I have implemented the Command like mentioned below
public class CreateRoleCommand : CreateUpdateRoleDto, IRequest<Guid>
{
}
with the corresponding Validator
public class CreateRoleCommandValidator : AbstractValidator<CreateRoleCommand>
{
}
Now, I want to implement the batch processing for the same "Role" entity and I have implemented the Command like mentioned below
public class CreateRoleBatchCommand : CreateUpdateRoleDto, IRequest
{
}
with its validator
public class CreateRoleBatchCommandValidator : AbstractValidator<CreateRoleBatchCommand>
{
}
CreateRoleBatchCommandValidator is just a copy of the CreateRoleCommandValidator except that it implements IRequest instead of IRequest but I don't think we need this additional validator as this is just a duplicate code.
Below is the implementation of BatchCommand
public class BatchCommand : IRequest
{
public IEnumerable<IRequest> Requests { get; set; }
}
The batch command validator uses the PolymorphicValidator and sets CreateRoleBatchCommandValidator as a validator for CreateRoleBatchCommand.
public class BatchCommandValidator : AbstractValidator<BatchCommand>
{
public BatchCommandValidator()
{
this.RuleForEach(x => x.Requests).SetAsyncValidator(new PolymorphicValidator<BatchCommand, IRequest>()
.Add<CreateRoleBatchCommand>(new CreateRoleBatchCommandValidator()));
}
}
However, I want to set CreateRoleCommand as a validator for CreateRoleBatchCommand, something like this
public class BatchCommandValidator : AbstractValidator<BatchCommand>
{
public BatchCommandValidator()
{
this.RuleForEach(x => x.Requests).SetAsyncValidator(new PolymorphicValidator<BatchCommand, IRequest>()
.Add<CreateRoleBatchCommand>(new CreateRoleCommandValidator()));
}
}
However it throws the following error.
CS1503 Argument 1: cannot convert from 'CreateRoleCommandValidator' to
'FluentValidation.IValidator'

Uno Platform WASM SignalR deserialization issues

I have a SignalR service and an Uno Platform WASM Client (with Prism). I want to call a hub method, which returns a model. The problem is, i have two identical models (Properties, Methods, etc.) but the client can only receive one of them upon calling the hub methods. With the other model a exception gets thrown on deserialization.
Hub:
using Microsoft.AspNetCore.SignalR;
using ReservationManagement.SignalRInterface.Model;
using System.Threading.Tasks;
namespace ReservationManagement.Service.Hubs
{
public class TestServiceHub: Hub
{
public async Task<LocationModel> LocationModel()
{
return new LocationModel() { Name = "Location" };
}
public async Task<TestModel> TestModel()
{
return new TestModel() { Name = "Test" };
}
}
}
Models:
namespace ReservationManagement.SignalRInterface.Model
{
public class TestModel
{
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
}
namespace ReservationManagement.SignalRInterface.Model
{
public class LocationModel
{
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
}
ViewModel:
using Microsoft.AspNetCore.SignalR.Client;
using Prism.Commands;
using ReservationManagement.SignalRInterface.Model;
namespace ReservationManagement.UnoPrism.ViewModels
{
class TestViewModel: ViewModelBase
{
private HubConnection HubConnection { get; set; }
public DelegateCommand Loaded { get; private set; }
public LocationModel LocationModel
{
get => _locationModel;
set { SetProperty(ref _locationModel, value); }
}
private LocationModel _locationModel;
public TestModel TestModel
{
get => _testModel;
set { SetProperty(ref _testModel, value); }
}
private TestModel _testModel;
public TestViewModel()
{
Loaded = new DelegateCommand(LoadedExecute);
}
private async void LoadedExecute()
{
HubConnection = new HubConnectionBuilder()
.WithUrl("http://localhost:5000/TestServiceHubAnyOrigin")
.WithAutomaticReconnect()
.Build();
await HubConnection.StartAsync();
LocationModel = await HubConnection.InvokeAsync<LocationModel>("LocationModel");
TestModel = await HubConnection.InvokeAsync<TestModel>("TestModel");
}
}
}
The call for LocationModel works fine and it is set to what the service returns. The call for TestModel results in an exception. If i switch the calling order (1. TestModel, 2. LocationModel) the exception will still be thrown on the TestModel-call.
Everything works perfectly fine when i build for Uwp.
Exception:
System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'ReservationManagement.SignalRInterface.Model.TestModel'. Path: $ | LineNumber: 0 | BytePositionInLine: 1. ---> System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'ReservationManagement.SignalRInterface.Model.TestModel'.
I also tried this, as the exception suggests, with a singular parameterized constructor and a parameterized constructor annotated with 'JsonConstructorAttribute', but still the same exception.
This is a generic issue related to the IL Linker step, which removes members that are detected as not used, in some cases incorrectly when reflection is used.
You will need to add linker descriptors to fix this in the LinkerConfig.xml file in the WebAssembly project, likely in the assembly that contains the ReservationManagement namespace.
You can find additional documentation about the linker configuration here.

Is there a way to get the current controller instance in ASP.NET 5?

Is there a way to do this using DI? I tried IScopedInstance<Controller> but this gives me null. Poked around aspnet's source code but didn't win. Any ideas?
I have a controller that accepts different IPaymentMethods. The IPaymentMethod can be a ViewComponent that can render Views. If the IPaymentMethod is a ViewComponent, I want it to use MVC's built-in model binding on post back.
public class XController : Controller
{
// ctor, props, ...
public IActionResult Checkout()
{
return View(new Model
{
PaymentMethodId = 1,
PaymentMethodType = typeof(MyPaymentMethod) // The razor file will use this type to render it as a ViewComponent
});
}
[HttpPost]
public IActionResult Checkout(Model model)
{
var paymentMethod = _paymentService.GetPaymentMethodById(model.PaymentMethodId);
paymentMethod.ProcessPayment();
// ..
}
}
This is where I need the controller to be injected. I wanted to make use of the built-in MVC validation and model binding.
public class MyPaymentMethod : IPaymentMethod
{
private Controller _currentController;
public MyPaymentMethod(IScopedInstance<Controller> controller)
{
_currentController = controller.Value;
}
public void ProcessPayment()
{
var model = new PaymentModel();
_currentController.TryUpdateModel(model, typeof(PaymentModel), null);
if (!_currentController.ModelState.IsValid)
{
return; // or exception
}
// Process Payment using model
}
public Task<IViewComponentResult> InvokeAsync()
{
// returns View
}
}
public interface IPaymentMethod
{
void ProcessPayment();
}
Since the model instance is required in the ProcessPayment method, why not simply pass it as a parameter?
[HttpPost]
public IActionResult Checkout(PaymentModel model)
{
var paymentMethod = _paymentService.GetPaymentMethodById(model.PaymentMethodId);
if (!ModelState.IsValid)
{
return; // or exception
}
paymentMethod.ProcessPayment(model);
// ..
}
public void ProcessPayment(PaymentModel model)
{
// Process Payment using model
}
Your service is taking on responsibilities that belong to the controller - namely checking ModelState.IsValid.
public interface IPaymentMethod
{
void ProcessPayment(PaymentModel model);
}
You may wish to also pass just the properties that are needed from the payment model, or you may wish to make an IPaymentModel interface to decouple your model from your PaymentService. In that case, your IPaymentModel would go into a shared layer.
public interface IPaymentMethod
{
void ProcessPayment(IPaymentModel model);
}
This no longer works with beta7
At this time of writing (beta6), this probably isn't supported and there is a good reason for it: Controllers in ASP.NET 5 does not need to inherit from the Controller class. I have, however, found a way for this to work using ActionFilters.
public class ScopeControllerActionFilterAttribute : ActionFilterAttribute
{
private readonly IScopedInstance<Controller> _controller;
public ScopeControllerActionFilterAttribute(IScopedInstance<Controller> controller)
{
_controller = controller;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (_controller.Value == null)
{
_controller.Value = context.Controller as Controller;
}
}
}
Note that depending on the stage of the http request lifecycle, the Value of IScopedInstance<Controller> may still be empty.

ASP.Net (3.5) MVC partial class for rule validation

I have 2 projects. A data project, which contains my database and my Entity Framework model. I have table called 'User', and then have a generated EF class for user.
I am trying to add a partial class:
using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;
using System.Text;
namespace Data
{
public partial class user
{
public bool isValid
{
get {
return (GetRuleViolations().Count()==0);
}
}
public IEnumerable<RuleViolation> GetRuleViolations()
{
yield break;
}
partialvoid OnValidate(ChangeAction action)
{
if (isValid)
throw new ApplicationException("Rule violation prevents saving");
}
}
public class RuleViolation
{
public string ErrorMessage { get; private set; }
public string PropertyName { get; private set; }
public RuleViolation (string errorMessage)
{
ErrorMessage = errorMessage;
}
public RuleViolation(string errorMessage, string propertyName)
{
ErrorMessage = errorMessage;
PropertyName = propertyName;
}
}
}
This is following the MVC 1.0 NerdDinner example. However, I am getting a design time error on the OnValidate method:
partial void OnValidate(ChangeAction action)
{
if (isValid)
throw new ApplicationException("Rule violation prevents saving");
}
No definining declaration found for implimenting declaration of partial method 'void OnValidate(ChangeAction action)'
What am I doing wrong?
There should be another file containing the rest of the user class.
In this file you have to declare that there might be a partial method called OnValidate.
public partial class user
{
partial void OnValidate(ChangeAction action);
}
Some detail:
A partial method needs to have a signature specified somewhere. Once the signature has been defined then an optional implementation can be specified. Read http://msdn.microsoft.com/en-us/library/wa80x488.aspx for more information.

Resources