C# RequiredAttribute messages on RESTful service appear double - c#

I'm having a RESTful service done with C# ASP.NET. On my models, I'm using the DataAnnotations' RequiredAttribute (from System.ComponentModel.DataAnnotation). When I'm sending an input model with missing properties (via Swagger or Postman), I get double $"{property} is required" messages, although I only have one [Required] attribute on each property.
I thought maybe it comes from inheritance in my models (I sometimes also have three-level hierarchies and no triple messages, so I think this is not the problem) or from registering the validations double somehow (but where are they registered?). For example, this is one parent:
public class CertificatePayload : AchievementBase
{
public string ExternalLink { get; set; }
}
and this one child class:
public class AchievementBase
{
[Required]
public string GrantedTo { get; set; }
[Required]
public string GrantedBy { get; set; }
}
Any ideas about what I could check or what could be the problem?

Since there came no answer...
A colleague of mine solved the problem with a trick: deleting the required attributes and adding the IValidatable interface to the object with a validate method showed that the validate method was called twice. Stacktrace was the same both times. The right search words found the solution in the end:
StackOverflow-ASP.NET WEB API 2 - ModelBinding Firing twice per request
I had to rebind the ModelValidatorProvider. I applied it to where I had bound my Entities.

Related

How does custom model binding work for a property of an unknown type at compile time?

I have a UI designer on the front end which creates a layout.
A layout has rows, and each row has columns, and each column has widgets. The widgets are identified by a key and they also have a config.
public class Layout
{
[Required]
public IEnumerable<Row>? Rows { get; init; }
}
public record Row
{
[Required]
public IEnumerable<Column>? Columns { get; init; }
}
public record Column
{
[Required]
public IEnumerable<Widget>? Widgets { get; init; }
}
public record Widget
{
[Required]
public string? WidgetTypeKey { get; init; }
public object? Config { get; init; }
}
The config of a widget could be any number of C# classes that don't share anything in common. I don't know which one it will be until I determine the widget type by key. So therefore I have used object as the type.
But the config classes still have validation requirements such as [Required], [Range], [MaxLength] and so on.
I can resolve the config class at run time, but I'm not sure how to go about this so that I still get all of the usual ASP.NET validation through the pipeline.
At first I thought I could attach [BindModel] to the Config property or the Widget class and use my own IModelBinder, but these aren't used at all. ASP.NET only considers them if they're at the top of the hierarchy. I.e. the layout. So the model binder is never hit.
I also tried writing an IModelBinderProvider, but again the same problem. The provider is only hit for the initial Layout type, but nothing beyond that. It never queries again for any other type.
I also experimented with generics, thinking that maybe Config could be a TConfig type, but I have no idea how to resolve that at runtime during model binding. Especially since each widget can be a different type.
I guess I could write my own model binder for a layout, but then I miss out on all the automated validation don't I?
Is this too crazy to attempt? Has anyone ever successfully resolved a dynamic object at runtime with a deeply-nested complex type while also letting ASP.NET core do its full validation pipeline?
Your Layout class should implement IValidatableObject interface with Validator class:
public class Layout : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
...
Validator.TryValidate(...);
...
}
}
You should have the validation logic with System.ComponentModel namespace, ASP.NET Core will pick it up automatically.

Adding existing method to WCF Service but no data returned

I've added an existing method to a web service (that I did not write).
I can bring the method into Soap UI by performing and update on the service.
I can run the method in SOAP UI and debug it and clearly see my method is pulling the data I want, processing it correctly but I am getting nothing back in Soap UI and my debugger terminates at that point.
Its worth nothing I build this method from an existing working method that returns data just fine. I imagine I am just missing some minor configuration some ware.
Things I have done,
1) Added the new method to the operation contract
2) Added all the necessary logic for processing the data.
I can furnish any code/configs but I just don't know what exactly is needed to troubleshoot this as I am attempting to add this off existing code. I'm generally not a vague with the details but I'm in the process of educating myself at the same time as well as trudging through some existing documentation I have found online.
The service is intended to return a class. We define a DTO and paste the results from a SQL query into our DTO. We then do some integrity checking on the DTO and test for failure or success. We then add the DTO to a container object named CustomResult based on success or failure and return the CustomResult. The CustomResult class should not need any modifying (famous last words). I did however create a new DTO class which I can add to my original post
DTO Class
namespace Custom.Company.Services
{
[DataContract]
public class TimeUnitDto
{
[DataMember]
public string Calendar { get; set; }
[DataMember]
public long AverageHour { get; set; }
[DataMember]
public long AverageDay { get; set; }
[DataMember]
public long AverageWeek { get; set; }
[DataMember]
public long AverageMonth { get; set; }
[DataMember]
public long AverageYear { get; set; }
[DataMember]
public long LookupRefreshInd { get; set; }
}
}
Thanks,
All, I figured it out. Unfortunately the answer is propriety to what I am doing but basically we the DTO to our CustomResult Class.
I copy/pasted the code from another service we use since the functionality was similar. I forgot to replace of the 'Status' assignments to the new status I created for this dto.
It was trying to place it in a Status object that the data is not going to recognize. Thanks to everyone who looked into this for me.

WebApi Controller model serialization issue

I'm facing little stranger issue with Web API controller. I have a collection which is being passed in an action of api controller. Object being used is collection is having 4 properties.
My action is able to accept collection parameter when it's properties are in specific order. See below :-
[HttpPost]
public ForexRates UpdateRates([FromBody] Rates rates)
{
// TODO: Obviously code :)
return rates;
}
This code is being place in API controller & calling from Postman. See below:-
<rates>
<rate>
<id>fefef</id>
<rate>35353.333</rate>
<series>dfefge</series>
<series-order>sfefefef</series-order>
</rate></rates>
If I change the order of the properties I started getting null value in my action. Can some one please explain this :)
Models
public class Rate
{
public string Id { get; set; }
public string Date { get; set; }
public double Rate { get; set; }
}
public class Rates : Collection<ForexRate>
{
}
You will need to control the order with which your XML is serialized. Use XmlElementAttribute and specify the Order.
There is a similar question here
FYI, I suppose there is no way for you to change the order of the properties, while you supply from PostMan to your WebApi service. You will need to follow the exact order.
If you don't wanna do that, then pass this Xml as a string parameter and then parse it inside a method.
The default binder can have issues when the same name is used in different places during binding.
In your case you've got Rate.Rate - both class name and property name. Try changing your class to (and corresponding xml for the post) :
public class Rate
{
public string Id { get; set; }
public string Date { get; set; }
public double Value { get; set; }
}
and then try changing the order.
While I don't have a definitive reason why it works in one order and not another, it's likely that when it gets to the Rate(double) value it tries to create a new Rate(object) but doesn't have the correct properties (as its just a double).
A more complicated solution would be to write a specific model binder for the Rate object.
The issue has to do with the DataContractSerializer which expects the elements to occur in a specific order (alphabetical with some consideration given to inheritance). That's the default serializer used when creating a Web API project.
You can override this and specify a different serializer during API Configuration like this:
GlobalConfiguration.Configuration.Formatters.XmlFormatter
.SetSerializer<SomeType>(new XmlSerializer(typeof(SomeType)));

How do I prevent Entity Framework forcing validation on child object properties?

I have an ASP.NET MVC 3 project that uses .Net 4.0 Framework and Entity Framework 4.4. I am using the code first w/ migrations approach for the Entity Framework.
I have an object A that has a property of object B. object A really just needs to know the id for object B, but MVC is enforcing all annotation validations on object B as I've marked it required in object A (which it is). ModelState.IsValid is always returning false because some validations are failing on object B when the form is submitted.
Example:
public class FormField
{
public int Id { get; set; }
[MaxLength(50)]
[Required]
[DisplayName("Field Name")]
public string Name { get; set; }
[Required]
public Form Form { get; set; }
}
public class Form
{
public Form()
{
Fields = new List<FormField>();
}
public int Id { get; set; }
[Required]
public string Name { get; set; }
public List<FormField> Fields { get; set; }
}
This isn't a problem when editing an existing FormField as I can just put a hidden field with the Form.Name property on the page (this still strikes me as something that should be unnecessary). The issue arises when creating a new FormField. I display a drop down list of forms (this is populated from my view model), and make that field point to the FormField.Form.Id property. ASP .NET MVC is still expecting formField.Form.Name (as this was marked as required on the Form object).
If I remove the "[Required]" annotation from the Form field of the FormField object, the validations wouldn't fire, but this would make the foreign key to Form.Id nullable in the database, which it shouldn't be.
Any thoughts? I'm probably doing something wrong here but I'm not entirely sure what.
I removed the [Required] annotation on FormField.Form. That didn't fix the issue. I tried adding "[Bind(Exclude = "vm.FormField.Form")]" to the parameter being posted. This stops the binding, but ensures I'm still left with the validation errors in ModelState.
Ultimately, I had to do:
ModelState.Remove("FormField.Form.Id");
ModelState.Remove("FormField.Form.Name");
And this prevented the ModelState errors I was getting. As I really only need the Form.Id, I had a property in my ViewModel "SelectedForm" and use this property for that value.
So...this works, but...this seems a pretty tedious solution as if I had more required fields on the FormField.Form object, I would have to list each one as well as hard code the property name in a string form.
Can anyone think of a way to refine this approach?
Maybe you could remove the [Required] attribute and instead use the EF fluent API to configure this:
modelBuilder.Entity<FormField>().Property(ff => ff.Form).IsRequired();

Conditional JSON Deserialization using C#

I'm writing a C# test automation to validate web services that return JSON strings. I created a DataContract that mapped to what was being returned. Assume this is what was being returned:
{"DataModule" : {"required":"false", "location":"toolbar"}}
My test automation was working fine, but then I started getting this error:
"The data contract type 'DataModule' cannot be deserialized because
the required data members 'required, location' were not found."
I checked the JSON and the data module was now
{"DataModule" : {"width":"400", "height":"320"}}
I discovered that the dev implementation is that if the first type of data module is encountered, the client parses that and creates a button on a toolbar. If the second type of data module is returned, the button appears on the toolbar AND a panel appears in another location with those measurements.
Is there a way to either create optional members in a data contract OR implement conditional deserialization to account for JSON objects that may have multiple implementations?
If you declare the model with all of the likely properties, only the ones found in the JSON string will be populated:
public class DataModule
{
public bool Required { get; set; }
public string Location { get; set; }
public string Width { get; set; }
public string Height { get; set; }
}
#dave-s
I had already tried adding all of the properties, but since I knew I was on the right track, your suggestion keyed me into something else. The properties were all decorated with
[System.Runtime.Serialization.DataMemberAttribute(IsRequired = false)]
But the class itself, was decorated with only [Serializable]. When I changed [Serializable] to
[System.Runtime.Serialization.DataContractAttribute()]
it started working.

Categories

Resources