Question

I am new to ServiceStack. I use the Version 4.04.

I created two programs they are using Redis queues to communication to each other. One is Asp.Net Host the other is a Windows-Service.

While basic sending and receiving messages is working well, I have some problems to configure Validation for request DTOs at the Windows-Service program.

These are my request, response DTOs an the validator:

public class RegisterCustomer : IReturn<RegisterCustomerResponse>
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string CompanyName { get; set; }
  public string EMailAddress { get; set; }
  public string Password { get; set; }
}

public class RegisterCustomerValidator : AbstractValidator<RegisterCustomer>
{
  public RegisterCustomerValidator()
  {
    RuleFor(m => m.FirstName).NotEmpty();
    RuleFor(m => m.LastName).NotEmpty();
    RuleFor(m => m.EMailAddress).NotEmpty().EmailAddress();
    RuleFor(m => m.Password).NotEmpty().Length(5, 100);
  }
}

public class RegisterCustomerResponse
{
  public ResponseStatus ResponseStatus { get; set; }
}

This is the part where I configure the validation:

private void configureValidation(Container container)
{
  Plugins.Add(new ValidationFeature());
  // container.RegisterValidators(typeof(RegisterCustomerValidator).Assembly);
  container.Register<IValidator<RegisterCustomer>>(new RegisterCustomerValidator());
}

This is my service:

public class RegisterCustomerService : RavenService
{
  public RegisterCustomerService(IDocumentSession ravenSession)
    : base(ravenSession)
  {
  }

  public RegisterCustomerResponse Any(RegisterCustomer request)
  {
    Logger.Info("Start: Handel message '{0}' ({1})".Fmt(request.ToString(), request.GetType().Name));

    RegisterCustomerResponse result = new RegisterCustomerResponse();     

    Logger.Info("End: Handel message '{0}' ({1})".Fmt(request.ToString(), request.GetType().Name));
    return result;
  }
}
  1. The messages & validator are in a seperate assembly located. Auto registering the Validator at the IOC via container.RegisterValidators(typeof(RegisterCustomerValidator).Assembly); is not wokring. After I register the Validator via:

container.Register<IValidator<RegisterCustomer>>(new RegisterCustomerValidator());

I was able to resolve the Validator at the Service via "TryResolve"

  1. I thought as soon as I register the validator at the IOC, the request DTO gets validated. But it looks like that is not the case: The caller gets back the response and the "ResponseStatus" property is null.

  2. I created RequestFilter to do the validation:

    public class ValidationFilterAttribute : RequestFilterAttribute { public override void Execute(IRequest req, IResponse res, object requestDto) { string dummy = "HelloWorld"; } }

and applied it to the request dto:

[ValidationFilter]
public class RegisterCustomer : IReturn<RegisterCustomerResponse>
{
  ...
}

The "Execute" methods is not called. What do I miss here?

Any help is much appreciated.

Was it helpful?

Solution

This was because the validation were getting executed as a Global RequestFilter which aren't executed for MQ Requests which are considered to be internal services and skip the Global Request Filters.

We've added new IAppHost.GlobalMessageRequestFilters in v4.05 which the ValidationFeature uses to now validate requests via MQ as well.

Note: that any validation or service errors are sent to the Response DLQ (e.g. QueueNames<RegisterCustomerResponse>.Dlq) as opposed to the default INQ (e.g. QueueNames<RegisterCustomerResponse>.Inq) for valid responses as visible in this example:

public class ValidateTestMq
{
    public int Id { get; set; }
}

public class ValidateTestMqResponse
{
    public int CorrelationId { get; set; }

    public ResponseStatus ResponseStatus { get; set; }
}

public class ValidateTestMqValidator : AbstractValidator<ValidateTestMq>
{
    public ValidateTestMqValidator()
    {
        RuleFor(x => x.Id)
            .GreaterThanOrEqualTo(0)
            .WithErrorCode("PositiveIntegersOnly");
    }
}

The test below publishes an invalid request followed by a valid one:

using (var mqFactory = appHost.TryResolve<IMessageFactory>())
{
    var request = new ValidateTestMq { Id = -10 };
    mqFactory.CreateMessageProducer().Publish(request);
    var msg = mqFactory.CreateMessageQueueClient()
        .Get(QueueNames<ValidateTestMqResponse>.Dlq, null)
        .ToMessage<ValidateTestMqResponse>();

    Assert.That(msg.GetBody().ResponseStatus.ErrorCode,
        Is.EqualTo("PositiveIntegersOnly"));

    request = new ValidateTestMq { Id = 10 };
    mqFactory.CreateMessageProducer().Publish(request);
    msg = mqFactory.CreateMessageQueueClient()
         .Get(QueueNames<ValidateTestMqResponse>.In, null)
        .ToMessage<ValidateTestMqResponse>();
    Assert.That(msg.GetBody().CorrelationId, Is.EqualTo(request.Id));
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top