How to show responses example in swagger documenation - c#

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);
}

Related

Add Swagger documentation for DTO properties

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

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?

PartitionKey value must be supplied for this operation in cosmosdb delete operaiton

I am trying to delete a document from Cosmos DB
My code is like this:
public async Task<IHttpActionResult> DeletePartner(string id)
{
var telemetry = new TelemetryClient();
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var customers = await CosmosStoreHolder.Instance.CosmosStoreCustomer.Query().Where(x=> x.PartnerId == id).ToListAsync();
var userStore = CosmosStoreHolder.Instance.CosmosStoreUser;
var users = await userStore.Query().Where(x => x.PartnerId == id).ToListAsync(); ;
if (customers.Count> 0 || users.Count>0)
{
return BadRequest("You cant delete partners with existing customers or users");
}
else
{
var result = await CosmosStoreHolder.Instance.CosmosStorePartner.RemoveByIdAsync(id, "/CosmosEntityName");
return Ok(result);
}
}
catch (Exception ex)
{
string guid = Guid.NewGuid().ToString();
var dt = new Dictionary<string, string>
{
{ "Error Lulo: ", guid }
};
telemetry.TrackException(ex, dt);
return BadRequest("Error Lulo: " + guid);
}
}
}
[SharedCosmosCollection("shared")]
public class Partner : ISharedCosmosEntity
{
/// <summary>
/// Partner id
/// </summary>
[JsonProperty("Id")]
public string Id { get; set; }
/// <summary>
/// Partner name
/// </summary>
public string PartnerName { get; set; }
/// <summary>
/// Partner contact name
/// </summary>
public string PartnerContact { get; set; }
/// <summary>
/// Partner contact phone
/// </summary>
public string PartnerPhone { get; set; }
/// <summary>
/// Partner contact Office 365 domain
/// </summary>
public string PartnerDomain { get; set; }
/// <summary>
/// Partner type, silver, gold or platinum
/// </summary>
[ValidEnumValue]
public PartnerType PartnerType { get; set; }
/// <summary>
/// Partner start date
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// Partner end date
/// </summary>
public DateTime EndDate { get; set; }
/// <summary>
/// Parter enabled
/// </summary>
public bool Enabled { get; set; }
/// <summary>
/// CosmosEntityname
/// </summary>
[CosmosPartitionKey]
public string CosmosEntityName { get; set; }
}
/// <summary>
/// Partner type Enum
/// </summary>
public enum PartnerType
{
///Silver
Silver,
///Gold
Gold,
///Platinum
Platinum
}
But I got this error:
PartitionKey value must be supplied for this operation
I was trying to send as string "/CosmosEntityName" as second parameter, but it doesnt work
I am using Cosmonaut
You need to use the request options. For example, if your collection is partitioned by CosmosEntityName;
await this.documentClient.DeleteDocumentAsync(productDocument._self, new RequestOptions { PartitionKey = new Microsoft.Azure.Documents.PartitionKey(productDocument.CosmosEntityName) });
EDIT:
Here's what you need with Cosmonaut SDK
You need to provide the partition key value not the partition key
definition when you delete. Your delete request should look like this,
assuming the id is your partition key.
var deleted = await this._cosmonautClient.DeleteDocumentAsync(this._databaseName, collectionName, message.Id, new RequestOptions { PartitionKey = new PartitionKey(message.Id) });
You need to pass the value of the partition key of the element you want to delete as second parameter, not the path and attribute name.
var result = await CosmosStoreHolder.Instance.CosmosStorePartner.RemoveByIdAsync(id, "<partition key value for that id>");
Since the attribute you have defined as PK is CosmosEntityName, you need that attribute's value for that document.

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

Sending data to the server with fnServerParams and aoData for jquery DataTable does not work in MVC4

I want to send extra data to serverside (ASP.Net MVC4) for my jquery datatable. There are many examples on how to this client side, but I can't get it to work on the serverside.
Here's the code:
javascript:
$(document).ready(function () {
var oTable = $('#myDataTable').dataTable({
"bServerSide": true,
"sAjaxSource": "SearchPatient/DataHandler",
"fnServerParams": function (aoData) {
alert('in fnServerParams');
aoData.push( { "name": "more_data", "value": "my_value" } );
}
});
});
Note: the alert goes off, so the function itself is working.
My model class:
/// <summary>
/// Class that encapsulates most common parameters sent by DataTables plugin
/// </summary>
public class JQueryDataTableParamModel
{
/// <summary>
/// fnServerparams, this should be an array of objects?
/// </summary>
public object[] aoData { get; set; }
/// <summary>
/// Request sequence number sent by DataTable, same value must be returned in response
/// </summary>
public string sEcho { get; set; }
/// <summary>
/// Text used for filtering
/// </summary>
public string sSearch { get; set; }
/// <summary>
/// Number of records that should be shown in table
/// </summary>
public int iDisplayLength { get; set; }
/// <summary>
/// First record that should be shown(used for paging)
/// </summary>
public int iDisplayStart { get; set; }
/// <summary>
/// Number of columns in table
/// </summary>
public int iColumns { get; set; }
/// <summary>
/// Number of columns that are used in sorting
/// </summary>
public int iSortingCols { get; set; }
/// <summary>
/// Comma separated list of column names
/// </summary>
public string sColumns { get; set; }
/// <summary>
/// Text used for filtering
/// </summary>
public string oSearch { get; set; }
}
and finally my Controller:
public ActionResult DataHandler(JQueryDataTableParamModel param)
{
if (param.aoData != null)
{
// Get first element of aoData. NOT working, always null
string lSearchValue = param.aoData[0].ToString();
// Process search value
// ....
}
return Json(new
{
sEcho = param.sEcho,
iTotalRecords = 97,
iTotalDisplayRecords = 3,
aaData = new List<string[]>() {
new string[] {"1", "IE", "Redmond", "USA", "NL"},
new string[] {"2", "Google", "Mountain View", "USA", "NL"},
new string[] {"3", "Gowi", "Pancevo", "Serbia", "NL"}
}
},
JsonRequestBehavior.AllowGet);
}
Note: the action handler gets hit, so the ajax call to get data is also working and my datatable gets filled with 3 rows..
The problem is: aoData is always null. I expect the first element to hold "my_value".
Any help is much appreciated!
After searching for hours to find the answer finally posted it here. Only to come up with the answer in minutes:
This does the trick:
Add this line serverside in the DataHandler:
var wantedValue = Request["more_data"];
So the value is in the request and not in the model.
Thanks.
The value(s) are indeed in the model, but they are passed as individual fields, not as elements of aoData:
public class JQueryDataTableParamModel {
/// The "more_data" field specified in aoData
public string more_data { get; set; }
public string sEcho { get; set; }
public string sSearch { get; set; }
public int iDisplayLength { get; set; }
public int iDisplayStart { get; set; }
public int iColumns { get; set; }
public int iSortingCols { get; set; }
public string sColumns { get; set; }
public string oSearch { get; set; }
}
Usage:
public ActionResult DataHandler(JQueryDataTableParamModel param) {
/// Reference the field by name, not as a member of aoData
string lSearchValue = param.more_data;
return Json(
new {
sEcho = param.sEcho,
iTotalRecords = 97,
iTotalDisplayRecords = 3,
aaData = new List<string[]>() {
new string[] {"1", "IE", "Redmond", "USA", "NL"},
new string[] {"2", "Google", "Mountain View", "USA", "NL"},
new string[] {"3", "Gowi", "Pancevo", "Serbia", "NL"}
}
},
JsonRequestBehavior.AllowGet
);
}
I will post another answer just to show a way to avoid code duplication.
You can also make your JQueryDataTableParamModel as a base class for others. Datatables will send the custom data in the same object, so you can't make the model bind it directly, unless your C# View Model matches exactly the DataTables sent object.
This can be achieved as #SetFreeByTruth answered, but you could have some code duplication if you want it to be used in a whole project. This table has more_data, what if you have another table with only a property called custom_data? You would need to fill your object with multiple fields or create several datatables view models, each with your custom data.
For your scenario, you could use inheritance. Create a new class like this:
//Inheritance from the model will avoid duplicate code
public class SpecificNewParamModel: JQueryDataTableParamModel
{
public string more_data { get; set; }
}
Using it like this in a controller:
public JsonResult ReportJson(SpecificNewParamModel Model)
{
//code omitted code for clarity
return Json(Return);
}
If you send the DataTables request and inspect the Model, you can see that it's filled with your custom data (more_data) and the usual DataTables data.

Categories