How to hide some of the [FormData] properties from Swagger UI? - c#

I have some properties in the object that corresponds to the request body, but I would like to hide them from Swagger UI documentation, because their purpose is just to work in the background along the request flow. I've tried some solutions to POST and GET requests, but when the request is sent by FormData and I have properties that are navigation properties (an object), the settings don't work and the information is shown in Swagger UI.
Let me give an example.
I have a class which is used to store (in memory) the logged in user data:
public class LoggedUser
{
public UserInfo(long id, string name)
{
Id = id;
Name = name;
}
public long Id { get; init; }
public string Name { get; init; }
}
And I have a class which is used as a base for the request body classes:
public abstract class RequestCommand
{
public LoggedUser User{ get; init; }
...
}
But these properties are shown in Swagger UI:
I've tried to use this solution: Ignore [FromForm] data from rendering on swagger
but when I have navigation properties like on the image above (User.Id, User.Name, ...) it does not work.
I would like to find a solution that can hide these properties from Swagger UI.

Related

How to ignore a property on Swagger body input but still show on responses?

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.

Model Binding ignoring properties that have the JsonIgnore attribute

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!

Hide model classmember in controller in C# Web API?

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(}

How to POST in ASP.NET Web API with model property as interface

Suppose I have a model:
public class Menu
{
public string Name { get; set; }
public IMenuCommand Next { get; set; }
}
IMenuCommand could have different implementations, like:
public class NextStepCommand : IMenuCommand
{
public int Step { get; set; }
}
public class VoiceCommand : IMenuCommand
{
public string Message { get; set; }
}
And I want to POST menus with different commands to the ASP.NET Web API service. How can I do that?
The request below will create an object with specified Name, but Next command will be null:
POST http://localhost/api/menus: {"name":"bob","next":{"step":1}}
Returns 201: {"Name":"bob","Next":null}
Default Web API binders can't map my request params to the needed C# type - of course it's a tricky part. Can I use some "known-type" attribute for interface-based properties or is there any other approach to handle this case, probably a custom model binder?
I think what you're looking for is Json.NET's support for type name handling. It allows you to specify the type to deserialize into by adding the "$type" json tag. You can try this code out to see how it works:
Console.WriteLine(JsonConvert.DeserializeObject<Menu>(
#"{
""name"":""bob"",
""next"":
{
""$type"" : ""ConsoleApplication.NextStepCommand,ConsoleApplication"",
""step"" : 1
}
}",
new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Auto }).Next);
You'll have to replace the namespace and assembly name with your own, but you should see the NextStepCommand being correctly deserialized.
In WebAPI, you'll need to tweak your request to add the "$type" type information, and you'll need to enable TypeNameHandling like this:
config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;

Suggest REST/Service design for collection in 'DTO' response/request

Just learning REST and ServiceStack and asking for suggestion how to build this example schema:
I have a User object with those fields:
public class User
{
public string ID {get;set;
public string Name {get;set;}
public List<int> OrderID {get;set;} // inner collection
// all other fields
}
I have a Service class:
public class UserService : Service
{
public List<User> Get(User UserRequest)
{
List<User> lstUsers = // Somehow polulate a collection of Users
return lstUsers;
}
// Other functions
}
and a AppHost file (self hosted for now)
public UserHost : AppHostHttpListenerBase
{
public override void Configure(Funq.Container container)
{
Routes
.Add<User>("/UserInfo")
.Add<User>("/User/{ID}");
}
}
Everything working fine: the client (browser) send an empty 'User' request and receive collection of User objects, but what I need to add is to get specific User request. For example I want to receive (and change) all Orders with specific User ID.
For example User with ID = 2 has a collection of 10 OrderID. So I though I can build an URI like this: /MyHost/2/Orders and it will return collection of Order objects.
I already tried to add another DTO object Order and bind it to routes like this:
Route.Add<Order>("/User/{ID}/Oders")
but it does not work. The screen (at least in browser) always stay blank and it seems no response is coming from SS.
Is it a RESFull way of doing things ? How It could be implemented in ServiceStack ? and could you provide a little code example (even schematic) how to do that ?
If you want to return a list of orders for a given user, then binding to the URL "/User/{ID}/Orders" is the RESTful way, yes.
With ServiceStack I normally prefer to define request and response classes, where request class will have all the possible input parameters and response defines the resulting object. Though often these to can be combined like User in the example.
As {ID} in the URL refers to the user ID, it's better to name it {UserID} and also have this property on your request class.
Probably you can just have UserID on the Order class:
public class Order
{
public string ID { get; set; } // order ID
public string UserID { get; set; } // user ID - also request param
// ...
}
Routes.Add<Order>("/User/{UserID}/Oders");
Now, UserID from the URL is mapped to your UserID property and you can use it to query the db, etc.
Each request class normally corresponds to another service class with your app logic. Thus,
for User you have UserService : RestServiceBase<User>,
for Order there is OrderService : RestServiceBase<Order>.
When ServiceStack sees this URL mapping Routes.Add<Order>("/User/{UserID}/Oders") it knows to invoke OrderService for a given URL.
public class OrderService : RestServiceBase<Order>
{
public override object OnGet(Order request)
{
// ...
}
}

Categories

Resources