ServiceStack validators not firing - validation

I am trying to use fluent validation in ServiceStack. I've added the validation plugin and registered my validator.
Plugins.Add(new ValidationFeature());
container.RegisterValidators(typeof(CreateLeaveValidator).Assembly);
I have implemented a validator class for my service model:
public class CreateLeaveValidator : AbstractValidator<CreateLeave>
{
public CreateLeaveValidator()
{
RuleFor(cl => cl.StudentId).NotEmpty();
RuleFor(cl => cl.LeaveDepart).NotEmpty().GreaterThan(DateTime.Now).WithMessage("Leave must begin AFTER current time and date.");
RuleFor(cl => cl.LeaveReturn).NotEmpty().GreaterThan(cl => cl.LeaveDepart).WithMessage("Leave must end AFTER it begins.");
RuleFor(cl => cl.ApprovalStatus).Must( status => ( ("P".Equals(status)) || ("C".Equals(status)) || ("A".Equals(status)) || ("D".Equals(status)) ) );
}
}
Service Model:
[Route("/leaves", "POST")]
public class CreateLeave : IReturn<LeaveResponse>, IUpdateApprovalStatus
{
public int StudentId { get; set; }
public DateTime RequestDate { get; set; }
public DateTime LeaveDepart { get; set; }
public DateTime LeaveReturn { get; set; }
public string Destination { get; set; }
public string HostRelationship { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Postal { get; set; }
public string Hostphone { get; set; }
public string Cellphone { get; set; }
public string Transport { get; set; }
public string Driver { get; set; }
public string Companions { get; set; }
public string Reason { get; set; }
public string ApprovalStatus { get; set; }
public DateTime ApprovalDate { get; set; }
public string ApprovalComment { get; set; }
public string ApprovalReason { get; set; }
public int ApprovalUser { get; set; }
}
But when I create a request with no StudentId or an invalid ApprovalStatus, the validator does not appear to fire and catch the invalid request.
How can I go about troubleshooting the cause of this?
UPDATE: Correction it appears validators are working with my actual service but not in my unit tests. I'm guessing I must not be configuring my apphost correctly in the unit test setup. Here's my test constructor:
public LeaveTests()
{
Licensing.RegisterLicense(#"[license key]");
appHost = new BasicAppHost(typeof(ApiServices).Assembly).Init();
ServiceStack.Text.JsConfig.DateHandler = ServiceStack.Text.DateHandler.ISO8601;
appHost.Plugins.Add(new ValidationFeature());
appHost.Container.RegisterValidators(typeof(CreateLeaveValidator).Assembly);
}

ServiceStack Validation filters are executed in a Global Request Filter which require a full integration test to run, e.g:
public class MyIntegrationTests
{
ServiceStackHost appHost;
public MyIntegrationTests()
{
appHost = new AppHost()
.Init()
.Start("http://localhost:8000/");
}
[OneTimeTearDown] void OneTimeTearDown() => appHost.Dispose();
[Test]
public void Execute_validation_filters()
{
var client = new JsonServiceClient("http://localhost:8000/");
try
{
var response = client.Post(new CreateLeave { ... });
}
catch(WebServiceException ex)
{
//...
}
}
}

Related

Receiving Multi-part form data in dotnet core web API

I'm working on an web API, where it needs to receive the multi-part form data with in a model. As of now it's not receiving the request and showing the Bad request When I tried from the Postman.
My model :
public class InvoiceDetails
{
public int? po_id { get; set; }
public DateTime created_date { get; set; }
public string grn_status { get; set; }
public int utilization_amount { get; set; }
public int currency_id { get; set; }
public string currency_code { get; set; }
public string currency_symbol { get; set; }
[FromForm(Name = "invoice_file")]
public List<IFormCollection> invoice_file { get;set;}
[FromForm(Name = "other_files")]
public List<IFormCollection> other_files { get; set; }
}
In the above class/model "invoice_file" and "other_files" can have multiple file uploads so I made it List.
My Action Method :
[HttpPost]
[Route("CreateInvoice")]
public IActionResult CreateInvoice([FromForm]InvoiceDetails idobj )
{
//var modelData = Request.Form["invoice_file"];
Response resobj = new Response();
try
{
if (idobj.invoice_file.Count > 0)
{
resobj = _dataContext.AddInvoice(idobj);
if (resobj.flag == true)
{
Upload(idobj);
}
}
else
{
resobj.flag = false;
resobj.message = "please upload atleast one invioce file";
}
}
catch (Exception ex)
{
}
return Ok(resobj);
}
How can I make the action method or model, in such a way that user can upload the model with multiple files to the properties other_files & invoice_file.
Reference of postman Image
As CodeCaster says,add Content-Type:multipart/form-data; and change List<IFormCollection> to List<IFormFile>.It is not changing the whole model to List.So you can also retrieve other information which exists in your model with idobj.xxx.Change
public class InvoiceDetails
{
public int? po_id { get; set; }
public DateTime created_date { get; set; }
public string grn_status { get; set; }
public int utilization_amount { get; set; }
public int currency_id { get; set; }
public string currency_code { get; set; }
public string currency_symbol { get; set; }
[FromForm(Name = "invoice_file")]
public List<IFormCollection> invoice_file { get;set;}
[FromForm(Name = "other_files")]
public List<IFormCollection> other_files { get; set; }
}
to
public class InvoiceDetails
{
public int? po_id { get; set; }
public DateTime created_date { get; set; }
public string grn_status { get; set; }
public int utilization_amount { get; set; }
public int currency_id { get; set; }
public string currency_code { get; set; }
public string currency_symbol { get; set; }
[FromForm(Name = "invoice_file")]
public List<IFormFile> invoice_file { get; set; }
[FromForm(Name = "other_files")]
public List<IFormFile> other_files { get; set; }
}
result:
IFormCollection is used to retrieve all the values from posted form data.refer to the official document.
If you want to use c,you can try to use it,you can do like this
public IActionResult CreateInvoice([FromForm]IFormCollection idobj)
and you need to get the data you want to foreach keys of IFormCollection,so public List<IFormCollection> invoice_file { get;set;} is better than use IFormCollection.

AbpBoilerPlate EF Core 3.0 StackOverflowException during UpdateAsync

Recently I've experienced this error while trying to update entities in MS SQL Database.
I have two models:
[AutoMap(typeof(Employees))]
[Table("Employees")]
public class Employees : FullAuditedEntity
{
public Employees()
{
EmployeesAdresses = new HashSet<EmployeesAdresses>();
WorkTimeEntries = new HashSet<WorkTimeEntries>();
ContractEntries = new HashSet<ContractEntries>();
}
public string Name { get; set; }
public string Surname { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
public ICollection<EmployeesAdresses> EmployeesAdresses { get; set; }
public ICollection<WorkTimeEntries> WorkTimeEntries { get; set; }
public ICollection<ContractEntries> ContractEntries { get; set; }
[NotMapped]
public List<GroupRelations> GroupRelations { get; set; }
}
[AutoMap(typeof(EmployeesAdresses))]
[Table("EmployeesAdresses")]
public class EmployeesAdresses : FullAuditedEntity
{
public string Street { get; set; }
public string HouseNumber { get; set; }
public string ApartmentNumber { get; set; }
public string PostalCode { get; set; }
public string City { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public bool IsDefault { get; set; }
public bool IsActive { get; set; }
[ForeignKey("Employees")]
public int EmployeeId { get; set; }
public virtual Employees Employee { get; set; }
}
I am trying to update Employee Adress using simple appservice:
public async Task UpdateEmployee (Employees employeeInput)
{
try
{
var _employee = await _employeesRepository.GetAllIncluding(x => x.EmployeesAdresses).Where(x => x.Id == employeeInput.Id).SingleOrDefaultAsync();
if (_employee == null)
throw new Exception($"Brak pracownika o ID: {employeeInput.Id}");
_employee.EmployeesAdresses.Clear();
_employee.WorkTimeEntries.Clear();
ObjectMapper.Map(employeeInput, _employee);
await _employeesRepository.UpdateAsync(_employee);
}
catch (Exception ex)
{
throw new UserFriendlyException(#"Wystąpił błąd podczas aktualizacji pracownika.", ex.Message, ex.InnerException);
}
}
I am getting StackOverFlowException and I really don't know whats the issue. Last error on stacktrace in diagnostic tool event tab is StringCompare error.
Did you experience such a behaviour? Any ideas what might be a problem?

Why Entity Framework replaces provided value with a new incremental value

I'm trying to work my way through a Dot Net Core MVC project, but I am facing a problem I couldn't solve.
And honestly, I didn't know what to search for.
The project is a simple vehicle registry,
Each vehicle has a Make, a Model, a some Features.
Here are the domain models:
public class Make
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Model> Models { get; set; }
public Make()
{
Models=new Collection<Model>();
}
}
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
public Make Make { get; set; }
public int MakeId { get; set; }
public Model()
{
Make=new Make();
}
}
public class Feature
{
public int Id { get; set; }
public string Name { get; set; }
}
[Table(name:"VehicleFeatures")]
public class VehicleFeature
{
public int VehicleId { get; set; }
public int FeatureId { get; set; }
public Feature Feature { get; set; }
public Vehicle Vehicle { get; set; }
public VehicleFeature()
{
Feature=new Feature();
Vehicle=new Vehicle();
}
}
public class Vehicle
{
public int Id { get; set; }
public Model Model { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ModelId { get; set; }
public ICollection<VehicleFeature> VehicleFeatures { get; set; }
public string ContactName { get; set; }
public string ContactPhone { get; set; }
public string ContactEmail { get; set; }
public DateTime LastUpdate { get; set; }
public bool IsRegistered { get; set; }
public Vehicle()
{
Model=new Model();
VehicleFeatures=new Collection<VehicleFeature>();
}
}
The problem is that when I send the following request to the corresponding controller, EF is replacing the provided value with a new incremental value.
The request I send:
{
"ModelId":10,
"VehicleFeatures":[
{"featureId":45},
{"featureId":46}
],
"ContactName":"Alice",
"ContactPhone":"1234",
"ContactEmail":"Alice#local",
"LastUpdate":"1980-01-01",
"IsRegistered":true
}
And this is what happens:
The controller receives correct values,
Correct values are added to the context,
And then suddenly all hell breaks loose.
This is the controller code:
[HttpPost]
public async Task<IActionResult> CreateVehicle([FromBody] Vehicle v)
{
context.Vehicles.Add(v);
await context.SaveChangesAsync();
return Ok(v.ModelId);
}
What am I missing here?

Microsoft Cognitive Services Web Search API - DeSerialization Issues

I want to learn Cognitive Services Web Search APIs so I started creating a bot application . I already have a account sub- key and other required information also I read many articles and watch build 2016 videos on this as well.I am having trouble while deserializing the result .
I am not able to find the proxy class that I can use to do that .
The url I am using is https://api.cognitive.microsoft.com/bing/v5.0/search/
and I found a proxy class for previous api version . Can anybody tell me how to get proxy class of the api request / response in VS 2015 for these service.
My Code look like this:
string BingSearchUrl = "https://api.cognitive.microsoft.com/bing/v5.0/search/";
const string bingKey = "Key";
public static async Task<string> Search(string query)
{
var client = HttpClientFactory.Create();
var queryString = BingSearchUrl + "?q=" + query + "&count=10";
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", bingKey);
client.DefaultRequestHeaders.Add("Accept", "application/json");
// Request parameters
string r = await client.GetStringAsync(queryString);
var jsonResult = JsonConvert.DeserializeObject<Bing.ExpandableSearchResult>(r);
return jsonResult.Web.First().Title;
Try below code
public string BingSearchUrl = "https://api.cognitive.microsoft.com/bing/v5.0/search/";
const string bingKey =[KEY];
public async void Search()
{
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", bingKey);
// Request parameters
queryString["q"] = "microsoft";
queryString["count"] = "10";
queryString["offset"] = "0";
queryString["mkt"] = "en-us";
queryString["safeSearch"] = "Moderate";
var uri = "https://api.cognitive.microsoft.com/bing/v5.0/news/search?" + queryString;
var response = await client.GetStringAsync(uri);
var jsonResult = JsonConvert.DeserializeObject<BingJson>(response);
string title = jsonResult.value[0].name.ToString();
}
With the jsonResult.value[0] you can loop through the results. First results is at [0] position.
I Created a model class looking at the bing search response json. It looks like,
public class BingJson
{
public string _type { get; set; }
public Instrumentation instrumentation { get; set; }
public string readLink { get; set; }
public int totalEstimatedMatches { get; set; }
public Value[] value { get; set; }
}
public class Instrumentation
{
public string pingUrlBase { get; set; }
public string pageLoadPingUrl { get; set; }
}
public class Value
{
public string name { get; set; }
public string url { get; set; }
public string urlPingSuffix { get; set; }
public Image image { get; set; }
public string description { get; set; }
public About[] about { get; set; }
public Provider[] provider { get; set; }
public DateTime datePublished { get; set; }
public string category { get; set; }
}
public class Image
{
public Thumbnail thumbnail { get; set; }
}
public class Thumbnail
{
public string contentUrl { get; set; }
public int width { get; set; }
public int height { get; set; }
}
public class About
{
public string readLink { get; set; }
public string name { get; set; }
}
public class Provider
{
public string _type { get; set; }
public string name { get; set; }
}
With this model, I am able to get the desired result. The Model which is presented in the other answer is not working in my case.
namespace BingSearchBot
{
public class RootObject
{
public string _type { get; set; }
public WebPages webPages { get; set; }
public RelatedSearches relatedSearches { get; set; }
public RankingResponse rankingResponse { get; set; }
}
public class WebPages
{
public string webSearchUrl { get; set; }
public int totalEstimatedMatches { get; set; }
public List<Value> value { get; set; }
}
public class Value
{
public string id { get; set; }
public string name { get; set; }
public string url { get; set; }
public List<About> about { get; set; }
public string displayUrl { get; set; }
public string snippet { get; set; }
public List<DeepLink> deepLinks { get; set; }
public string dateLastCrawled { get; set; }
public List<SearchTag> searchTags { get; set; }
}
public class About
{
public string name { get; set; }
}
public class DeepLink
{
public string name { get; set; }
public string url { get; set; }
public string snippet { get; set; }
}
public class SearchTag
{
public string name { get; set; }
public string content { get; set; }
}
public class Value2
{
public string text { get; set; }
public string displayText { get; set; }
public string webSearchUrl { get; set; }
}
public class RelatedSearches
{
public string id { get; set; }
public List<Value2> value { get; set; }
}
public class Value3
{
public string id { get; set; }
}
public class Item
{
public string answerType { get; set; }
public int resultIndex { get; set; }
public Value3 value { get; set; }
}
public class Mainline
{
public List<Item> items { get; set; }
}
public class RankingResponse
{
public Mainline mainline { get; set; }
}
}

Using Linq to populate a class

I am getting a weird behavior. I have a class that I created that is used to format data comping from a data entity into a data grid. I am a using a linq query to create a list of the class type from a list of the entity type. Some of the properties of the class are accessible from the linq query but other give me an error. (AMNotStartedPortalDisplay' does not contain a definition for 'ChecklistStatusID'). So my question is why can linq access some properties but not others? I see no reason why this should be happening.
Here is my class:
public class AMWOTPortalDisplay
{
public string DisplayName { get; set; }
public string LOB { get; set; }
public string DisplayProjectPackages { get; set; }
public string ChecklistStatus { get; set; }
public int ChecklistStatusID { get; set; }
public string InstallDate { get; set; }
public string dateToYellow { get; set; }
public string dateToRed { get; set; }
public string ApplicationManager { get; set; }
public string ApplicationManagerLanID { get; set; }
public int ApplicationManagerUserID { get; set; }
public string ImpersonatedManager { get; set; }
public string ImpersonatedManagerLanID { get; set; }
public int ImpersonatedManagerUserID { get; set; }
public string DelegateName { get; set; }
public string DelegateLanID { get; set; }
public int DelegateUserID { get; set; }
public string WOTAssignee { get; set; }
public int ChecklistID { get; set; }
public string DisplayLinkText { get; set; }
public string LinkTextURL { get; set; }
public string rowColor { get; set; }
public string rowTextColor { get; set; }
}
And here is the linq query as I have it so far:
var portaldisplay = checklists
.Select(c => new AMNotStartedPortalDisplay
{
DisplayName = string.Format("{0} ({1})", c.Application.Name, c.Application.ApplicationID),
LOB = c.Application.LOB,
ChecklistStatus = c.ChecklistStatusType.TypeName,
ChecklistStatusID = c.ChecklistStatusTypeID
});
Thanks,
Rhonda
Be careful with your types:
public class AMWOTPortalDisplay
And then:
Select(c => new AMNotStartedPortalDisplay { ... })
It looks like your query should probably be:
Select(c => new AMWOTPortalDisplay { ... })

Resources