ServiceStack Validator - Request not injected - servicestack

I have a validator and I'm trying to use some session variables as part of the validation logic, however the base.Request is always coming back as NULL. I've added it in the lambda function as directed and also the documentation for Validation seems to be out of date as the tip in the Fluent validation for request dtos section mentions to use IRequiresHttpRequest, but the AbstractValidator class already implements IRequiresRequest.
This is my code:
public class UpdateContact : IReturn<UpdateContactResponse>
{
public Guid Id { get; set; }
public string Reference { get; set; }
public string Notes { get; set; }
public List<Accounts> Accounts { get; set; }
}
public class UpdateContactResponse : ResponseBase
{
public Guid ContactId { get; set; }
}
public class UpdateContactValidator : AbstractValidator<UpdateContact>
{
public UpdateContactValidator(IValidator<AccountDetail> accountDetailValidator)
{
RuleSet(ApplyTo.Post | ApplyTo.Put, () => {
var session = base.Request.GetSession() as CustomAuthSession;
RuleFor(c => c.Reference).Must(x => !string.IsNullOrEmpty(x) && session.Region.GetCountry() == RegionCodes.AU);
});
RuleFor(R => R.Accounts).SetCollectionValidator(accountDetailValidator);
}
}
Is there something I'm missing?

Access to injected dependencies can only be done from within a RuleFor() lambda, delegates in a RuleSet() are executed on constructor initialization to setup the rules for that RuleSet.
So you need to change your access to base.Request to within RuleFor() lambda:
RuleSet(ApplyTo.Post | ApplyTo.Put, () => {
RuleFor(c => c.Reference)
.Must(x => !string.IsNullOrEmpty(x) &&
(Request.GetSession() as CustomAuthSession).Region.GetCountry() == RegionCodes.AU);
});

Related

Error when adding Where or OrderBy clauses to Azure Mobile Apps request

I'm developing an Azure Mobile App service to interface to my Xamarin application.
I've created, connected and successfully populated an SQL Database, but when I try to add some filters to my request, for example an orderby() or where() clauses, it returns me a Bad Request error.
For example, this request: https://myapp.azurewebsites.net/tables/Race?$orderby=iRound%20desc,iYear%20desc&$top=1&ZUMO-API-VERSION=2.0.0 gives me {"message":"The query specified in the URI is not valid. Could not find a property named 'IYear' on type 'MyType'."}.
My configuration method is this:
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration()
.AddTablesWithEntityFramework()
.ApplyTo(config);
config.MapHttpAttributeRoutes();
Database.SetInitializer(new CreateDatabaseIfNotExists<MainDataContext>());
app.UseWebApi(config);
and my DbContext is this:
public class MainDataContext : DbContext
{
private const string connectionStringName = "Name=MS_TableConnectionString";
public MainDataContext() : base(connectionStringName)
{
Database.Log = s => WriteLog(s);
}
public void WriteLog(string msg)
{
System.Diagnostics.Debug.WriteLine(msg);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(
new AttributeToColumnAnnotationConvention<TableColumnAttribute, string>(
"ServiceTableColumn", (property, attributes) => attributes.Single().ColumnType.ToString()));
}
public DbSet<Race> Race { get; set; }
public DbSet ...ecc...
}
Following this guide, I added a migration after creating my TableControllers. So the TableController for the example type shown above is pretty standard:
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
public class RaceController : TableController<Race>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MainDataContext context = new MainDataContext();
DomainManager = new EntityDomainManager<Race>(context, Request);
}
// GET tables/Race
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
public IQueryable<Race> GetAllRace()
{
return Query();
}
// GET tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<Race> GetRace(string id)
{
return Lookup(id);
}
// PATCH tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<Race> PatchRace(string id, Delta<Race> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/Race
public async Task<IHttpActionResult> PostRace(Race item)
{
Race current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteRace(string id)
{
return DeleteAsync(id);
}
}
As you can see, I already tried to add the EnableQuery attribute to my TableController, as seen on Google. I also tried to add these filters to the HttpConfiguration object, without any success:
config.Filters.Add(new EnableQueryAttribute
{
PageSize = 10,
AllowedArithmeticOperators = AllowedArithmeticOperators.All,
AllowedFunctions = AllowedFunctions.All,
AllowedLogicalOperators = AllowedLogicalOperators.All,
AllowedQueryOptions = AllowedQueryOptions.All
});
config.AddODataQueryFilter(new EnableQueryAttribute
{
PageSize = 10,
AllowedArithmeticOperators = AllowedArithmeticOperators.All,
AllowedFunctions = AllowedFunctions.All,
AllowedLogicalOperators = AllowedLogicalOperators.All,
AllowedQueryOptions = AllowedQueryOptions.All
});
I don't know what to investigate more, as things seems to be changing too fast for a newbie like me who's first got into Azure.
EDIT
I forgot to say that asking for the complete table, so for example https://myapp.azurewebsites.net/tables/Race?ZUMO-API-VERSION=2.0.0, returns correctly the entire dataset. The problem occurs only when adding some clauses to the request.
EDIT 2
My model is like this:
public class Race : EntityData
{
public int iRaceId { get; set; }
public int iYear { get; set; }
public int iRound { get; set; }
ecc..
}
and the database table that was automatically created is this, including all the properties inherited from EntityData:
Database table schema
Digging into the source code, Azure Mobile Apps sets up camelCase encoding of all requests and responses. It then puts them back after transmission accordign to rules - so iRaceId becomes IRaceId on the server.
The easiest solution to this is to bypass the auto-naming and use a JsonProperty attribute on each property within your server-side DTO and client-side DTO so that they match and will get encoding/decoded according to your rules.
So:
public class Race : EntityData
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("raceId")]
public int iRaceId { get; set; }
[JsonProperty("year")]
public int iYear { get; set; }
[JsonProperty("round")]
public int iRound { get; set; }
etc..
}

ServiceStack and FluentValidation not firing separate rule sets

I'm working in ServiceStack and using FluentValidation to handle incoming DTOs on requests. I've broken these out as follows, but my unit tests don't seem to be able to target specific rule sets. My code is as follows:
DTO / REQUEST CLASSES
public class VendorDto
{
public int Id { get; set; }
public string Name { get; set; }
}
public class DvrDto
{
public int Id { get; set; }
public string Name { get; set; }
public VendorDto Vendor { get; set; }
}
public class DvrRequest : IReturn<DvrResponse>
{
public DvrDto Dvr { get; set; }
}
VALIDATOR CLASSES
public class VendorValidator : AbstractValidator<VendorDto>
{
public VendorValidator()
{
RuleFor(v => v.Name).NotEmpty();
}
}
public class DvrValidator : AbstractValidator<DvrDto>
{
public DvrValidator()
{
RuleFor(dvr => dvr.Name).NotEmpty();
RuleFor(dvr => dvr.Vendor).NotNull().SetValidator(new VendorValidator());
}
}
public class DvrRequestValidator : AbstractValidator<DvrRequest>
{
public DvrRequestValidator()
{
RuleSet(HttpMethods.Post, () =>
{
RuleFor(req => req.Dvr).SetValidator(new DvrValidator());
});
RuleSet(HttpMethods.Patch, () =>
{
RuleFor(req => req.Dvr).SetValidator(new DvrValidator());
RuleFor(req => req.Dvr.Id).GreaterThan(0);
});
}
}
UNIT TEST
[TestMethod]
public void FailWithNullDtoInRequest()
{
// Arrange
var dto = new DvrRequest();
var validator = new DvrRequestValidator();
// Act
var result = validator.Validate(msg, ruleSet: HttpMethods.Post);
// Assert
Assert.IsTrue(result.IsValid);
}
I would prefer to be able to control what gets called depending on what the HttpMethod is that's being called. My thought here was, I want to validate all fields on the DvrDto (and child VendorDto) for both POST and PATCH, but only require a valid Id be set on PATCH. I am setting up my DvrRequestValidator to handle this. However, my unit test as written above (targeting the RuleSet for the POST verb) always finds the request to be valid, even though the validator should fail the request.
In fiddling with it, if I make the following changes:
VALIDATOR
public class DvrRequestValidator : AbstractValidator<DvrRequest>
{
public DvrRequestValidator()
{
RuleFor(req => req.Dvr).SetValidator(new DvrValidator());
RuleSet(HttpMethods.Patch, () =>
{
RuleFor(req => req.Dvr.Id).GreaterThan(0);
});
}
}
TEST CALL (removing the targeted verb)
// Act
var result = validator.Validate(msg); // , ruleSet: HttpMethods.Post);
The validator then works as I expect for a POST, but the PATCH rule set doesn't get executed. As a result, I seem to lose the granularity of what I want validated on a particular verb. It would appear to me that this is supported in examples I've seen both on StackOverflow and in the FluentValidation docs. Am I doing something wrong here? Or is this not possible?
The Validators are registered in ServiceStack's Global Request Filters so you'd typically use an Integration Test with a Service Client to test validation errors.
If you want to test the validator independently in a Unit Test you can execute a HTTP Method Result set with something like:
var req = new BasicRequest(requestDto);
var validationResult = validator.Validate(new ValidationContext(requestDto, null,
new MultiRuleSetValidatorSelector(HttpMethods.Patch)) {
Request = req
});
Unit Testing ServiceStack Features
Although note a lot of ServiceStack functionality assumes there's an AppHost is available, but in most cases you can just use an In Memory AppHost, e.g:
[Test]
public void My_unit_test()
{
using (new BasicAppHost().Init())
{
//test ServiceStack classes
}
}
Of if you prefer you can set it up once per test fixture with something like:
public class MyUnitTests
{
ServiceStackHost appHost;
public MyUnitTests() => appHost = new BasicAppHost().Init();
[OneTimeTearDown]
public void OneTimeTearDown() => appHost.Dispose();
[Test]
public void My_unit_test()
{
//test ServiceStack classes
}
}

adding initial rows into tables using Fluent migrator

Im a classic programmer that is newbie at generics and this is an asp.net MVC5 sample application for learning purposes of integrating authorization (users/roles) using fluent migrator lib. I wantto add some sample datas into tables as they created (using migrator console tool).
getting compilation error: USERNAME does not exist in the current context
what should I add in to using section or any example of:
Insert.IntoTable method ?
(thanks)
namespace SampleApp.Migrations
{
[Migration(1)]
public class AuthMigrations:Migration
{
public override void Up()
{
Create.Table("users").
WithColumn("ID").AsInt32().Identity().PrimaryKey().
WithColumn("USERNAME").AsString(128).
WithColumn("EMAIL").AsCustom("VARCHAR(128)").
WithColumn("PASSWORD_HASH").AsString(128);
Create.Table("roles").
WithColumn("ID").AsInt32().Identity().PrimaryKey().
WithColumn("NAME").AsString(128);
Create.Table("role_users").
WithColumn("ID").AsInt32().Identity().PrimaryKey().
WithColumn("USER_ID").AsInt32().ForeignKey("users", "ID").OnDelete(Rule.Cascade).
WithColumn("ROLE_ID").AsInt32().ForeignKey("roles", "ID").OnDelete(Rule.Cascade);
//Error:The name 'USERNAME' does not exist in the current context
Insert.IntoTable("users").Row(new { USERNAME:"superadmin",EMAIL:"superadmin#mvcapp.com",PASSWORD_HASH:"dfgkmdglkdmfg34532+"});
Insert.IntoTable("users").Row(new { USERNAME:"admin",EMAIL:"admin#mvcapp.com",PASSWORD_HASH:"dfgkmdglkdmfg34532+"});
}
public override void Down()
{
Delete.Table("role_users");
Delete.Table("roles");
Delete.Table("users");
}
}
and
namespace SampleApp.Models
{
public class User
{
public virtual int Id { get; set; }
public virtual string Username { get; set; }
public virtual string EMail { get; set; }
public virtual string passwordhash { get; set; }
}
public class UserMap : ClassMapping<User>
{
public UserMap()
{
Table("Users");
Id(x => x.Id, x => x.Generator(Generators.Identity));
Property(x => x.Username, x => x.NotNullable(true));
Property(x => x.EMail, x => x.NotNullable(true));
Property(x=>x.passwordhash,x=>
{
x.Column("PASSWORD_HASH");
x.NotNullable(true);
});
}
}
}
In C#, you must use an equals sign ("=") in the object initializer instead of a colon (":").
Insert.IntoTable("users").Row(new { USERNAME = "superadmin",EMAIL = "superadmin#mvcapp.com",PASSWORD_HASH = "dfgkmdglkdmfg34532+"});
Insert.IntoTable("users").Row(new { USERNAME = "admin",EMAIL = "admin#mvcapp.com",PASSWORD_HASH = "dfgkmdglkdmfg34532+"});

ServiceStack Validation Feature Throws Exception

I am trying to implement validation feature in ServiceStack to validate my RequestDTO's before calling db operations.
When i try to validate request dto like
ValidationResult result = this.AddBookingLimitValidator.Validate(request);
the code automatically throws a validation error automatically.
I can not even debug service what is happening behind the scenes ? Can i change that behaviour or am i doing something wrong here.
Thanks.
My Request DTO :
[Route("/bookinglimit", "POST")]
[Authenticate]
public class AddBookingLimit : IReturn<AddBookingLimitResponse>
{
public int ShiftId { get; set; }
public DateTime Date { get; set; }
public int Limit { get; set; }
}
My Response DTO :
public class AddBookingLimitResponse
{
public int Id { get; set; }
public ResponseStatus ResponseStatus { get; set; }
}
Validation class :
public class AddBookingLimitValidator : AbstractValidator<AddBookingLimit>
{
public AddBookingLimitValidator()
{
RuleFor(r => r.Limit).GreaterThan(0).WithMessage("Limit 0 dan büyük olmalıdır");
}
}
Service Implementation :
public AddBookingLimitResponse Post(AddBookingLimit request)
{
ValidationResult result = this.AddBookingLimitValidator.Validate(request);
Shift shift = new ShiftRepository().Get(request.ShiftId);
BookingLimit bookingLimit = new BookingLimit
{
RestaurantId = base.UserSession.RestaurantId,
ShiftId = request.ShiftId,
StartDate = request.Date.AddHours(shift.StartHour.Hour).AddMinutes(shift.StartHour.Minute),
EndDate = request.Date.AddHours(shift.EndHour.Hour).AddMinutes(shift.EndHour.Minute),
Limit = request.Limit,
CreateDate = DateTime.Now,
CreatedBy = base.UserSession.UserId,
Status = (byte)Status.Active
};
return new AddBookingLimitResponse
{
Id = new BookingLimitRepository().Add(bookingLimit)
};
}
AppHost code :
container.RegisterValidators(typeof(AddBookingLimitValidator).Assembly);
Plugins.Add(new ValidationFeature());
And i consume the service in c# code:
try
{
AddBookingLimitResponse response = ClientHelper.JsonClient.Post(new AddBookingLimit
{
Date = DateTime.Parse(DailyBookingLimitDateTextBox.Text),
Limit = Convert.ToInt32(DailyBookingLimitTextBox.Text),
ShiftId = Convert.ToInt32(DailyDayTypeSelection.SelectedValue)
});
WebManager.ShowMessage(UserMessages.SaveSuccessful.FormatString(Fields.BookingLimit));
}
catch (WebServiceException ex)
{
WebManager.ShowMessage(ex.ResponseStatus.Message);
}
Right, ServiceStack validates the request DTO before the service gets called if the ValidationFeature is enabled.
To manually invoke the validator in the service, you have to remove this line from your AppHost first:
Plugins.Add(new ValidationFeature());
Please make sure that the validator property in your service has the type IValidator<>, otherwise it won't be injected by the IoC container if you register your validators with container.RegisterValidators(typeof(AddBookingLimitValidator).Assembly).
public class TestService : Service
{
public IValidator<Request> Validator { get; set; }
public RequestResponse Post(Request request)
{
Validator.Validate(request);
...
}
}

ServiceStack and FluentValidation NOT firing

I must be overlooking something around getting the fluent-validation to fire within basic Service-Stack application I created.
I have been following the example found here. For the life of me I can't seem to get my validators fire????
Crumbs, there must be something stupid that I'm missing....???
I'm issuing a user request against the User-Service (http://my.service/users), the request goes straight through without invoking the appropriate validator registered.
Request is :
{"Name":"","Company":"Co","Age":10,"Count":110,"Address":"123 brown str."}
Response :
"user saved..."
Here is the code :
1.DTO
[Route("/users")]
public class User
{
public string Name { get; set; }
public string Company { get; set; }
public int Age { get; set; }
public int Count { get; set; }
public string Address { get; set; }
}
2.Validator
public class UserValidator : AbstractValidator<User>
{
public UserValidator()
{
RuleFor(r => r.Name).NotEmpty();
RuleFor(r => r.Age).GreaterThan(0);
}
}
3.AppHostBase
public class ValidationAppHost : AppHostBase
{
public ValidationAppHost()
: base("Validation Test", typeof(UserService).Assembly)
{
}
public override void Configure(Funq.Container container)
{
Plugins.Add(new ValidationFeature());
//This method scans the assembly for validators
container.RegisterValidators(typeof(UserValidator).Assembly);
}
}
4.Service
public class UserService : Service
{
public object Any(User user)
{
return "user saved...";
}
}
5.Global.asax.cs
protected void Application_Start(object sender, EventArgs e)
{
new ValidationAppHost().Init();
}
Ok....found the issue....I (in error) installed (via nuget) and referenced within my project the FluentValidation.dll with Service-Stack's FluentValidation implementation (see namespace ServiceStack.FluentValidation).
Once I removed this the sole incorrect FluentValidation reference and ensured that my validator extended from the service-stack implementation of the AbstractValidator the validators fired correctly...

Resources