Add Swagger documentation for DTO properties - c#

I'm using methods in my WebAPI that take in parameters.
I can provide descriptions in my Swagger UI for the basic parameters (id and terminology) using the following 'param' XML Comments:
/// <param name="terminology">terminology comment goes here</param>
/// <param name="id">id comment goes here</param>
/// <param name="filterDto"></param>
/// <returns>The results</returns>
[HttpGet("{terminology}/product/{id}/details")]
[ProducesResponseType(typeof(IPacksResponse), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ErrorsDto), StatusCodes.Status500InternalServerError)]
public IActionResult GetProductPacks(
[FromRoute] string terminology,
[FromRoute] string id,
[FromQuery] GetPackByIdFilteredDto filterDto)
However, what I need help with is... The 3rd parameter being passed in (filterDto) is a class with several properties which also appear in the Swagger UI, but I don't know how to create descriptions for each of those inputs:
public class GetPackByIdFilteredDto
{
public string Dt { get; set; }
public string Dc { get; set; }
public string Ms { get; set; }

Related

Asp.net return double data sometimes is not accurate

In my asp.net, I have double? field in my model :
public class MyModel
{
/// <summary>
/// VoltageCVal
/// </summary>
[DataMember(Name = "voltageCVal")]
public double? VoltageCVal { get; set; }
}
In web api ,I get the data from database and it get the result of 3.2999999523162842 before return, when it returns, I get the api result of 3.3
Why it happens?Could you help me?

How to show responses example in swagger documenation

I developed asp.net web API and I used swagger to API documentation and consume purposes. I need to show swagger response model sample in swagger documentation as follows
This image I got from the internet
How can I add a response example as above image
My controller as follows
/// <param name="sDate">Start date</param>
/// <param name="eDate">End date</param>
/// <param name="lCode">Location code</param>
/// <param name="page">Page number</param>
/// <param name="pageSize">Page size</param>
[Route("lobbydetail")]
[SwaggerResponse(HttpStatusCode.OK, Type = typeof(ResultOutput<List<LDetailRecord>>))]
[SwaggerResponse(HttpStatusCode.BadRequest, Type = typeof(APIError))]
[SwaggerResponse(HttpStatusCode.InternalServerError, Type = typeof(APIError))]
public IHttpActionResult GetDetails(DateTime sDate, DateTime eDate, string lCode = null, int page = 1, int pageSize = 100)
{
try
{
if (sDate > eDate)
{
return Content(HttpStatusCode.BadRequest, new APIError("400", "Start date is greater than end date."));
}
var tID = Convert.ToInt32(jwtData.GetTokenClaim(TENANT_ID));
return Ok(dataView.GetDetailViewData(tID, sDate, eDate, lCode, page, pageSize));
}
catch (ArgumentException ae)
{
return Content(HttpStatusCode.BadRequest, new APIError("404", "Invalid location code"));
}
catch (Exception ex)
{
Logger.LogErrorEvent(ex);
return Content(HttpStatusCode.InternalServerError, new APIError("500", "Error occurred"));
}
}
My as follows LDetailRecord
public class LDetailRecord
{
public DateTime TDateTime { get; set; }
public dynamic Account { get; set; }
public string LCode { get; set; }
public string LName { get; set; }
public string ConfNumber { get; set; }
public decimal WTime { get; set; }
public decimal AssTime { get; set; }
public List<string> RequestedServices { get; set; }
public string PersonRequested { get; set; }
public string AssistedBy { get; set; }
public string CustomerType { get; set; }
public string CheckedInBy { get; set; }
public string Comments { get; set; }
public string PreferredLanguage { get; set; }
}
In my swagger shows as follows
I'm new to the web api and swagger, please help me, what I did wrong here
The answer by #Mikah-Barnett is not entirely correct when it comes to error responses.
Also, because you're returning a different type when there's an error, use the
[ProducesErrorResponseType(typeof(APIError))]
as well. That will let Swagger know you want a different model when there's a client error.
ProducesErrorResponseTypeAttribute(Type) - Is used for API documentation, but can only define a single error type for all errors which are specified with ProducesResponseTypeAttribute(Int32) attribute.
ProducesResponseTypeAttribute(Type, Int32) - Is used for API documentation when you want to have more detailed granularity over all the different types returned, depending on the response status code
As an example, below is what you could define per endpoint. Even better, common response type attributes can be specified at the controller level, meaning you don't need to duplicate for every endpoint.
[HttpPost]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)]
[ProducesResponseType(typeof(NewOrderResponse), StatusCodes.Status201Created)]
public async Task<IActionResult> Post([FromBody, Required] NewOrderRequest orderRequest)
You need to explicitly state the return type in your methods. So, instead of
public IHttpActionResult GetDetails(...
use
public IHttpActionResult<LDetailRecord> GetDetails(...
That lets OpenAPI know exactly what you're planning to return and it will then show an example of the model in the UI.
Also, because you're returning a different type when there's an error, use the
[ProducesErrorResponseType(typeof(APIError))]
as well. That will let Swagger know you want a different model when there's a client error.
Here's a good article from MSFT documenting how this works, and below is a more complete example (from that article) showing all the pieces together.
/// <summary>
/// Creates a TodoItem.
/// </summary>
/// <remarks>
/// Sample request:
///
/// POST /Todo
/// {
/// "id": 1,
/// "name": "Item1",
/// "isComplete": true
/// }
///
/// </remarks>
/// <param name="item"></param>
/// <returns>A newly created TodoItem</returns>
/// <response code="201">Returns the newly created item</response>
/// <response code="400">If the item is null</response>
[HttpPost]
[ProducesResponseType(201)]
[ProducesResponseType(400)]
[ProducesErrorResponseType(typeof(APIError))]
public ActionResult<TodoItem> Create(TodoItem item)
{
_context.TodoItems.Add(item);
_context.SaveChanges();
return CreatedAtRoute("GetTodo", new { id = item.Id }, item);
}

MVC c# Comments Listing / Display

I am trying to find the most efficient way to display a set of nested comments from a database schema and into a c# object that i can convert to a serialised JSON object.
My Schema is as follows:
As you can see from the screenshot there is an Update which has a parent issue id (not relevant here) and a Comment. There is also a ParentUpdateID which enables the table to store nested replies for each reply and so on.
I have created a class within c# which i want to convert the results from my Entity Framework call;
/// <summary>
/// The resource view model.
/// </summary>
public class Comment
{
/// <summary>
/// Gets or sets the id.
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
public string Body { get; set; }
/// <summary>
/// Gets or sets the modified by.
/// </summary>
public string ModifiedBy { get; set; }
/// <summary>
/// Gets or sets the modified date.
/// </summary>
public DateTime ModifiedDate { get; set; }
/// <summary>
/// Gets or sets the created by.
/// </summary>
public string CreatedBy { get; set; }
/// <summary>
/// Gets or sets the created date.
/// </summary>
public DateTime CreatedDate { get; set; }
/// <summary>
/// Gets or sets the parent comment id.
/// </summary>
public Guid? ParentCommentId { get; set; }
/// <summary>
/// Gets or sets the children.
/// </summary>
public List<Comment> Children { get; set; }
}
And here is how i am retrieving the List of Comments / Updates from the database.
List<ClientIssuesUpdate> updates = this.db.ClientIssuesUpdates.Where(i => i.IssueId == issueId).ToList();
Is there a clever way through either a loop or using lync that can order all of the comments showing most recent first, but then display in date order each comments children and so on. (e.g. All comments without a ParentUpdateId would be to level comments)
Any guidance would be helpful so i can learn for the future.
Many thanks
This should order them by date created.
Depending on how deep your comments children gets nested, you would have to add more loops.
List<ClientIssuesUpdate> updates = this.db.ClientIssuesUpdates.Where(i => i.IssueId == issueId).ToList().OrderByDescending(p => p.CreatedDate);
foreach (var comment in updates.Where(comment => comment.Children != null))
{
// order the children
comment.Children = new List<Comment>(comment.Children.OrderByDescending(c => c.CreatedDate));
}
*cant remember if you should use OrderBy<> or OrderByDescending<>

Generic types, Interfaces

I would like to create a generic notification engine. The idea is to have a single core engine to process any type of notification. This engine will process notification and handle all logging, error handling etc..
I created 3 simple interfaces:
public interface INotificationInput
{
/// <summary>
/// Friendly Name for logging/tracing usage
/// </summary>
string FriendlyName { get; set; }
string NotificationCode{ get; set; }
Double Version { get; set; }
}
public interface INotificationOutput
{
/// <summary>
/// Friendly Name for logging/tracing usage
/// </summary>
string FriendlyName { get; }
}
public interface INotificationProvider<out Toutput, Tinput> where Toutput : INotificationOutput where Tinput : INotificationInput
{
/// <summary>
/// Friendly Name for logging/tracing usage
/// </summary>
string FriendlyName { get; set; }
/// <summary>
/// Generates and returns an INotificationOutput from data
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
Toutput GenerateNotificationOutput(Tinput data);
}
So the INotificationProvider will chunk the INotificationInput to create a INotificationOutput.
That could be information to send a email, a sms, you name it, the engine will call the methods and do the magic of scheduling, logging, handling errors and so on..
I implemented the interface like this:
/// <summary>
/// INotificationInput represented by a dummy object
/// </summary>
public class DummyNotificationInput : INotificationInput
{
public string FriendlyName { get; set; }
public string NotificationCode { get; set; }
public double Version { get; set; }
}
public class DummyNotificationOutput : INotificationOutput
{
public string FriendlyName { get; private set; }
}
public class DummyProvider : INotificationProvider<DummyNotificationOutput, DummyNotificationInput>
{
public string FriendlyName { get; set; }
public DummyNotificationOutput GenerateNotificationOutput(DummyNotificationInput data)
{
throw new NotImplementedException();
}
}
Now I would like my engine to have a list of provider:
var providersList = new List<INotificationProvider<INotificationOutput, INotificationInput>>();
The problem is that I cannot to the following:
providersList.Add(new DummyProvider<DummyNotificationOutput, DummyNotificationInput>());
There must be a solution. Am I using the wrong approach?
The second generic type argument to INotificationProvider isn't covariant (at a conceptual level), but you're trying to use it as if it were. It is actually contravariant.
In your list of INotificationProvider objects you've defined the input notification as an INotificationInput. This means objects added to this list need to be able to accept any type of INotificationInput as input to their GenerateNotificationOutput function. You're trying to add an object that only knows how to handle DummyNotificationInput objects. It would fail if it were passed some other type of input.
Either your provider needs to accept INotificationInput objects, if you want to be able to add it to that list, or the list needs to define all of the objects as accepting DummyNotificationInput.
As Servy has already answered, you can't really do this due to what you providersList is expecting
With this in mind, it may actually be simpler to just make INotificationProvider non-generic:
public interface INotificationProvider
{
/// <summary>
/// Friendly Name for logging/tracing usage
/// </summary>
string FriendlyName { get; set; }
/// <summary>
/// Generates and returns an INotificationOutput from data
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
INotificationOutput GenerateNotificationOutput(INotificationInput data);
}
Then the DummyProvider becomes:
public class DummyProvider : INotificationProvider
{
public string FriendlyName { get; set; }
public INotificationOutput GenerateNotificationOutput(INotificationInput data)
{
throw new NotImplementedException();
}
}
Now, probably not what you had in mind - you are expecting to pass DummyNotificationInput instances to DummyProvider
You could just type check in your Provider code
public class DummyProvider : INotificationProvider
{
public string FriendlyName { get; set; }
public INotificationOutput GenerateNotificationOutput(INotificationInput data)
{
if (!(data is DummyNotificationInput)) throw new ArgumentException("Invalid type specified", "data");
return something...;
}
}
Obviously, you lose design time checking - but if you really need to put them in a covariant list you can't provide an implementor that has a derived generic type argument

how to retrieve class over web service?

I have a "User" class in both my Web App and my Webservice.
Everything works and I get data back but when I assign the User class in my Web App that of the Web Service then it cannot convert between the two
How I call it in my web app
User user = new User();
user = userService.Login(UserName.Text, Password.Text); // Here it says it can't convert
how I send it from my web service
[WebMethod]
public User Login (string userName, string Password)
{
UserData usrData = new UserData();
return usrData.UserExists(userName, Password);
}
And what my class looks like in both the Web App and the Web Service
[Serializable]
public class User
{
/// <summary>
/// User Id
/// </summary>
public int ID { get; set; }
/// <summary>
/// User Name
/// </summary>
public string Name { get; set; }
/// <summary>
/// User Surname
/// </summary>
public string Surname { get; set; }
/// <summary>
/// User Email
/// </summary>
public string Email { get; set; }
/// <summary>
/// User Password
/// </summary>
public string Password { get; set; }
}
So you're trying to call it from C# code? Simply do this...
User user = userService.Login(UserName.Text, Password.Text);
If you're calling it from the server side, you can remove the [WebMethod] attribute. You don't even need a Web Service for this, assuming userService is of type UserService, just place the UserService.cs class inside your App_Code folder. Or better yet, place UserService class in a class library, and add a reference in your web application to that library.

Categories