I am using MVC5 WebAPI and i have a class as follows:
public string Name { get; set; }
public string Sub { get; set; }
public List<Actions> Actions { get; set; }
(Actions is another custom class)
When client makes an API call they have to send the Actions property as null. Is there a way that the client may not send the actions property at all?
How could this be done?
Thanks in advance.
I think this public List Actions { get; set; } is optional by default.
if user/client don't specify it will be null.
Have you added any required field validations to it?
If the client doesn't post actions. Then Actions at the time of model binding would be null. So there isn't any extra work from your side. In other words, if you post only Name and Sub, then Actions would be null.
Related
I am using Swashbuckle and Swagger UI to automatically generate API documentation.
My person endpoint uses the following PersonViewmodel:
public int? ID { get; set; }
public string Name { get; set; }
My endpoint method takes in a PersonViewmodel as the body and returns a PersonViewmodel using an IActionResult. However, I don't want the user defining an ID as that is generated by the business logic. In the code if an ID is set it is ignored.
How can I change the Swagger UI to not show the ID in the Example value for the body input but still show the ID for the Example value for the responses?
I have found many ways to remove properties completely from Swagger UI such as [JsonIgnore] or setting the property to internal or private. But how can I remove a property from the input example in swagger but keep it in the output/responses example?
There is a better way now with now. There are two things you need to do
Annotate with SwaggerSchema found in Swashbuckle.AspNetCore.Annotations;
[SwaggerSchema(ReadOnly = true)]
public int Id { get; set; }
EnableAnnotations in AddSwaggerGen
builder.Services.AddSwaggerGen(options =>
{
options.EnableAnnotations();
});
See more about it in the Documentation
Please use the attribute [BindNever] above the property like
[BindNever]
public int? Id {get;set;}
Also, please Check if you're using Newtonsoft.Json to serialize, that could be the reason your System.Text's JsonIgnore attribute didn't work.
I'm building a web api microservice using Core 3. I have a class defined as follows:
public class UserSourceList
{
[JsonIgnore]
public string UserId { get; set; }
[JsonIgnore]
public string ListId { get; set; }
public string Name { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public ListTypes ListType { get; set; }
public List<string> Ids { get; set; }
public DateTimeOffset CreationTime { get; set; }
}
When the framework attempts to bind the data provided by a HTTP PUT, it will not populate the UserId and ListId fields. As a result, model binding is failing during validation and returning a HTTP 400, stating that UserId and ListId are required.
The controller's action method is defined as follows:
[HttpPut("{userId:userid}/{listId:listid}", Name = "ReplaceUserList")]
public ActionResult Replace(string userId, string listId, UserSourceList model)
{
return Ok(_listManager.ReplaceUserList(model.UserId, model.ListId, model));
}
A typical call to the API would look similar to this:
PUT /api/v1/listmgmt/abc123def456/c788f2f7b7984424910726d4a290be26
PUT Body
{
"name": "Test",
"listType": "Eans",
"ids": ["97814571867716", "9781430257615", "9780982550670"],
"userId":"abc123def456",
"listId":"c788f2f7b7984424910726d4a290be26"
}
If I removed the JsonIgnore Attribute from the UserId and ListId properties of the model, everything binds as expected.
Is it expected behavior that model binding will ignore fields flagged with JsonIgnore?
I know I can work around it by changing how my validation code works or I can split my model. I would like to understand the current behavior as it is different from what I expected and experienced with ASP.NET MVC 4 and WebApi 2.
Thanks
Short answer, Newtonsoft Json.Net is being used to deserialize the post/put body when the content type is application/json. Therefore, the userId and listId parameters are being ignored during deserialization, but evaluated during model validation.
I removed the JsonIgnore Attribute as well as all the Data Annotations, and changed to the FluentValidation package which provided the ability at runtime to configure how the body should be validated based up the type of call made.
I think the reason is because of this:
[HttpPut("{userId:userid}/{listId:listid}", Name = "ReplaceUserList")]
userId and listId are required and cannot be ignored because they are defined in the annotation HttpPut. I think you need to remove them from HttpPut's parameters and find another way to get around this.
Hope this helps!
I am trying to learn and understand C# Web API and MVC.
I understand the simple tutorials where one has a simple Product or Person class as a Model and then makes a CRUD Controller to make use of the model.
But I need it to be a bit more complex and can't figure it out.
I have following Model:
public class PersonModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public DateTime LastUpdated { get; set; }
}
Same as the table in my database. The LastUpdated column has a default constraint: (GETUTCDATE())
I am not interrested in exposing LastUpdated in my POST methods in PersonsController:
public void PostPerson(PersonModel person)
{
// Upload person to database
}
Because then one could insert an invalid datetime in LastUpdated - or I have to manuel set LastUpdated in my business logic, but why not just let my SQL server do it?
Anyway to hide LastUpdated in PostPerson?
As a sidenote I would like to be able to show LastUpdated in my GetPerson method.
How is that possible?
When you implement a property in a class, you can specify different access modifiers for the get vs. set accessors.
This is true whether you are implementing the property yourself, or using an automatic property.
Different combinations of access modifiers include:
get/set both public – client can read/write property value
get/set both private – client has no access to the property
get public, set private – property is read-only
get private, set public – property is write-only
// get/set both public
public string Name { get; set; }
// get/set both private
private string SecretName { get; set; }
// public get => read-only
public string CalcName { get; private set; }
// public set => write-only
public string WriteOnlyName { private get; set; }
You could create a custom DTO as a view model for the POST operation on this controller. This would be additionally handy because you probably also don't want the client to supply the Id value either (I assume). Something like this:
public class PersonDTO
{
public string Name { get; set; }
public string Title { get; set; }
}
This would be the input for the controller action:
public void PostPerson(PersonDTO person)
{
// Upload person to database
}
Then in the code you'd create a new PersonModel to add to the data context. Something like:
using (var db = new MyDataContext())
{
var newPerson = new PersonModel
{
Name = person.Name,
Title = person.Title
};
db.Persons.Add(newPerson);
db.SaveChanges();
}
(Or perhaps create a kind of translation method on the DTO which returns an instance of the model, acting as a sort of factory method and putting the logic in the object rather than in the controller.) This way the client isn't providing an entire PersonModel instance, just an object which describes the creation of that instance. The GET operation can still return the full PersonModel.
When building an API (using WebAPI, for example) it can often be really useful to fine-tune the inputs and outputs like this. And such custom DTOs/ViewModels really come in handy, albeit at the cost of slightly more code by creating essentially a translation layer to the backing models.
One tool I've found particularly handy in determining where in the API I need to tweak things is when using Swagger to generate my API docs. Looking through the generated docs, I may notice something which I don't want to be exposed. This is an indicator that I need to customize that API endpoint a little more so that the resulting docs are a little cleaner.
Try adding the exclude attribute above the property
[Exclude]
public DateTime LastUpdated {get; set(}
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)));
I am attempting to model bind a complex object with a non-sequential list using an ApiController. All of the fields except the list are set correctly, but the list contains one element (even though two list elements were posted) and the element is null. If I take the exact same code and point it to an MVC Controller using the same parameter type in my action method, everything works as expected.
Since I am using a non-sequential list, I am using the hidden ".Index" input as described by Phil Haack (http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx)
The ApiController also binds the list correctly if I remove the ".Index" input and send the list as a sequential list starting at 0. (This option work for testing, but is not a great option in production as the list items can be added and removed by the user, which is why I want to use the non-sequential list.)
I understand that Web API Controllers do parameter binding differently than MVC Controllers as discussed here, but it seems like non-sequential lists should bind correctly in Web API Controllers. Am I missing something? Why does the same code work for an MVC Controller and not a Web API Controller? How can I get non-sequential lists to bind correctly in Web API?
Here are my Post parameters:
Parameters application/x-www-form-urlencoded
BatchProductLots.Index 1
BatchProductLots.Index 2
BatchProductLots[1].BrandId 1
BatchProductLots[1].ContainerId 9
BatchProductLots[1].ContainerLot 123
BatchProductLots[1].PackageId 2
BatchProductLots[1].PlannedQuantity 0
BatchProductLots[1].ProducedQuantity 20
BatchProductLots[2].BrandId 1
BatchProductLots[2].ContainerId 9
BatchProductLots[2].ContainerLot 123
BatchProductLots[2].PackageId 1
BatchProductLots[2].PlannedQuantity 0
BatchProductLots[2].ProducedQuantity 1
BatchStatusId 1
LotNumber 070313
ProductionDate 07/03/2013
RecipeId 1
RecipeQuantity 1
SauceId 22
X-Requested-With XMLHttpRequest
Here is my Web API Controller Action:
(request.BatchProductLots list is set to one element (even though two elements were posted) and that one element is null)
public Response Create(BatchCreateRequest request)
{
Response response = new Response();
try
{
Batch batch = Mapper.Map<Batch>(request);
batchService.Save(batch);
response.Success = true;
}
catch (Exception ex)
{
response.Message = ex.Message;
response.Success = false;
}
return response;
}
Here is the complex object with the list that I am attempting to bind to:
public class BatchCreateRequest
{
public int BatchStatusId { get; set; }
public DateTime ProductionDate { get; set; }
public string LotNumber { get; set; }
public int SauceId { get; set; }
public int RecipeId { get; set; }
public int RecipeQuantity { get; set; }
public List<BatchProductLot> BatchProductLots { get; set; }
public class BatchProductLot
{
public int BrandId { get; set; }
public int ContainerId { get; set; }
public string ContainerLot { get; set; }
public int PackageId { get; set; }
public int PlannedQuantity { get; set; }
public int ProducedQuantity { get; set; }
}
}
Short answer, it's not possible using Web Api's Model Binder. MVC and Web Api use different model binders and the Web Api model binder only works on simple types.
See this answer for links that explain further as well as possible solutions.
Longer answer, create a custom implementation of System.Web.Http.ModelBinding.IModelBinder and change your Action's signature to the following
public Response Create([ModelBinder(CustomModelBinder)]BatchCreateRequest request)
Do you really need Index to be set? In that case, one possible solution could be to make Index part of the BatchProductLot class. The sequence of list won't matter then and Web Api should be able to bind it.
Another idea would be to use application/json content type and send JSON. You can use Json.Net to deserialize and model binding would work.
Read Using an alternate JSON Serializer in ASP.NET Web API and even use this Nuget Package WebApi Json.NET MediaTypeFormatter if you don't want to do the hand wiring.